author | kergoth <kergoth> | 2002-01-25 22:14:26 (UTC) |
---|---|---|
committer | kergoth <kergoth> | 2002-01-25 22:14:26 (UTC) |
commit | 15318cad33835e4e2dc620d033e43cd930676cdd (patch) (unidiff) | |
tree | c2fa0399a2c47fda8e2cd0092c73a809d17f68eb /core/apps/embeddedkonsole/TEScreen.cpp | |
download | opie-15318cad33835e4e2dc620d033e43cd930676cdd.zip opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.gz opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.bz2 |
Initial revision
Diffstat (limited to 'core/apps/embeddedkonsole/TEScreen.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r-- | core/apps/embeddedkonsole/TEScreen.cpp | 1197 |
1 files changed, 1197 insertions, 0 deletions
diff --git a/core/apps/embeddedkonsole/TEScreen.cpp b/core/apps/embeddedkonsole/TEScreen.cpp new file mode 100644 index 0000000..a3d115d --- a/dev/null +++ b/core/apps/embeddedkonsole/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 | } | ||