-rw-r--r-- | noncore/apps/opie-console/TEWidget.cpp | 1 |
1 files changed, 1 insertions, 0 deletions
diff --git a/noncore/apps/opie-console/TEWidget.cpp b/noncore/apps/opie-console/TEWidget.cpp index d1ff85e..1c83710 100644 --- a/noncore/apps/opie-console/TEWidget.cpp +++ b/noncore/apps/opie-console/TEWidget.cpp | |||
@@ -1,869 +1,870 @@ | |||
1 | /* ------------------------------------------------------------------------ */ | 1 | /* ------------------------------------------------------------------------ */ |
2 | /* */ | 2 | /* */ |
3 | /* [TEWidget.C] Terminal Emulation Widget */ | 3 | /* [TEWidget.C] Terminal Emulation Widget */ |
4 | /* */ | 4 | /* */ |
5 | /* ------------------------------------------------------------------------ */ | 5 | /* ------------------------------------------------------------------------ */ |
6 | /* */ | 6 | /* */ |
7 | /* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ | 7 | /* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ |
8 | /* */ | 8 | /* */ |
9 | /* This file is part of Konsole - an X terminal for KDE */ | 9 | /* This file is part of Konsole - an X terminal for KDE */ |
10 | /* */ | 10 | /* */ |
11 | /* ------------------------------------------------------------------------ */ | 11 | /* ------------------------------------------------------------------------ */ |
12 | /* */ | 12 | /* */ |
13 | /* Ported Konsole to Qt/Embedded */ | 13 | /* Ported Konsole to Qt/Embedded */ |
14 | /* */ | 14 | /* */ |
15 | /* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ | 15 | /* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ |
16 | /* */ | 16 | /* */ |
17 | /* -------------------------------------------------------------------------- */ | 17 | /* -------------------------------------------------------------------------- */ |
18 | /*! \class TEWidget | 18 | /*! \class TEWidget |
19 | 19 | ||
20 | \brief Visible screen contents | 20 | \brief Visible screen contents |
21 | 21 | ||
22 | This class is responsible to map the `image' of a terminal emulation to the | 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 | 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 | 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 | 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. | 26 | size and key events and by being ordered to show a new image. |
27 | 27 | ||
28 | <ul> | 28 | <ul> |
29 | <li> The internal image has the size of the widget (evtl. rounded up) | 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. | 30 | <li> The external image used in setImage can have any size. |
31 | <li> (internally) the external image is simply copied to the internal | 31 | <li> (internally) the external image is simply copied to the internal |
32 | when a setImage happens. During a resizeEvent no painting is done | 32 | when a setImage happens. During a resizeEvent no painting is done |
33 | a paintEvent is expected to follow anyway. | 33 | a paintEvent is expected to follow anyway. |
34 | </ul> | 34 | </ul> |
35 | 35 | ||
36 | \sa TEScreen \sa Emulation | 36 | \sa TEScreen \sa Emulation |
37 | */ | 37 | */ |
38 | 38 | ||
39 | /* FIXME: | 39 | /* FIXME: |
40 | - 'image' may also be used uninitialized (it isn't in fact) in resizeEvent | 40 | - 'image' may also be used uninitialized (it isn't in fact) in resizeEvent |
41 | - 'font_a' not used in mouse events | 41 | - 'font_a' not used in mouse events |
42 | - add destructor | 42 | - add destructor |
43 | */ | 43 | */ |
44 | 44 | ||
45 | /* TODO | 45 | /* TODO |
46 | - evtl. be sensitive to `paletteChange' while using default colors. | 46 | - evtl. be sensitive to `paletteChange' while using default colors. |
47 | - set different 'rounding' styles? I.e. have a mode to show clipped chars? | 47 | - set different 'rounding' styles? I.e. have a mode to show clipped chars? |
48 | */ | 48 | */ |
49 | 49 | ||
50 | // #include "config.h" | 50 | // #include "config.h" |
51 | #include "TEWidget.h" | 51 | #include "TEWidget.h" |
52 | #include "session.h" | 52 | #include "session.h" |
53 | #include <qpe/config.h> | 53 | #include <qpe/config.h> |
54 | 54 | ||
55 | #include <qapplication.h> | 55 | #include <qapplication.h> |
56 | #include <qcursor.h> | 56 | #include <qcursor.h> |
57 | #include <qregexp.h> | 57 | #include <qregexp.h> |
58 | #include <qpainter.h> | 58 | #include <qpainter.h> |
59 | #include <qclipboard.h> | 59 | #include <qclipboard.h> |
60 | #include <qstyle.h> | 60 | #include <qstyle.h> |
61 | #include <qfile.h> | 61 | #include <qfile.h> |
62 | #include <qdragobject.h> | 62 | #include <qdragobject.h> |
63 | #include <qvbox.h> | 63 | #include <qvbox.h> |
64 | 64 | ||
65 | #include <stdio.h> | 65 | #include <stdio.h> |
66 | #include <stdlib.h> | 66 | #include <stdlib.h> |
67 | #include <unistd.h> | 67 | #include <unistd.h> |
68 | #include <ctype.h> | 68 | #include <ctype.h> |
69 | #include <sys/stat.h> | 69 | #include <sys/stat.h> |
70 | #include <sys/types.h> | 70 | #include <sys/types.h> |
71 | #include <signal.h> | 71 | #include <signal.h> |
72 | 72 | ||
73 | #include <assert.h> | 73 | #include <assert.h> |
74 | 74 | ||
75 | 75 | ||
76 | 76 | ||
77 | // #include "TEWidget.moc" | 77 | // #include "TEWidget.moc" |
78 | //#include <kapp.h> | 78 | //#include <kapp.h> |
79 | //#include <kcursor.h> | 79 | //#include <kcursor.h> |
80 | //#include <kurl.h> | 80 | //#include <kurl.h> |
81 | //#include <kdebug.h> | 81 | //#include <kdebug.h> |
82 | //#include <klocale.h> | 82 | //#include <klocale.h> |
83 | 83 | ||
84 | #define HERE printf("%s(%d): %s\n",__FILE__,__LINE__,__FUNCTION__) | 84 | #define HERE printf("%s(%d): %s\n",__FILE__,__LINE__,__FUNCTION__) |
85 | #define HCNT(Name) // { static int cnt = 1; printf("%s(%d): %s %d\n",__FILE__,__LINE__,Name,cnt++); } | 85 | #define HCNT(Name) // { static int cnt = 1; printf("%s(%d): %s %d\n",__FILE__,__LINE__,Name,cnt++); } |
86 | 86 | ||
87 | #define loc(X,Y) ((Y)*columns+(X)) | 87 | #define loc(X,Y) ((Y)*columns+(X)) |
88 | 88 | ||
89 | //FIXME: the rim should normally be 1, 0 only when running in full screen mode. | 89 | //FIXME: the rim should normally be 1, 0 only when running in full screen mode. |
90 | #define rimX 0 // left/right rim width | 90 | #define rimX 0 // left/right rim width |
91 | #define rimY 0 // top/bottom rim high | 91 | #define rimY 0 // top/bottom rim high |
92 | 92 | ||
93 | #define yMouseScroll 1 | 93 | #define yMouseScroll 1 |
94 | // scroll increment used when dragging selection at top/bottom of window. | 94 | // scroll increment used when dragging selection at top/bottom of window. |
95 | 95 | ||
96 | /* Button XPM */ | 96 | /* Button XPM */ |
97 | namespace { | 97 | namespace { |
98 | static char * menu_xpm[] = { | 98 | static char * menu_xpm[] = { |
99 | "12 12 5 1", | 99 | "12 12 5 1", |
100 | " c None", | 100 | " c None", |
101 | ".c #000000", | 101 | ".c #000000", |
102 | "+c #FFFDAD", | 102 | "+c #FFFDAD", |
103 | "@c #FFFF00", | 103 | "@c #FFFF00", |
104 | "#c #E5E100", | 104 | "#c #E5E100", |
105 | " ", | 105 | " ", |
106 | " ", | 106 | " ", |
107 | " ......... ", | 107 | " ......... ", |
108 | " .+++++++. ", | 108 | " .+++++++. ", |
109 | " .+@@@@#. ", | 109 | " .+@@@@#. ", |
110 | " .+@@@#. ", | 110 | " .+@@@#. ", |
111 | " .+@@#. ", | 111 | " .+@@#. ", |
112 | " .+@#. ", | 112 | " .+@#. ", |
113 | " .+#. ", | 113 | " .+#. ", |
114 | " .+. ", | 114 | " .+. ", |
115 | " .. ", | 115 | " .. ", |
116 | " "}; | 116 | " "}; |
117 | 117 | ||
118 | } | 118 | } |
119 | 119 | ||
120 | 120 | ||
121 | /* ------------------------------------------------------------------------- */ | 121 | /* ------------------------------------------------------------------------- */ |
122 | /* */ | 122 | /* */ |
123 | /* Colors */ | 123 | /* Colors */ |
124 | /* */ | 124 | /* */ |
125 | /* ------------------------------------------------------------------------- */ | 125 | /* ------------------------------------------------------------------------- */ |
126 | 126 | ||
127 | //FIXME: the default color table is in session.C now. | 127 | //FIXME: the default color table is in session.C now. |
128 | // We need a way to get rid of this one, here. | 128 | // We need a way to get rid of this one, here. |
129 | static const ColorEntry base_color_table[TABLE_COLORS] = | 129 | static const ColorEntry base_color_table[TABLE_COLORS] = |
130 | // The following are almost IBM standard color codes, with some slight | 130 | // The following are almost IBM standard color codes, with some slight |
131 | // gamma correction for the dim colors to compensate for bright X screens. | 131 | // gamma correction for the dim colors to compensate for bright X screens. |
132 | // It contains the 8 ansiterm/xterm colors in 2 intensities. | 132 | // It contains the 8 ansiterm/xterm colors in 2 intensities. |
133 | { | 133 | { |
134 | // Fixme: could add faint colors here, also. | 134 | // Fixme: could add faint colors here, also. |
135 | // normal | 135 | // normal |
136 | ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 1, 0 ), // Dfore, Dback | 136 | ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 1, 0 ), // Dfore, Dback |
137 | ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0x18), 0, 0 ), // Black, Red | 137 | ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0x18), 0, 0 ), // Black, Red |
138 | ColorEntry(QColor(0x18,0xB2,0x18), 0, 0 ), ColorEntry( QColor(0xB2,0x68,0x18), 0, 0 ), // Green, Yellow | 138 | ColorEntry(QColor(0x18,0xB2,0x18), 0, 0 ), ColorEntry( QColor(0xB2,0x68,0x18), 0, 0 ), // Green, Yellow |
139 | ColorEntry(QColor(0x18,0x18,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0xB2), 0, 0 ), // Blue, Magenta | 139 | ColorEntry(QColor(0x18,0x18,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0xB2), 0, 0 ), // Blue, Magenta |
140 | ColorEntry(QColor(0x18,0xB2,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 0, 0 ), // Cyan, White | 140 | ColorEntry(QColor(0x18,0xB2,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 0, 0 ), // Cyan, White |
141 | // intensiv | 141 | // intensiv |
142 | ColorEntry(QColor(0x00,0x00,0x00), 0, 1 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 1, 0 ), | 142 | ColorEntry(QColor(0x00,0x00,0x00), 0, 1 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 1, 0 ), |
143 | ColorEntry(QColor(0x68,0x68,0x68), 0, 0 ), ColorEntry( QColor(0xFF,0x54,0x54), 0, 0 ), | 143 | ColorEntry(QColor(0x68,0x68,0x68), 0, 0 ), ColorEntry( QColor(0xFF,0x54,0x54), 0, 0 ), |
144 | ColorEntry(QColor(0x54,0xFF,0x54), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0x54), 0, 0 ), | 144 | ColorEntry(QColor(0x54,0xFF,0x54), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0x54), 0, 0 ), |
145 | ColorEntry(QColor(0x54,0x54,0xFF), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0xB2), 0, 0 ), | 145 | ColorEntry(QColor(0x54,0x54,0xFF), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0xB2), 0, 0 ), |
146 | ColorEntry(QColor(0x54,0xFF,0xFF), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 0, 0 ) | 146 | ColorEntry(QColor(0x54,0xFF,0xFF), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 0, 0 ) |
147 | }; | 147 | }; |
148 | 148 | ||
149 | /* Note that we use ANSI color order (bgr), while IBMPC color order is (rgb) | 149 | /* Note that we use ANSI color order (bgr), while IBMPC color order is (rgb) |
150 | 150 | ||
151 | Code 0 1 2 3 4 5 6 7 | 151 | Code 0 1 2 3 4 5 6 7 |
152 | ----------- ------- ------- ------- ------- ------- ------- ------- ------- | 152 | ----------- ------- ------- ------- ------- ------- ------- ------- ------- |
153 | ANSI (bgr) Black Red Green Yellow Blue Magenta Cyan White | 153 | ANSI (bgr) Black Red Green Yellow Blue Magenta Cyan White |
154 | IBMPC (rgb) Black Blue Green Cyan Red Magenta Yellow White | 154 | IBMPC (rgb) Black Blue Green Cyan Red Magenta Yellow White |
155 | */ | 155 | */ |
156 | 156 | ||
157 | QColor TEWidget::getDefaultBackColor() | 157 | QColor TEWidget::getDefaultBackColor() |
158 | { | 158 | { |
159 | return color_table[DEFAULT_BACK_COLOR].color; | 159 | return color_table[DEFAULT_BACK_COLOR].color; |
160 | } | 160 | } |
161 | 161 | ||
162 | const ColorEntry* TEWidget::getColorTable() const | 162 | const ColorEntry* TEWidget::getColorTable() const |
163 | { | 163 | { |
164 | return color_table; | 164 | return color_table; |
165 | } | 165 | } |
166 | 166 | ||
167 | const ColorEntry* TEWidget::getdefaultColorTable() const | 167 | const ColorEntry* TEWidget::getdefaultColorTable() const |
168 | { | 168 | { |
169 | return base_color_table; | 169 | return base_color_table; |
170 | } | 170 | } |
171 | 171 | ||
172 | 172 | ||
173 | const QPixmap *TEWidget::backgroundPixmap() | 173 | const QPixmap *TEWidget::backgroundPixmap() |
174 | { | 174 | { |
175 | static QPixmap *bg = new QPixmap("~/qpim/main/pics/faded_bg.xpm"); | 175 | static QPixmap *bg = new QPixmap("~/qpim/main/pics/faded_bg.xpm"); |
176 | const QPixmap *pm = bg; | 176 | const QPixmap *pm = bg; |
177 | return pm; | 177 | return pm; |
178 | } | 178 | } |
179 | 179 | ||
180 | void TEWidget::setColorTable(const ColorEntry table[]) | 180 | void TEWidget::setColorTable(const ColorEntry table[]) |
181 | { | 181 | { |
182 | for (int i = 0; i < TABLE_COLORS; i++) color_table[i] = table[i]; | 182 | for (int i = 0; i < TABLE_COLORS; i++) color_table[i] = table[i]; |
183 | 183 | ||
184 | const QPixmap* pm = backgroundPixmap(); | 184 | const QPixmap* pm = backgroundPixmap(); |
185 | if (!pm) setBackgroundColor(color_table[DEFAULT_BACK_COLOR].color); | 185 | if (!pm) setBackgroundColor(color_table[DEFAULT_BACK_COLOR].color); |
186 | update(); | 186 | update(); |
187 | } | 187 | } |
188 | 188 | ||
189 | //FIXME: add backgroundPixmapChanged. | 189 | //FIXME: add backgroundPixmapChanged. |
190 | 190 | ||
191 | /* ------------------------------------------------------------------------- */ | 191 | /* ------------------------------------------------------------------------- */ |
192 | /* */ | 192 | /* */ |
193 | /* Font */ | 193 | /* Font */ |
194 | /* */ | 194 | /* */ |
195 | /* ------------------------------------------------------------------------- */ | 195 | /* ------------------------------------------------------------------------- */ |
196 | 196 | ||
197 | /* | 197 | /* |
198 | The VT100 has 32 special graphical characters. The usual vt100 extended | 198 | The VT100 has 32 special graphical characters. The usual vt100 extended |
199 | xterm fonts have these at 0x00..0x1f. | 199 | xterm fonts have these at 0x00..0x1f. |
200 | 200 | ||
201 | QT's iso mapping leaves 0x00..0x7f without any changes. But the graphicals | 201 | QT's iso mapping leaves 0x00..0x7f without any changes. But the graphicals |
202 | come in here as proper unicode characters. | 202 | come in here as proper unicode characters. |
203 | 203 | ||
204 | We treat non-iso10646 fonts as VT100 extended and do the requiered mapping | 204 | We treat non-iso10646 fonts as VT100 extended and do the requiered mapping |
205 | from unicode to 0x00..0x1f. The remaining translation is then left to the | 205 | from unicode to 0x00..0x1f. The remaining translation is then left to the |
206 | QCodec. | 206 | QCodec. |
207 | */ | 207 | */ |
208 | 208 | ||
209 | // assert for i in [0..31] : vt100extended(vt100_graphics[i]) == i. | 209 | // assert for i in [0..31] : vt100extended(vt100_graphics[i]) == i. |
210 | 210 | ||
211 | unsigned short vt100_graphics[32] = | 211 | unsigned short vt100_graphics[32] = |
212 | { // 0/8 1/9 2/10 3/11 4/12 5/13 6/14 7/15 | 212 | { // 0/8 1/9 2/10 3/11 4/12 5/13 6/14 7/15 |
213 | 0x0020, 0x25C6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, | 213 | 0x0020, 0x25C6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, |
214 | 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, | 214 | 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, |
215 | 0xF800, 0xF801, 0x2500, 0xF803, 0xF804, 0x251c, 0x2524, 0x2534, | 215 | 0xF800, 0xF801, 0x2500, 0xF803, 0xF804, 0x251c, 0x2524, 0x2534, |
216 | 0x252c, 0x2502, 0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00b7 | 216 | 0x252c, 0x2502, 0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00b7 |
217 | }; | 217 | }; |
218 | 218 | ||
219 | static QChar vt100extended(QChar c) | 219 | static QChar vt100extended(QChar c) |
220 | { | 220 | { |
221 | switch (c.unicode()) | 221 | switch (c.unicode()) |
222 | { | 222 | { |
223 | case 0x25c6 : return 1; | 223 | case 0x25c6 : return 1; |
224 | case 0x2592 : return 2; | 224 | case 0x2592 : return 2; |
225 | case 0x2409 : return 3; | 225 | case 0x2409 : return 3; |
226 | case 0x240c : return 4; | 226 | case 0x240c : return 4; |
227 | case 0x240d : return 5; | 227 | case 0x240d : return 5; |
228 | case 0x240a : return 6; | 228 | case 0x240a : return 6; |
229 | case 0x00b0 : return 7; | 229 | case 0x00b0 : return 7; |
230 | case 0x00b1 : return 8; | 230 | case 0x00b1 : return 8; |
231 | case 0x2424 : return 9; | 231 | case 0x2424 : return 9; |
232 | case 0x240b : return 10; | 232 | case 0x240b : return 10; |
233 | case 0x2518 : return 11; | 233 | case 0x2518 : return 11; |
234 | case 0x2510 : return 12; | 234 | case 0x2510 : return 12; |
235 | case 0x250c : return 13; | 235 | case 0x250c : return 13; |
236 | case 0x2514 : return 14; | 236 | case 0x2514 : return 14; |
237 | case 0x253c : return 15; | 237 | case 0x253c : return 15; |
238 | case 0xf800 : return 16; | 238 | case 0xf800 : return 16; |
239 | case 0xf801 : return 17; | 239 | case 0xf801 : return 17; |
240 | case 0x2500 : return 18; | 240 | case 0x2500 : return 18; |
241 | case 0xf803 : return 19; | 241 | case 0xf803 : return 19; |
242 | case 0xf804 : return 20; | 242 | case 0xf804 : return 20; |
243 | case 0x251c : return 21; | 243 | case 0x251c : return 21; |
244 | case 0x2524 : return 22; | 244 | case 0x2524 : return 22; |
245 | case 0x2534 : return 23; | 245 | case 0x2534 : return 23; |
246 | case 0x252c : return 24; | 246 | case 0x252c : return 24; |
247 | case 0x2502 : return 25; | 247 | case 0x2502 : return 25; |
248 | case 0x2264 : return 26; | 248 | case 0x2264 : return 26; |
249 | case 0x2265 : return 27; | 249 | case 0x2265 : return 27; |
250 | case 0x03c0 : return 28; | 250 | case 0x03c0 : return 28; |
251 | case 0x2260 : return 29; | 251 | case 0x2260 : return 29; |
252 | case 0x00a3 : return 30; | 252 | case 0x00a3 : return 30; |
253 | case 0x00b7 : return 31; | 253 | case 0x00b7 : return 31; |
254 | } | 254 | } |
255 | return c; | 255 | return c; |
256 | } | 256 | } |
257 | 257 | ||
258 | static QChar identicalMap(QChar c) | 258 | static QChar identicalMap(QChar c) |
259 | { | 259 | { |
260 | return c; | 260 | return c; |
261 | } | 261 | } |
262 | 262 | ||
263 | void TEWidget::fontChange(const QFont &) | 263 | void TEWidget::fontChange(const QFont &) |
264 | { | 264 | { |
265 | QFontMetrics fm(font()); | 265 | QFontMetrics fm(font()); |
266 | font_h = fm.height(); | 266 | font_h = fm.height(); |
267 | font_w = fm.maxWidth(); | 267 | font_w = fm.maxWidth(); |
268 | font_a = fm.ascent(); | 268 | font_a = fm.ascent(); |
269 | //printf("font_h: %d\n",font_h); | 269 | //printf("font_h: %d\n",font_h); |
270 | //printf("font_w: %d\n",font_w); | 270 | //printf("font_w: %d\n",font_w); |
271 | //printf("font_a: %d\n",font_a); | 271 | //printf("font_a: %d\n",font_a); |
272 | //printf("charset: %s\n",QFont::encodingName(font().charSet()).ascii()); | 272 | //printf("charset: %s\n",QFont::encodingName(font().charSet()).ascii()); |
273 | //printf("rawname: %s\n",font().rawName().ascii()); | 273 | //printf("rawname: %s\n",font().rawName().ascii()); |
274 | fontMap = | 274 | fontMap = |
275 | #if QT_VERSION < 300 | 275 | #if QT_VERSION < 300 |
276 | strcmp(QFont::encodingName(font().charSet()).ascii(),"iso10646") | 276 | strcmp(QFont::encodingName(font().charSet()).ascii(),"iso10646") |
277 | ? vt100extended | 277 | ? vt100extended |
278 | : | 278 | : |
279 | #endif | 279 | #endif |
280 | identicalMap; | 280 | identicalMap; |
281 | propagateSize(); | 281 | propagateSize(); |
282 | update(); | 282 | update(); |
283 | } | 283 | } |
284 | 284 | ||
285 | void TEWidget::setVTFont(const QFont& f) | 285 | void TEWidget::setVTFont(const QFont& f) |
286 | { | 286 | { |
287 | QFrame::setFont(f); | 287 | QFrame::setFont(f); |
288 | } | 288 | } |
289 | 289 | ||
290 | QFont TEWidget::getVTFont() { | 290 | QFont TEWidget::getVTFont() { |
291 | return font(); | 291 | return font(); |
292 | } | 292 | } |
293 | 293 | ||
294 | void TEWidget::setFont(const QFont &) | 294 | void TEWidget::setFont(const QFont &) |
295 | { | 295 | { |
296 | // ignore font change request if not coming from konsole itself | 296 | // ignore font change request if not coming from konsole itself |
297 | } | 297 | } |
298 | 298 | ||
299 | /* ------------------------------------------------------------------------- */ | 299 | /* ------------------------------------------------------------------------- */ |
300 | /* */ | 300 | /* */ |
301 | /* Constructor / Destructor */ | 301 | /* Constructor / Destructor */ |
302 | /* */ | 302 | /* */ |
303 | /* ----------------------------------------------------------------------- */ | 303 | /* ----------------------------------------------------------------------- */ |
304 | 304 | ||
305 | 305 | ||
306 | 306 | ||
307 | TEWidget::TEWidget(QWidget *parent, const char *name) : QFrame(parent,name) | 307 | TEWidget::TEWidget(QWidget *parent, const char *name) : QFrame(parent,name) |
308 | { | 308 | { |
309 | #ifndef QT_NO_CLIPBOARD | 309 | #ifndef QT_NO_CLIPBOARD |
310 | cb = QApplication::clipboard(); | 310 | cb = QApplication::clipboard(); |
311 | QObject::connect( (QObject*)cb, SIGNAL(dataChanged()), | 311 | QObject::connect( (QObject*)cb, SIGNAL(dataChanged()), |
312 | this, SLOT(onClearSelection()) ); | 312 | this, SLOT(onClearSelection()) ); |
313 | #endif | 313 | #endif |
314 | 314 | ||
315 | 315 | ||
316 | scrollbar = new QScrollBar( this ); | 316 | scrollbar = new QScrollBar( this ); |
317 | scrollbar->setCursor( arrowCursor ); | 317 | scrollbar->setCursor( arrowCursor ); |
318 | connect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollChanged(int))); | 318 | connect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollChanged(int))); |
319 | 319 | ||
320 | hscrollbar = new QScrollBar( Qt::Horizontal, this ); | 320 | hscrollbar = new QScrollBar( Qt::Horizontal, this ); |
321 | hscrollbar->setCursor( arrowCursor ); | 321 | hscrollbar->setCursor( arrowCursor ); |
322 | connect(hscrollbar, SIGNAL(valueChanged(int)), this, SLOT(hscrollChanged(int))); | 322 | connect(hscrollbar, SIGNAL(valueChanged(int)), this, SLOT(hscrollChanged(int))); |
323 | 323 | ||
324 | m_cornerButton = new QPushButton( this ); | 324 | m_cornerButton = new QPushButton( this ); |
325 | m_cornerButton->setPixmap( QPixmap( (const char**)menu_xpm ) ); | 325 | m_cornerButton->setPixmap( QPixmap( (const char**)menu_xpm ) ); |
326 | m_cornerButton->setMaximumSize( 14, 14 ); | 326 | m_cornerButton->setMaximumSize( 14, 14 ); |
327 | m_cornerButton->hide(); | 327 | m_cornerButton->hide(); |
328 | 328 | ||
329 | Config cfg("Konsole"); | 329 | Config cfg("Konsole"); |
330 | cfg.setGroup("ScrollBar"); | 330 | cfg.setGroup("ScrollBar"); |
331 | switch( cfg.readNumEntry("Position",2)){ | 331 | switch( cfg.readNumEntry("Position",2)){ |
332 | case 0: | 332 | case 0: |
333 | scrollLoc = SCRNONE; | 333 | scrollLoc = SCRNONE; |
334 | break; | 334 | break; |
335 | case 1: | 335 | case 1: |
336 | scrollLoc = SCRLEFT; | 336 | scrollLoc = SCRLEFT; |
337 | break; | 337 | break; |
338 | case 2: | 338 | case 2: |
339 | scrollLoc = SCRRIGHT; | 339 | scrollLoc = SCRRIGHT; |
340 | break; | 340 | break; |
341 | }; | 341 | }; |
342 | 342 | ||
343 | blinkT = new QTimer(this); | 343 | blinkT = new QTimer(this); |
344 | connect(blinkT, SIGNAL(timeout()), this, SLOT(blinkEvent())); | 344 | connect(blinkT, SIGNAL(timeout()), this, SLOT(blinkEvent())); |
345 | // blinking = FALSE; | 345 | // blinking = FALSE; |
346 | blinking = TRUE; | 346 | blinking = TRUE; |
347 | 347 | ||
348 | resizing = FALSE; | 348 | resizing = FALSE; |
349 | actSel = 0; | 349 | actSel = 0; |
350 | image = 0; | 350 | image = 0; |
351 | lines = 1; | 351 | lines = 1; |
352 | columns = 1; | 352 | columns = 1; |
353 | font_w = 1; | 353 | font_w = 1; |
354 | font_h = 1; | 354 | font_h = 1; |
355 | font_a = 1; | 355 | font_a = 1; |
356 | word_selection_mode = FALSE; | 356 | word_selection_mode = FALSE; |
357 | vcolumns = 0; | 357 | vcolumns = 0; |
358 | hposition = 0; | ||
358 | 359 | ||
359 | setMouseMarks(TRUE); | 360 | setMouseMarks(TRUE); |
360 | setVTFont( QFont("fixed") ); | 361 | setVTFont( QFont("fixed") ); |
361 | setColorTable(base_color_table); // init color table | 362 | setColorTable(base_color_table); // init color table |
362 | 363 | ||
363 | qApp->installEventFilter( this ); //FIXME: see below | 364 | qApp->installEventFilter( this ); //FIXME: see below |
364 | // KCursor::setAutoHideCursor( this, true ); | 365 | // KCursor::setAutoHideCursor( this, true ); |
365 | 366 | ||
366 | // Init DnD //////////////////////////////////////////////////////////////// | 367 | // Init DnD //////////////////////////////////////////////////////////////// |
367 | currentSession = NULL; | 368 | currentSession = NULL; |
368 | // setAcceptDrops(true); // attempt | 369 | // setAcceptDrops(true); // attempt |
369 | // m_drop = new QPopupMenu(this); | 370 | // m_drop = new QPopupMenu(this); |
370 | // m_drop->insertItem( QString("Paste"), 0); | 371 | // m_drop->insertItem( QString("Paste"), 0); |
371 | // m_drop->insertItem( QString("cd"), 1); | 372 | // m_drop->insertItem( QString("cd"), 1); |
372 | // connect(m_drop, SIGNAL(activated(int)), SLOT(drop_menu_activated(int))); | 373 | // connect(m_drop, SIGNAL(activated(int)), SLOT(drop_menu_activated(int))); |
373 | 374 | ||
374 | // we need focus so that the auto-hide cursor feature works | 375 | // we need focus so that the auto-hide cursor feature works |
375 | setFocus(); | 376 | setFocus(); |
376 | setFocusPolicy( WheelFocus ); | 377 | setFocusPolicy( WheelFocus ); |
377 | } | 378 | } |
378 | 379 | ||
379 | //FIXME: make proper destructor | 380 | //FIXME: make proper destructor |
380 | // Here's a start (David) | 381 | // Here's a start (David) |
381 | TEWidget::~TEWidget() | 382 | TEWidget::~TEWidget() |
382 | { | 383 | { |
383 | qApp->removeEventFilter( this ); | 384 | qApp->removeEventFilter( this ); |
384 | if (image) free(image); | 385 | if (image) free(image); |
385 | } | 386 | } |
386 | 387 | ||
387 | /* ------------------------------------------------------------------------- */ | 388 | /* ------------------------------------------------------------------------- */ |
388 | /* */ | 389 | /* */ |
389 | /* Display Operations */ | 390 | /* Display Operations */ |
390 | /* */ | 391 | /* */ |
391 | /* ------------------------------------------------------------------------- */ | 392 | /* ------------------------------------------------------------------------- */ |
392 | 393 | ||
393 | /*! | 394 | /*! |
394 | attributed string draw primitive | 395 | attributed string draw primitive |
395 | */ | 396 | */ |
396 | 397 | ||
397 | void TEWidget::drawAttrStr(QPainter &paint, QRect rect, | 398 | void TEWidget::drawAttrStr(QPainter &paint, QRect rect, |
398 | QString& str, ca attr, BOOL pm, BOOL clear) | 399 | QString& str, ca attr, BOOL pm, BOOL clear) |
399 | { | 400 | { |
400 | if (pm && color_table[attr.b].transparent) | 401 | if (pm && color_table[attr.b].transparent) |
401 | { | 402 | { |
402 | paint.setBackgroundMode( TransparentMode ); | 403 | paint.setBackgroundMode( TransparentMode ); |
403 | if (clear) erase(rect); | 404 | if (clear) erase(rect); |
404 | } | 405 | } |
405 | else | 406 | else |
406 | { | 407 | { |
407 | if (blinking) | 408 | if (blinking) |
408 | paint.fillRect(rect, color_table[attr.b].color); | 409 | paint.fillRect(rect, color_table[attr.b].color); |
409 | else | 410 | else |
410 | { | 411 | { |
411 | paint.setBackgroundMode( OpaqueMode ); | 412 | paint.setBackgroundMode( OpaqueMode ); |
412 | paint.setBackgroundColor( color_table[attr.b].color ); | 413 | paint.setBackgroundColor( color_table[attr.b].color ); |
413 | } | 414 | } |
414 | } | 415 | } |
415 | 416 | ||
416 | if (color_table[attr.f].bold) | 417 | if (color_table[attr.f].bold) |
417 | paint.setPen(QColor( 0x8F, 0x00, 0x00 )); | 418 | paint.setPen(QColor( 0x8F, 0x00, 0x00 )); |
418 | else | 419 | else |
419 | paint.setPen(color_table[attr.f].color); | 420 | paint.setPen(color_table[attr.f].color); |
420 | 421 | ||
421 | paint.drawText(rect.x(),rect.y()+font_a, str); | 422 | paint.drawText(rect.x(),rect.y()+font_a, str); |
422 | 423 | ||
423 | if (attr.r & RE_UNDERLINE) | 424 | if (attr.r & RE_UNDERLINE) |
424 | paint.drawLine(rect.left(), rect.y()+font_a+1, rect.right(),rect.y()+font_a+1 ); | 425 | paint.drawLine(rect.left(), rect.y()+font_a+1, rect.right(),rect.y()+font_a+1 ); |
425 | } | 426 | } |
426 | 427 | ||
427 | /*! | 428 | /*! |
428 | The image can only be set completely. | 429 | The image can only be set completely. |
429 | 430 | ||
430 | The size of the new image may or may not match the size of the widget. | 431 | The size of the new image may or may not match the size of the widget. |
431 | */ | 432 | */ |
432 | 433 | ||
433 | void TEWidget::setImage(const ca* const newimg, int lines, int columns) | 434 | void TEWidget::setImage(const ca* const newimg, int lines, int columns) |
434 | { int y,x,len; | 435 | { int y,x,len; |
435 | const QPixmap* pm = backgroundPixmap(); | 436 | const QPixmap* pm = backgroundPixmap(); |
436 | QPainter paint; | 437 | QPainter paint; |
437 | setUpdatesEnabled(FALSE); | 438 | setUpdatesEnabled(FALSE); |
438 | paint.begin( this ); | 439 | paint.begin( this ); |
439 | HCNT("setImage"); | 440 | HCNT("setImage"); |
440 | 441 | ||
441 | QPoint tL = contentsRect().topLeft(); | 442 | QPoint tL = contentsRect().topLeft(); |
442 | int tLx = tL.x(); | 443 | int tLx = tL.x(); |
443 | int tLy = tL.y(); | 444 | int tLy = tL.y(); |
444 | hasBlinker = FALSE; | 445 | hasBlinker = FALSE; |
445 | 446 | ||
446 | int cf = -1; // undefined | 447 | int cf = -1; // undefined |
447 | int cb = -1; // undefined | 448 | int cb = -1; // undefined |
448 | int cr = -1; // undefined | 449 | int cr = -1; // undefined |
449 | 450 | ||
450 | int lins = QMIN(this->lines, QMAX(0,lines )); | 451 | int lins = QMIN(this->lines, QMAX(0,lines )); |
451 | int cols = QMIN(this->columns,QMAX(0,columns)); | 452 | int cols = QMIN(this->columns,QMAX(0,columns)); |
452 | QChar *disstrU = new QChar[cols]; | 453 | QChar *disstrU = new QChar[cols]; |
453 | 454 | ||
454 | //{ static int cnt = 0; printf("setImage %d\n",cnt++); } | 455 | //{ static int cnt = 0; printf("setImage %d\n",cnt++); } |
455 | for (y = 0; y < lins; y++) | 456 | for (y = 0; y < lins; y++) |
456 | { | 457 | { |
457 | const ca* lcl = &image[y*this->columns]; | 458 | const ca* lcl = &image[y*this->columns]; |
458 | const ca* const ext = &newimg[y*columns]; | 459 | const ca* const ext = &newimg[y*columns]; |
459 | if (!resizing) // not while resizing, we're expecting a paintEvent | 460 | if (!resizing) // not while resizing, we're expecting a paintEvent |
460 | for (x = 0; x < cols; x++) | 461 | for (x = 0; x < cols; x++) |
461 | { | 462 | { |
462 | hasBlinker |= (ext[x].r & RE_BLINK); | 463 | hasBlinker |= (ext[x].r & RE_BLINK); |
463 | if (ext[x] != lcl[x]) | 464 | if (ext[x] != lcl[x]) |
464 | { | 465 | { |
465 | cr = ext[x].r; | 466 | cr = ext[x].r; |
466 | cb = ext[x].b; | 467 | cb = ext[x].b; |
467 | if (ext[x].f != cf) cf = ext[x].f; | 468 | if (ext[x].f != cf) cf = ext[x].f; |
468 | int lln = cols - x; | 469 | int lln = cols - x; |
469 | disstrU[0] = fontMap(ext[x+0].c); | 470 | disstrU[0] = fontMap(ext[x+0].c); |
470 | for (len = 1; len < lln; len++) | 471 | for (len = 1; len < lln; len++) |
471 | { | 472 | { |
472 | if (ext[x+len].f != cf || ext[x+len].b != cb || ext[x+len].r != cr || | 473 | if (ext[x+len].f != cf || ext[x+len].b != cb || ext[x+len].r != cr || |
473 | ext[x+len] == lcl[x+len] ) | 474 | ext[x+len] == lcl[x+len] ) |
474 | break; | 475 | break; |
475 | disstrU[len] = fontMap(ext[x+len].c); | 476 | disstrU[len] = fontMap(ext[x+len].c); |
476 | } | 477 | } |
477 | QString unistr(disstrU,len); | 478 | QString unistr(disstrU,len); |
478 | drawAttrStr(paint, | 479 | drawAttrStr(paint, |
479 | QRect(blX+tLx+font_w*x,bY+tLy+font_h*y,font_w*len,font_h), | 480 | QRect(blX+tLx+font_w*x,bY+tLy+font_h*y,font_w*len,font_h), |
480 | unistr, ext[x], pm != NULL, true); | 481 | unistr, ext[x], pm != NULL, true); |
481 | x += len - 1; | 482 | x += len - 1; |
482 | } | 483 | } |
483 | } | 484 | } |
484 | // finally, make `image' become `newimg'. | 485 | // finally, make `image' become `newimg'. |
485 | memcpy((void*)lcl,(const void*)ext,cols*sizeof(ca)); | 486 | memcpy((void*)lcl,(const void*)ext,cols*sizeof(ca)); |
486 | } | 487 | } |
487 | drawFrame( &paint ); | 488 | drawFrame( &paint ); |
488 | paint.end(); | 489 | paint.end(); |
489 | setUpdatesEnabled(TRUE); | 490 | setUpdatesEnabled(TRUE); |
490 | if ( hasBlinker && !blinkT->isActive()) blinkT->start(1000); // 1000 ms | 491 | if ( hasBlinker && !blinkT->isActive()) blinkT->start(1000); // 1000 ms |
491 | if (!hasBlinker && blinkT->isActive()) { blinkT->stop(); blinking = FALSE; } | 492 | if (!hasBlinker && blinkT->isActive()) { blinkT->stop(); blinking = FALSE; } |
492 | delete [] disstrU; | 493 | delete [] disstrU; |
493 | } | 494 | } |
494 | 495 | ||
495 | // paint Event //////////////////////////////////////////////////// | 496 | // paint Event //////////////////////////////////////////////////// |
496 | 497 | ||
497 | /*! | 498 | /*! |
498 | The difference of this routine vs. the `setImage' is, | 499 | The difference of this routine vs. the `setImage' is, |
499 | that the drawing does not include a difference analysis | 500 | that the drawing does not include a difference analysis |
500 | between the old and the new image. Instead, the internal | 501 | between the old and the new image. Instead, the internal |
501 | image is used and the painting bound by the PaintEvent box. | 502 | image is used and the painting bound by the PaintEvent box. |
502 | */ | 503 | */ |
503 | 504 | ||
504 | void TEWidget::paintEvent( QPaintEvent* pe ) | 505 | void TEWidget::paintEvent( QPaintEvent* pe ) |
505 | { | 506 | { |
506 | 507 | ||
507 | //{ static int cnt = 0; printf("paint %d\n",cnt++); } | 508 | //{ static int cnt = 0; printf("paint %d\n",cnt++); } |
508 | const QPixmap* pm = backgroundPixmap(); | 509 | const QPixmap* pm = backgroundPixmap(); |
509 | QPainter paint; | 510 | QPainter paint; |
510 | setUpdatesEnabled(FALSE); | 511 | setUpdatesEnabled(FALSE); |
511 | paint.begin( this ); | 512 | paint.begin( this ); |
512 | paint.setBackgroundMode( TransparentMode ); | 513 | paint.setBackgroundMode( TransparentMode ); |
513 | HCNT("paintEvent"); | 514 | HCNT("paintEvent"); |
514 | 515 | ||
515 | // Note that the actual widget size can be slightly larger | 516 | // Note that the actual widget size can be slightly larger |
516 | // that the image (the size is truncated towards the smaller | 517 | // that the image (the size is truncated towards the smaller |
517 | // number of characters in `resizeEvent'. The paint rectangle | 518 | // number of characters in `resizeEvent'. The paint rectangle |
518 | // can thus be larger than the image, but less then the size | 519 | // can thus be larger than the image, but less then the size |
519 | // of one character. | 520 | // of one character. |
520 | 521 | ||
521 | QRect rect = pe->rect().intersect(contentsRect()); | 522 | QRect rect = pe->rect().intersect(contentsRect()); |
522 | 523 | ||
523 | QPoint tL = contentsRect().topLeft(); | 524 | QPoint tL = contentsRect().topLeft(); |
524 | int tLx = tL.x(); | 525 | int tLx = tL.x(); |
525 | int tLy = tL.y(); | 526 | int tLy = tL.y(); |
526 | 527 | ||
527 | int lux = QMIN(columns-1, QMAX(0,(rect.left() - tLx - blX ) / font_w)); | 528 | int lux = QMIN(columns-1, QMAX(0,(rect.left() - tLx - blX ) / font_w)); |
528 | int luy = QMIN(lines-1, QMAX(0,(rect.top() - tLy - bY ) / font_h)); | 529 | int luy = QMIN(lines-1, QMAX(0,(rect.top() - tLy - bY ) / font_h)); |
529 | int rlx = QMIN(columns-1, QMAX(0,(rect.right() - tLx - blX ) / font_w)); | 530 | int rlx = QMIN(columns-1, QMAX(0,(rect.right() - tLx - blX ) / font_w)); |
530 | int rly = QMIN(lines-1, QMAX(0,(rect.bottom() - tLy - bY ) / font_h)); | 531 | int rly = QMIN(lines-1, QMAX(0,(rect.bottom() - tLy - bY ) / font_h)); |
531 | 532 | ||
532 | /* | 533 | /* |
533 | printf("paintEvent: %d..%d, %d..%d (%d..%d, %d..%d)\n",lux,rlx,luy,rly, | 534 | printf("paintEvent: %d..%d, %d..%d (%d..%d, %d..%d)\n",lux,rlx,luy,rly, |
534 | rect.left(), rect.right(), rect.top(), rect.bottom()); | 535 | rect.left(), rect.right(), rect.top(), rect.bottom()); |
535 | */ | 536 | */ |
536 | 537 | ||
537 | // if (pm != NULL && color_table[image->b].transparent) | 538 | // if (pm != NULL && color_table[image->b].transparent) |
538 | // erase(rect); | 539 | // erase(rect); |
539 | // BL: I have no idea why we need this, and it breaks the refresh. | 540 | // BL: I have no idea why we need this, and it breaks the refresh. |
540 | 541 | ||
541 | QChar *disstrU = new QChar[columns]; | 542 | QChar *disstrU = new QChar[columns]; |
542 | for (int y = luy; y <= rly; y++) | 543 | for (int y = luy; y <= rly; y++) |
543 | for (int x = lux; x <= rlx; x++) | 544 | for (int x = lux; x <= rlx; x++) |
544 | { | 545 | { |
545 | int len = 1; | 546 | int len = 1; |
546 | disstrU[0] = fontMap(image[loc(x,y)].c); | 547 | disstrU[0] = fontMap(image[loc(x,y)].c); |
547 | int cf = image[loc(x,y)].f; | 548 | int cf = image[loc(x,y)].f; |
548 | int cb = image[loc(x,y)].b; | 549 | int cb = image[loc(x,y)].b; |
549 | int cr = image[loc(x,y)].r; | 550 | int cr = image[loc(x,y)].r; |
550 | while (x+len <= rlx && | 551 | while (x+len <= rlx && |
551 | image[loc(x+len,y)].f == cf && | 552 | image[loc(x+len,y)].f == cf && |
552 | image[loc(x+len,y)].b == cb && | 553 | image[loc(x+len,y)].b == cb && |
553 | image[loc(x+len,y)].r == cr ) | 554 | image[loc(x+len,y)].r == cr ) |
554 | { | 555 | { |
555 | disstrU[len] = fontMap(image[loc(x+len,y)].c); | 556 | disstrU[len] = fontMap(image[loc(x+len,y)].c); |
556 | len += 1; | 557 | len += 1; |
557 | } | 558 | } |
558 | QString unistr(disstrU,len); | 559 | QString unistr(disstrU,len); |
559 | drawAttrStr(paint, | 560 | drawAttrStr(paint, |
560 | QRect(blX+tLx+font_w*x,bY+tLy+font_h*y,font_w*len,font_h), | 561 | QRect(blX+tLx+font_w*x,bY+tLy+font_h*y,font_w*len,font_h), |
561 | unistr, image[loc(x,y)], pm != NULL, false); | 562 | unistr, image[loc(x,y)], pm != NULL, false); |
562 | x += len - 1; | 563 | x += len - 1; |
563 | } | 564 | } |
564 | delete [] disstrU; | 565 | delete [] disstrU; |
565 | drawFrame( &paint ); | 566 | drawFrame( &paint ); |
566 | paint.end(); | 567 | paint.end(); |
567 | setUpdatesEnabled(TRUE); | 568 | setUpdatesEnabled(TRUE); |
568 | } | 569 | } |
569 | 570 | ||
570 | void TEWidget::blinkEvent() | 571 | void TEWidget::blinkEvent() |
571 | { | 572 | { |
572 | blinking = !blinking; | 573 | blinking = !blinking; |
573 | repaint(FALSE); | 574 | repaint(FALSE); |
574 | } | 575 | } |
575 | 576 | ||
576 | /* ------------------------------------------------------------------------- */ | 577 | /* ------------------------------------------------------------------------- */ |
577 | /* */ | 578 | /* */ |
578 | /* Resizing */ | 579 | /* Resizing */ |
579 | /* */ | 580 | /* */ |
580 | /* ------------------------------------------------------------------------- */ | 581 | /* ------------------------------------------------------------------------- */ |
581 | 582 | ||
582 | void TEWidget::resizeEvent(QResizeEvent* ev) | 583 | void TEWidget::resizeEvent(QResizeEvent* ev) |
583 | { | 584 | { |
584 | // printf("resize: %d,%d\n",ev->size().width(),ev->size().height()); | 585 | // printf("resize: %d,%d\n",ev->size().width(),ev->size().height()); |
585 | //printf("approx: %d,%d\n",ev->size().width()/font_w,ev->size().height()/font_h); | 586 | //printf("approx: %d,%d\n",ev->size().width()/font_w,ev->size().height()/font_h); |
586 | //printf("leaves: %d,%d\n",ev->size().width()%font_w,ev->size().height()%font_h); | 587 | //printf("leaves: %d,%d\n",ev->size().width()%font_w,ev->size().height()%font_h); |
587 | //printf("curren: %d,%d\n",width(),height()); | 588 | //printf("curren: %d,%d\n",width(),height()); |
588 | HCNT("resizeEvent"); | 589 | HCNT("resizeEvent"); |
589 | 590 | ||
590 | // see comment in `paintEvent' concerning the rounding. | 591 | // see comment in `paintEvent' concerning the rounding. |
591 | //FIXME: could make a routine here; check width(),height() | 592 | //FIXME: could make a routine here; check width(),height() |
592 | assert(ev->size().width() == width()); | 593 | assert(ev->size().width() == width()); |
593 | assert(ev->size().height() == height()); | 594 | assert(ev->size().height() == height()); |
594 | 595 | ||
595 | propagateSize(); | 596 | propagateSize(); |
596 | } | 597 | } |
597 | 598 | ||
598 | void TEWidget::propagateSize() | 599 | void TEWidget::propagateSize() |
599 | { | 600 | { |
600 | ca* oldimg = image; | 601 | ca* oldimg = image; |
601 | int oldlin = lines; | 602 | int oldlin = lines; |
602 | int oldcol = columns; | 603 | int oldcol = columns; |
603 | makeImage(); | 604 | makeImage(); |
604 | // we copy the old image to reduce flicker | 605 | // we copy the old image to reduce flicker |
605 | int lins = QMIN(oldlin,lines); | 606 | int lins = QMIN(oldlin,lines); |
606 | int cols = QMIN(oldcol,columns); | 607 | int cols = QMIN(oldcol,columns); |
607 | if (oldimg) | 608 | if (oldimg) |
608 | { | 609 | { |
609 | for (int lin = 0; lin < lins; lin++) | 610 | for (int lin = 0; lin < lins; lin++) |
610 | memcpy((void*)&image[columns*lin], | 611 | memcpy((void*)&image[columns*lin], |
611 | (void*)&oldimg[oldcol*lin],cols*sizeof(ca)); | 612 | (void*)&oldimg[oldcol*lin],cols*sizeof(ca)); |
612 | free(oldimg); //FIXME: try new,delete | 613 | free(oldimg); //FIXME: try new,delete |
613 | } | 614 | } |
614 | else | 615 | else |
615 | clearImage(); | 616 | clearImage(); |
616 | 617 | ||
617 | //NOTE: control flows from the back through the chest right into the eye. | 618 | //NOTE: control flows from the back through the chest right into the eye. |
618 | // `emu' will call back via `setImage'. | 619 | // `emu' will call back via `setImage'. |
619 | 620 | ||
620 | resizing = TRUE; | 621 | resizing = TRUE; |
621 | emit changedImageSizeSignal(lines, columns); // expose resizeEvent | 622 | emit changedImageSizeSignal(lines, columns); // expose resizeEvent |
622 | resizing = FALSE; | 623 | resizing = FALSE; |
623 | } | 624 | } |
624 | 625 | ||
625 | /* ------------------------------------------------------------------------- */ | 626 | /* ------------------------------------------------------------------------- */ |
626 | /* */ | 627 | /* */ |
627 | /* Scrollbar */ | 628 | /* Scrollbar */ |
628 | /* */ | 629 | /* */ |
629 | /* ------------------------------------------------------------------------- */ | 630 | /* ------------------------------------------------------------------------- */ |
630 | 631 | ||
631 | void TEWidget::scrollChanged(int) | 632 | void TEWidget::scrollChanged(int) |
632 | { | 633 | { |
633 | emit changedHistoryCursor(scrollbar->value()); //expose | 634 | emit changedHistoryCursor(scrollbar->value()); //expose |
634 | } | 635 | } |
635 | 636 | ||
636 | void TEWidget::hscrollChanged(int loc) | 637 | void TEWidget::hscrollChanged(int loc) |
637 | { | 638 | { |
638 | hposition = loc; | 639 | hposition = loc; |
639 | propagateSize(); | 640 | propagateSize(); |
640 | update(); | 641 | update(); |
641 | } | 642 | } |
642 | 643 | ||
643 | void TEWidget::setScroll(int cursor, int slines) | 644 | void TEWidget::setScroll(int cursor, int slines) |
644 | { | 645 | { |
645 | disconnect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollChanged(int))); | 646 | disconnect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollChanged(int))); |
646 | scrollbar->setRange(0,slines); | 647 | scrollbar->setRange(0,slines); |
647 | scrollbar->setSteps(1,lines); | 648 | scrollbar->setSteps(1,lines); |
648 | scrollbar->setValue(cursor); | 649 | scrollbar->setValue(cursor); |
649 | connect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollChanged(int))); | 650 | connect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollChanged(int))); |
650 | } | 651 | } |
651 | 652 | ||
652 | void TEWidget::setScrollbarLocation(int loc) | 653 | void TEWidget::setScrollbarLocation(int loc) |
653 | { | 654 | { |
654 | if (scrollLoc == loc) return; // quickly | 655 | if (scrollLoc == loc) return; // quickly |
655 | scrollLoc = loc; | 656 | scrollLoc = loc; |
656 | propagateSize(); | 657 | propagateSize(); |
657 | update(); | 658 | update(); |
658 | } | 659 | } |
659 | 660 | ||
660 | /* ------------------------------------------------------------------------- */ | 661 | /* ------------------------------------------------------------------------- */ |
661 | /* */ | 662 | /* */ |
662 | /* Mouse */ | 663 | /* Mouse */ |
663 | /* */ | 664 | /* */ |
664 | /* ------------------------------------------------------------------------- */ | 665 | /* ------------------------------------------------------------------------- */ |
665 | 666 | ||
666 | /*! | 667 | /*! |
667 | Three different operations can be performed using the mouse, and the | 668 | Three different operations can be performed using the mouse, and the |
668 | routines in this section serve all of them: | 669 | routines in this section serve all of them: |
669 | 670 | ||
670 | 1) The press/release events are exposed to the application | 671 | 1) The press/release events are exposed to the application |
671 | 2) Marking (press and move left button) and Pasting (press middle button) | 672 | 2) Marking (press and move left button) and Pasting (press middle button) |
672 | 3) The right mouse button is used from the configuration menu | 673 | 3) The right mouse button is used from the configuration menu |
673 | 674 | ||
674 | NOTE: During the marking process we attempt to keep the cursor within | 675 | NOTE: During the marking process we attempt to keep the cursor within |
675 | the bounds of the text as being displayed by setting the mouse position | 676 | the bounds of the text as being displayed by setting the mouse position |
676 | whenever the mouse has left the text area. | 677 | whenever the mouse has left the text area. |
677 | 678 | ||
678 | Two reasons to do so: | 679 | Two reasons to do so: |
679 | 1) QT does not allow the `grabMouse' to confine-to the TEWidget. | 680 | 1) QT does not allow the `grabMouse' to confine-to the TEWidget. |
680 | Thus a `XGrapPointer' would have to be used instead. | 681 | Thus a `XGrapPointer' would have to be used instead. |
681 | 2) Even if so, this would not help too much, since the text area | 682 | 2) Even if so, this would not help too much, since the text area |
682 | of the TEWidget is normally not identical with it's bounds. | 683 | of the TEWidget is normally not identical with it's bounds. |
683 | 684 | ||
684 | The disadvantage of the current handling is, that the mouse can visibly | 685 | The disadvantage of the current handling is, that the mouse can visibly |
685 | leave the bounds of the widget and is then moved back. Because of the | 686 | leave the bounds of the widget and is then moved back. Because of the |
686 | current construction, and the reasons mentioned above, we cannot do better | 687 | current construction, and the reasons mentioned above, we cannot do better |
687 | without changing the overall construction. | 688 | without changing the overall construction. |
688 | */ | 689 | */ |
689 | 690 | ||
690 | /*! | 691 | /*! |
691 | */ | 692 | */ |
692 | 693 | ||
693 | void TEWidget::mousePressEvent(QMouseEvent* ev) | 694 | void TEWidget::mousePressEvent(QMouseEvent* ev) |
694 | { | 695 | { |
695 | //printf("press [%d,%d] %d\n",ev->x()/font_w,ev->y()/font_h,ev->button()); | 696 | //printf("press [%d,%d] %d\n",ev->x()/font_w,ev->y()/font_h,ev->button()); |
696 | if ( !contentsRect().contains(ev->pos()) ) return; | 697 | if ( !contentsRect().contains(ev->pos()) ) return; |
697 | QPoint tL = contentsRect().topLeft(); | 698 | QPoint tL = contentsRect().topLeft(); |
698 | int tLx = tL.x(); | 699 | int tLx = tL.x(); |
699 | int tLy = tL.y(); | 700 | int tLy = tL.y(); |
700 | 701 | ||
701 | word_selection_mode = FALSE; | 702 | word_selection_mode = FALSE; |
702 | 703 | ||
703 | //printf("press top left [%d,%d] by=%d\n",tLx,tLy, bY); | 704 | //printf("press top left [%d,%d] by=%d\n",tLx,tLy, bY); |
704 | if ( ev->button() == LeftButton) | 705 | if ( ev->button() == LeftButton) |
705 | { | 706 | { |
706 | QPoint pos = QPoint((ev->x()-tLx-blX)/font_w,(ev->y()-tLy-bY)/font_h); | 707 | QPoint pos = QPoint((ev->x()-tLx-blX)/font_w,(ev->y()-tLy-bY)/font_h); |
707 | 708 | ||
708 | if ( ev->state() & ControlButton ) preserve_line_breaks = FALSE ; | 709 | if ( ev->state() & ControlButton ) preserve_line_breaks = FALSE ; |
709 | 710 | ||
710 | if (mouse_marks || (ev->state() & ShiftButton)) | 711 | if (mouse_marks || (ev->state() & ShiftButton)) |
711 | { | 712 | { |
712 | emit clearSelectionSignal(); | 713 | emit clearSelectionSignal(); |
713 | iPntSel = pntSel = pos; | 714 | iPntSel = pntSel = pos; |
714 | actSel = 1; // left mouse button pressed but nothing selected yet. | 715 | actSel = 1; // left mouse button pressed but nothing selected yet. |
715 | grabMouse( /*crossCursor*/ ); // handle with care! | 716 | grabMouse( /*crossCursor*/ ); // handle with care! |
716 | } | 717 | } |
717 | else | 718 | else |
718 | { | 719 | { |
719 | emit mouseSignal( 0, pos.x() + 1, pos.y() + 1 ); // left button | 720 | emit mouseSignal( 0, pos.x() + 1, pos.y() + 1 ); // left button |
720 | } | 721 | } |
721 | } | 722 | } |
722 | if ( ev->button() == MidButton ) | 723 | if ( ev->button() == MidButton ) |
723 | { | 724 | { |
724 | emitSelection(); | 725 | emitSelection(); |
725 | } | 726 | } |
726 | if ( ev->button() == RightButton ) // Configure | 727 | if ( ev->button() == RightButton ) // Configure |
727 | { | 728 | { |
728 | emit configureRequest( this, ev->state()&(ShiftButton|ControlButton), ev->x(), ev->y() ); | 729 | emit configureRequest( this, ev->state()&(ShiftButton|ControlButton), ev->x(), ev->y() ); |
729 | } | 730 | } |
730 | } | 731 | } |
731 | 732 | ||
732 | void TEWidget::mouseMoveEvent(QMouseEvent* ev) | 733 | void TEWidget::mouseMoveEvent(QMouseEvent* ev) |
733 | { | 734 | { |
734 | // for auto-hiding the cursor, we need mouseTracking | 735 | // for auto-hiding the cursor, we need mouseTracking |
735 | if (ev->state() == NoButton ) return; | 736 | if (ev->state() == NoButton ) return; |
736 | 737 | ||
737 | if (actSel == 0) return; | 738 | if (actSel == 0) return; |
738 | 739 | ||
739 | // don't extend selection while pasting | 740 | // don't extend selection while pasting |
740 | if (ev->state() & MidButton) return; | 741 | if (ev->state() & MidButton) return; |
741 | 742 | ||
742 | //if ( !contentsRect().contains(ev->pos()) ) return; | 743 | //if ( !contentsRect().contains(ev->pos()) ) return; |
743 | QPoint tL = contentsRect().topLeft(); | 744 | QPoint tL = contentsRect().topLeft(); |
744 | int tLx = tL.x(); | 745 | int tLx = tL.x(); |
745 | int tLy = tL.y(); | 746 | int tLy = tL.y(); |
746 | int scroll = scrollbar->value(); | 747 | int scroll = scrollbar->value(); |
747 | 748 | ||
748 | // we're in the process of moving the mouse with the left button pressed | 749 | // we're in the process of moving the mouse with the left button pressed |
749 | // the mouse cursor will kept catched within the bounds of the text in | 750 | // the mouse cursor will kept catched within the bounds of the text in |
750 | // this widget. | 751 | // this widget. |
751 | 752 | ||
752 | // Adjust position within text area bounds. See FIXME above. | 753 | // Adjust position within text area bounds. See FIXME above. |
753 | QPoint pos = ev->pos(); | 754 | QPoint pos = ev->pos(); |
754 | if ( pos.x() < tLx+blX ) pos.setX( tLx+blX ); | 755 | if ( pos.x() < tLx+blX ) pos.setX( tLx+blX ); |
755 | if ( pos.x() > tLx+blX+columns*font_w-1 ) pos.setX( tLx+blX+columns*font_w ); | 756 | if ( pos.x() > tLx+blX+columns*font_w-1 ) pos.setX( tLx+blX+columns*font_w ); |
756 | if ( pos.y() < tLy+bY ) pos.setY( tLy+bY ); | 757 | if ( pos.y() < tLy+bY ) pos.setY( tLy+bY ); |
757 | if ( pos.y() > tLy+bY+lines*font_h-1 ) pos.setY( tLy+bY+lines*font_h-1 ); | 758 | if ( pos.y() > tLy+bY+lines*font_h-1 ) pos.setY( tLy+bY+lines*font_h-1 ); |
758 | // check if we produce a mouse move event by this | 759 | // check if we produce a mouse move event by this |
759 | if ( pos != ev->pos() ) cursor().setPos(mapToGlobal(pos)); | 760 | if ( pos != ev->pos() ) cursor().setPos(mapToGlobal(pos)); |
760 | 761 | ||
761 | if ( pos.y() == tLy+bY+lines*font_h-1 ) | 762 | if ( pos.y() == tLy+bY+lines*font_h-1 ) |
762 | { | 763 | { |
763 | scrollbar->setValue(scrollbar->value()+yMouseScroll); // scrollforward | 764 | scrollbar->setValue(scrollbar->value()+yMouseScroll); // scrollforward |
764 | } | 765 | } |
765 | if ( pos.y() == tLy+bY ) | 766 | if ( pos.y() == tLy+bY ) |
766 | { | 767 | { |
767 | scrollbar->setValue(scrollbar->value()-yMouseScroll); // scrollback | 768 | scrollbar->setValue(scrollbar->value()-yMouseScroll); // scrollback |
768 | } | 769 | } |
769 | 770 | ||
770 | QPoint here = QPoint((pos.x()-tLx-blX)/font_w,(pos.y()-tLy-bY)/font_h); | 771 | QPoint here = QPoint((pos.x()-tLx-blX)/font_w,(pos.y()-tLy-bY)/font_h); |
771 | QPoint ohere; | 772 | QPoint ohere; |
772 | bool swapping = FALSE; | 773 | bool swapping = FALSE; |
773 | 774 | ||
774 | if ( word_selection_mode ) | 775 | if ( word_selection_mode ) |
775 | { | 776 | { |
776 | // Extend to word boundaries | 777 | // Extend to word boundaries |
777 | int i; | 778 | int i; |
778 | int selClass; | 779 | int selClass; |
779 | 780 | ||
780 | bool left_not_right = ( here.y() < iPntSel.y() || | 781 | bool left_not_right = ( here.y() < iPntSel.y() || |
781 | here.y() == iPntSel.y() && here.x() < iPntSel.x() ); | 782 | here.y() == iPntSel.y() && here.x() < iPntSel.x() ); |
782 | bool old_left_not_right = ( pntSel.y() < iPntSel.y() || | 783 | bool old_left_not_right = ( pntSel.y() < iPntSel.y() || |
783 | pntSel.y() == iPntSel.y() && pntSel.x() < iPntSel.x() ); | 784 | pntSel.y() == iPntSel.y() && pntSel.x() < iPntSel.x() ); |
784 | swapping = left_not_right != old_left_not_right; | 785 | swapping = left_not_right != old_left_not_right; |
785 | 786 | ||
786 | // Find left (left_not_right ? from here : from start) | 787 | // Find left (left_not_right ? from here : from start) |
787 | QPoint left = left_not_right ? here : iPntSel; | 788 | QPoint left = left_not_right ? here : iPntSel; |
788 | i = loc(left.x(),left.y()); | 789 | i = loc(left.x(),left.y()); |
789 | selClass = charClass(image[i].c); | 790 | selClass = charClass(image[i].c); |
790 | while ( left.x() > 0 && charClass(image[i-1].c) == selClass ) | 791 | while ( left.x() > 0 && charClass(image[i-1].c) == selClass ) |
791 | { i--; left.rx()--; } | 792 | { i--; left.rx()--; } |
792 | 793 | ||
793 | // Find left (left_not_right ? from start : from here) | 794 | // Find left (left_not_right ? from start : from here) |
794 | QPoint right = left_not_right ? iPntSel : here; | 795 | QPoint right = left_not_right ? iPntSel : here; |
795 | i = loc(right.x(),right.y()); | 796 | i = loc(right.x(),right.y()); |
796 | selClass = charClass(image[i].c); | 797 | selClass = charClass(image[i].c); |
797 | while ( right.x() < columns-1 && charClass(image[i+1].c) == selClass ) | 798 | while ( right.x() < columns-1 && charClass(image[i+1].c) == selClass ) |
798 | { i++; right.rx()++; } | 799 | { i++; right.rx()++; } |
799 | 800 | ||
800 | // Pick which is start (ohere) and which is extension (here) | 801 | // Pick which is start (ohere) and which is extension (here) |
801 | if ( left_not_right ) | 802 | if ( left_not_right ) |
802 | { | 803 | { |
803 | here = left; ohere = right; | 804 | here = left; ohere = right; |
804 | } | 805 | } |
805 | else | 806 | else |
806 | { | 807 | { |
807 | here = right; ohere = left; | 808 | here = right; ohere = left; |
808 | } | 809 | } |
809 | } | 810 | } |
810 | 811 | ||
811 | if (here == pntSel && scroll == scrollbar->value()) return; // not moved | 812 | if (here == pntSel && scroll == scrollbar->value()) return; // not moved |
812 | 813 | ||
813 | if ( word_selection_mode ) { | 814 | if ( word_selection_mode ) { |
814 | if ( actSel < 2 || swapping ) { | 815 | if ( actSel < 2 || swapping ) { |
815 | emit beginSelectionSignal( ohere.x(), ohere.y() ); | 816 | emit beginSelectionSignal( ohere.x(), ohere.y() ); |
816 | } | 817 | } |
817 | } else if ( actSel < 2 ) { | 818 | } else if ( actSel < 2 ) { |
818 | emit beginSelectionSignal( pntSel.x(), pntSel.y() ); | 819 | emit beginSelectionSignal( pntSel.x(), pntSel.y() ); |
819 | } | 820 | } |
820 | 821 | ||
821 | actSel = 2; // within selection | 822 | actSel = 2; // within selection |
822 | pntSel = here; | 823 | pntSel = here; |
823 | emit extendSelectionSignal( here.x(), here.y() ); | 824 | emit extendSelectionSignal( here.x(), here.y() ); |
824 | } | 825 | } |
825 | 826 | ||
826 | void TEWidget::mouseReleaseEvent(QMouseEvent* ev) | 827 | void TEWidget::mouseReleaseEvent(QMouseEvent* ev) |
827 | { | 828 | { |
828 | //printf("release [%d,%d] %d\n",ev->x()/font_w,ev->y()/font_h,ev->button()); | 829 | //printf("release [%d,%d] %d\n",ev->x()/font_w,ev->y()/font_h,ev->button()); |
829 | if ( ev->button() == LeftButton) | 830 | if ( ev->button() == LeftButton) |
830 | { | 831 | { |
831 | if ( actSel > 1 ) emit endSelectionSignal(preserve_line_breaks); | 832 | if ( actSel > 1 ) emit endSelectionSignal(preserve_line_breaks); |
832 | preserve_line_breaks = TRUE; | 833 | preserve_line_breaks = TRUE; |
833 | actSel = 0; | 834 | actSel = 0; |
834 | 835 | ||
835 | //FIXME: emits a release event even if the mouse is | 836 | //FIXME: emits a release event even if the mouse is |
836 | // outside the range. The procedure used in `mouseMoveEvent' | 837 | // outside the range. The procedure used in `mouseMoveEvent' |
837 | // applies here, too. | 838 | // applies here, too. |
838 | 839 | ||
839 | QPoint tL = contentsRect().topLeft(); | 840 | QPoint tL = contentsRect().topLeft(); |
840 | int tLx = tL.x(); | 841 | int tLx = tL.x(); |
841 | int tLy = tL.y(); | 842 | int tLy = tL.y(); |
842 | 843 | ||
843 | if (!mouse_marks && !(ev->state() & ShiftButton)) | 844 | if (!mouse_marks && !(ev->state() & ShiftButton)) |
844 | emit mouseSignal( 3, // release | 845 | emit mouseSignal( 3, // release |
845 | (ev->x()-tLx-blX)/font_w + 1, | 846 | (ev->x()-tLx-blX)/font_w + 1, |
846 | (ev->y()-tLy-bY)/font_h + 1 ); | 847 | (ev->y()-tLy-bY)/font_h + 1 ); |
847 | releaseMouse(); | 848 | releaseMouse(); |
848 | } | 849 | } |
849 | } | 850 | } |
850 | 851 | ||
851 | void TEWidget::mouseDoubleClickEvent(QMouseEvent* ev) | 852 | void TEWidget::mouseDoubleClickEvent(QMouseEvent* ev) |
852 | { | 853 | { |
853 | if ( ev->button() != LeftButton) return; | 854 | if ( ev->button() != LeftButton) return; |
854 | 855 | ||
855 | QPoint tL = contentsRect().topLeft(); | 856 | QPoint tL = contentsRect().topLeft(); |
856 | int tLx = tL.x(); | 857 | int tLx = tL.x(); |
857 | int tLy = tL.y(); | 858 | int tLy = tL.y(); |
858 | QPoint pos = QPoint((ev->x()-tLx-blX)/font_w,(ev->y()-tLy-bY)/font_h); | 859 | QPoint pos = QPoint((ev->x()-tLx-blX)/font_w,(ev->y()-tLy-bY)/font_h); |
859 | 860 | ||
860 | // pass on double click as two clicks. | 861 | // pass on double click as two clicks. |
861 | if (!mouse_marks && !(ev->state() & ShiftButton)) | 862 | if (!mouse_marks && !(ev->state() & ShiftButton)) |
862 | { | 863 | { |
863 | emit mouseSignal( 0, pos.x()+1, pos.y()+1 ); // left button | 864 | emit mouseSignal( 0, pos.x()+1, pos.y()+1 ); // left button |
864 | emit mouseSignal( 3, pos.x()+1, pos.y()+1 ); // release | 865 | emit mouseSignal( 3, pos.x()+1, pos.y()+1 ); // release |
865 | emit mouseSignal( 0, pos.x()+1, pos.y()+1 ); // left button | 866 | emit mouseSignal( 0, pos.x()+1, pos.y()+1 ); // left button |
866 | return; | 867 | return; |
867 | } | 868 | } |
868 | 869 | ||
869 | 870 | ||