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