1
2 /* Terminality - a portable terminal handling library
3 * Copyright (C) 1998-2002, Emil Mikulic.
4 * This is LGPL - look at COPYING.LIB
5 */
6
7 /* Project: Terminality/GUI
8 * File: textview.cpp
9 * Author: Michal Safranek
10 * Description: Text viewer implementation
11 */
12
13 #include <tn.h>
14 #include <gui.h>
15 #include <keyhndl.h>
16
17 #define ESC "\x01B" /* ESC char for color combinations */
18
19 const char textview_rcsid[] =
20 "$Id: textview.cpp,v 1.2 2002/07/26 01:39:40 darkmoon Exp $";
21
22
23
24 // Initialise textview
textview(int xx,int yy,int ww,int hh)25 textview::textview(int xx, int yy, int ww, int hh){
26 content = NULL;
27 hscroll = scroll = 0;
28 x = xx; y = yy; w = ww; h = hh;
29 FG = LightGray; /* FIXME: Any reasonable default? */
30 BG = COLOR_FORM_BG;
31 fixed_colors = beeps = false;
32 horiz_scroll = true;
33 maxchars = 0;
34 reg_id = register_add(Textview, this);
35 }
36
37 // Initialise textview
textview(int xx,int yy,int ww,int hh,char * cont)38 textview::textview(int xx, int yy, int ww, int hh, char *cont){
39 content = NULL;
40 hscroll = scroll = 0;
41 x = xx; y = yy; w = ww; h = hh;
42 FG = LightGray; /* FIXME: Any reasonable default? */
43 BG = COLOR_FORM_BG;
44 fixed_colors = beeps = false;
45 horiz_scroll = true;
46 maxchars = 0;
47 set_content(cont);
48 reg_id = register_add(Textview, this);
49 }
50
51 // destroy TW
~textview()52 textview::~textview(){
53 int i;
54
55 // Kill old content (if any)
56 if(content){
57 for(i = 0; i < tn_list_size(content); i++){
58 xfree(tn_list_getdata(content, i));
59 }
60 tn_list_free(content);
61 tn_list_kill(content);
62 content = NULL;
63 }
64 register_del(reg_id);
65 }
66
67 // change scheme
change_scheme(void)68 void textview::change_scheme(void){
69 if(fixed_colors) return;
70 /* FIXME: Change FG ?!? */
71 BG = COLOR_FORM_BG;
72 }
73
74 // draw it
draw(void)75 void textview::draw(void){
76 int i, j, count, skip;
77 color c;
78 unsigned char *tmp = NULL;
79
80 setcolor(FG, BG);
81 gotoxy(x,y);
82 // Clean up the space ...
83 for(j = y; j < h + y; j++){
84 for(i = x; i < w + x; i++){
85 gotoxy(i, j);
86 writech(' ');
87 }
88 }
89 count = tn_list_size(content); // How many lines is registered?
90 for(j = 1; j <= h; j++){
91 if(j + scroll > count) break; // We're out of content
92 tmp = (unsigned char*)tn_list_getdata(content, j+scroll-1);
93 // Get content
94 gotoxy(x, y - 1 + j);
95 setcolor(FG, BG);
96 skip = hscroll;
97 for(i = 1; i <= w;){
98 if(*tmp == 0) break; // No chars left
99 if(*tmp == 27){ // Color escape?
100 if(*(tmp + 1) && *(tmp + 2)) {
101 // No end of string? Good ...
102 if(*(tmp + 1) == 'f' ||
103 *(tmp + 1) == 'b') {
104 // Color escape
105 tmp += 2;
106 switch(*tmp){
107 case '0': c = Black; break;
108 case '1': c = Blue; break;
109 case '2': c = Green; break;
110 case '3': c = Cyan; break;
111 case '4': c = Red; break;
112 case '5': c = Magenta; break;
113 case '6': c = Brown; break;
114 case '7': c = LightGray; break;
115 case '8': c = DarkGray; break;
116 case '9': c = LightBlue; break;
117 case 'a': c = LightGreen; break;
118 case 'b': c = LightCyan; break;
119 case 'c': c = LightRed; break;
120 case 'd': c = LightMagenta; break;
121 case 'e': c = Yellow; break;
122 case 'f': c = White; break;
123 case 'n':
124 case '!':
125 if(*(tmp - 1) == 'f'){
126 c = FG;
127 }else{
128 c = BG;
129 }
130 break;
131 default: // Invalid color
132 if(*(tmp - 1) == 'f'){
133 c = LightGray;
134 }else{
135 c = Black;
136 }
137 }
138 if(*(tmp - 1) == 'f'){
139 fgcolor(c);
140 }else{
141 bgcolor(c);
142 }
143 tmp++;
144 continue;
145 }
146 }
147 }
148 if(!skip){
149 if(*tmp < 32 && *tmp != '\t'){
150 writech('.');
151 // FIXME: Why it can't display
152 // chars < 32 ?
153 }else{
154 if(*tmp == '\t'){
155 writech(' ');
156 }else{
157 writech(*tmp);
158 }
159 }
160 i++;
161 }else{
162 skip--;
163 }
164 tmp++;
165 }
166 }
167 }
168
169 // run TW
run(void)170 int textview::run(void){
171 key k;
172 cursor cs = get_cursor();
173
174 set_cursor(none);
175 while(1){
176 draw(); // draw menu
177 update();
178 k = readkey(); // read key
179 // FIXME: This should be new type not Custom
180 k = keyhandler(k, Custom);
181 k = handle_key(k, Custom, this);
182 switch(k){ // handle keypress
183 case KEY_NOTHING:
184 break;
185 case KEY_UP:
186 if(scroll){
187 scroll--;
188 }else{
189 if(beeps) beep();
190 }
191 break;
192 case ' ':
193 case KEY_ENTER:
194 case KEY_DOWN:
195 if(scroll < tn_list_size(content) - h){
196 scroll++;
197 }else{
198 if(beeps) beep();
199 }
200 break;
201 case KEY_LEFT:
202 if(horiz_scroll){
203 if(hscroll){
204 hscroll--;
205 }else{
206 if(beeps) beep();
207 }
208 }else{
209 if(beeps) beep();
210 }
211 break;
212 case KEY_RIGHT:
213 if(horiz_scroll){
214 if(hscroll < maxchars - w){
215 hscroll++;
216 }else{
217 if(beeps) beep();
218 }
219 }else{
220 if(beeps) beep();
221 }
222 break;
223 case KEY_HOME:
224 if(scroll){
225 scroll = 0;
226 }else{
227 if(beeps) beep();
228 }
229 break;
230 case KEY_END:
231 if(scroll < tn_list_size(content) - h){
232 scroll = tn_list_size(content) - h;
233 }else{
234 if(beeps) beep();
235 }
236 break;
237 case KEY_PGUP:
238 if(scroll == 0){
239 if(beeps) beep();
240 }else{
241 if(scroll >= h){
242 scroll -= h;
243 }else{
244 scroll = 0;
245 }
246 }
247 break;
248 case KEY_TAB:
249 case KEY_PGDOWN:
250 if(scroll < tn_list_size(content) - h){
251 if(scroll < tn_list_size(content)-2*h){
252 scroll += h;
253 }else{
254 scroll = tn_list_size(content)
255 - h;
256 }
257 }else{
258 if(beeps) beep();
259 }
260 break;
261 case KEY_ESC:
262 set_cursor(cs);
263 return -1;
264 default:
265 if(beeps) beep();
266 }
267 }
268 }
269
270 // set content of the view
set_content(char * str)271 void textview::set_content(char *str){
272 char *tmp = NULL, *end = NULL, *n = NULL;
273 int i;
274
275 // Kill old content (if any)
276 if(content){
277 for(i = 0; i < tn_list_size(content); i++){
278 xfree(tn_list_getdata(content, i));
279 }
280 tn_list_free(content);
281 tn_list_kill(content);
282 content = NULL;
283 }
284 // Set new
285 if(! *str) return;
286 tmp = str;
287 while(*tmp){
288 if(! (end = strchr(tmp, '\n'))){ // no trailing '\n'
289 end = str + strlen(str);
290 }
291 // what a wonderful reimplementation of strdup using xmalloc ...
292 n = (char *) xmalloc((end - tmp) + 1);
293 memcpy(n, tmp, (end - tmp));
294 *(n + (end - tmp)) = 0;
295 maxchars = (unsigned)maxchars<strlen(n)?strlen(n):maxchars;
296 // add it
297 if(! content)
298 content = tn_list_new(n);
299 else
300 tn_list_add(content, n);
301 // advance ptr
302 tmp = end + 1;
303 if(! *end) break; // no trailing '\n', break
304 }
305 }
306
307 // How many bytes read at once?
308 #define READLINE_CHUNK 100
309
310 // Reads single line from file.
readline(FILE * in)311 static char *readline(FILE *in){
312 char *buf = NULL;
313 long velikost_buf = READLINE_CHUNK, index = 0;
314 char first_time = 1;
315
316 buf = (char *) xmalloc(velikost_buf);
317 while(1){
318 if(fgets(buf+index,READLINE_CHUNK-1,in) == NULL){ /* nothing */
319 if(first_time == 1){ *buf = 0; return buf; } /* EOF */
320 break;
321 }
322 first_time = 0;
323 index = strlen(buf);
324 if(buf[index - 1] == '\n'){ break; }
325 velikost_buf += READLINE_CHUNK;
326 buf = (char*) xrealloc(buf, velikost_buf);
327 }
328 return buf;
329 }
330
331 // Import file ...
import_file(char * fname)332 int textview::import_file(char *fname){
333 FILE *fd;
334 char *ln = NULL, *n = NULL;
335 int i;
336
337 if(! (fd = fopen(fname, "r"))) return 1;
338 // Kill old content (if any)
339 if(content){
340 for(i = 0; i < tn_list_size(content); i++){
341 xfree(tn_list_getdata(content, i));
342 }
343 tn_list_free(content);
344 tn_list_kill(content);
345 content = NULL;
346 }
347 while(*(ln = readline(fd))){
348 // trim trailing '\n'
349 if(strchr(ln, '\n')) *strchr(ln, '\n') = 0;
350 // what a wonderful reimplementation of strdup using xmalloc ...
351 n = (char *) xmalloc(strlen(ln) + 1);
352 memcpy(n, ln, strlen(ln));
353 *(n + strlen(ln)) = 0;
354 maxchars = (unsigned)maxchars<strlen(n)?strlen(n):maxchars;
355 // add it
356 if(! content)
357 content = tn_list_new(n);
358 else
359 tn_list_add(content, n);
360 xfree(ln);
361 }
362 fclose(fd);
363 return 0;
364 }
365
winTextview(int xx,int yy,int ww,int hh,char * tit)366 winTextview::winTextview(int xx, int yy, int ww, int hh, char *tit) :
367 textview(xx + 1, yy + 1, ww - 2, hh - 2){
368 if(tit){
369 title = (char *) xmalloc(strlen(tit) + 2);
370 strcpy(title, tit);
371 }else{
372 title = NULL;
373 }
374 LF = COLOR_FORM_TL;
375 DF = COLOR_FORM_BR;
376 }
377
winTextview(int xx,int yy,int ww,int hh,char * tit,char * cont)378 winTextview::winTextview(int xx, int yy, int ww, int hh, char *tit,
379 char *cont) : textview(xx + 1, yy + 1, ww - 2, hh - 2, cont){
380 if(tit){
381 title = (char *) xmalloc(strlen(tit) + 2);
382 strcpy(title, tit);
383 }else{
384 title = NULL;
385 }
386 LF = COLOR_FORM_TL;
387 DF = COLOR_FORM_BR;
388 }
389
~winTextview()390 winTextview::~winTextview(){
391 if(title)
392 xfree(title);
393 }
394
395 // change scheme
change_scheme(void)396 void winTextview::change_scheme(void){
397 if(fixed_colors) return;
398 /* FIXME: Change FG ?!? */
399 BG = COLOR_FORM_BG;
400 LF = COLOR_FORM_TL;
401 DF = COLOR_FORM_BR;
402 }
403
404
draw()405 void winTextview::draw(){
406 box_3d(x - 1, y - 1, x + w, y + h, BG, LF, DF);
407 if(title){
408 setcolor(DF, BG);
409 gotoxy(x, y - 1);
410 printw(" %s ", title);
411 }
412 textview::draw();
413 }
414
415 #ifdef TEXTVIEW_TEST
416 #include <fcntl.h>
417
main(void)418 int main(void){
419 #ifdef __DJGPP__
420 _fmode = O_BINARY;
421 #endif
422 initcons();
423 clrscr();
424 #ifdef PLAINTEXTVIEW_TEST
425 textview *n = new textview(5, 5, 15, 5);
426 n->FG = White;
427 n->BG = Blue;
428 n->horiz_scroll = true;
429 n->beeps = true;
430 n->set_content("bla123456789012345\nbla2\n1\n2\n3\n4\n5");
431 #if 0 // file import
432 if(n->import_file("blah")){
433 printw("file import failed ... :(\n\r");
434 update();
435 }else{
436 n->run();
437 }
438 #else
439 n->run();
440 #endif
441 delete n;
442 #else
443 winTextview *nn = new winTextview(5, 5, 15, 5, "Pokus");
444 nn->FG = White;
445 nn->BG = Blue;
446 nn->LF = LightCyan;
447 nn->DF = Cyan;
448 nn->horiz_scroll = true;
449 nn->beeps = true;
450 nn->set_content("bla123456789012345\nbla2\n1\n2\n3\n4\n5");
451 nn->run();
452 delete nn;
453 #endif
454 donecons();
455 }
456 #endif
457
458