1 
2 /*************************************************************************
3  * Copyright (C) 2007-2015 Ruben Pollan Bella <meskio@sindominio.net>    *
4  *                                                                       *
5  *  This file is part of TuDu.                                           *
6  *                                                                       *
7  *  TuDu is free software; you can redistribute it and/or modify         *
8  *  it under the terms of the GNU General Public License as published by *
9  *  the Free Software Foundation; version 3 of the License.       *
10  *                                                                       *
11  *  TuDu is distributed in the hope that it will be useful,              *
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of       *
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
14  *  GNU General Public License for more details.                         *
15  *                                                                       *
16  *  You should have received a copy of the GNU General Public License    *
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.*
18  *************************************************************************/
19 
20 #include "text.h"
21 
22 #define cursor_x (cursor_col % cols)
23 #define rows_in_line(line) ((line->length() / cols) + 1)
24 
operator =(const wstring & str)25 Text& Text::operator=(const wstring& str)
26 {
27 	unsigned int i = 0, size;
28 
29 	/* build a list with each line of the text in an entry */
30 	text.clear();
31 	while (i < str.length())
32 	{
33 		for (size = 0; (i+size < str.length()) &&
34 				('\n' != str[i+size]); ++size);
35 		text.push_back(str.substr(i, size));
36 		i += size+1;
37 	}
38 	if (text.empty()) /* if is empty it makes a sigfault at edit */
39 		text.push_back(L"");
40 
41 	/* set up the cursor */
42 	cursor_col = 0; cursor_y = INT_MIN;
43 	cursor_line = text.begin();
44 	offset = text.begin();
45 
46 	return *this;
47 }
48 
operator !=(const wstring & str)49 bool Text::operator!=(const wstring& str)
50 {
51 	return (str != getStr());
52 }
53 
print(Window & win)54 void Text::print(Window& win)
55 {
56 	lines = win._lines();
57 	cols = win._cols();
58 	wstring str = _getStr(offset, lines);
59 
60 	win._erase();
61 	win._addstr(0,0,str);
62 	win._refresh();
63 }
64 
edit(Window & win)65 void Text::edit(Window& win)
66 {
67 	bool resized = false;
68 
69 	/* calculate the cursor_y */
70 	cursor_y = 0;
71 	for (list<wstring>::iterator i = offset; i != cursor_line; ++i)
72 		cursor_y += rows_in_line(i);
73 	cursor_y += cursor_col / cols;
74 
75 	lines = win._lines();
76 	cols = win._cols();
77 	win._move(cursor_y, cursor_x);
78 	curs_set(1);
79 	win._refresh();
80 
81 	/* editor loop */
82 	wint_t key = '\e';
83    	bool isKeyCode = (win._getch(key) == KEY_CODE_YES);
84 	while ('\e' != key) {
85 		if (isKeyCode)
86 		{
87 			switch (key)
88 			{
89 				case KEY_RESIZE:
90 					resized = true;
91 					break;
92 				case KEY_LEFT: left();
93 					break;
94 				case KEY_RIGHT: right();
95 					break;
96 				case KEY_DOWN: down();
97 					break;
98 				case KEY_UP: up();
99 					break;
100 				case KEY_HOME: home();
101 					break;
102 				case KEY_END: end();
103 					break;
104 				case KEY_NPAGE: next_page();
105 					break;
106 				case KEY_PPAGE: prev_page();
107 					break;
108 				case KEY_BACKSPACE: backspace();
109 					break;
110 				case KEY_DC: supr();
111 					break;
112 				case KEY_ENTER: new_line();
113 					break;
114 			}
115 		}
116 		else
117 		{
118 			switch (key)
119 			{
120 				case '\n': new_line();
121 					break;
122 				case '\t': tab();
123 					break;
124 				default:
125 					cursor_line->insert(cursor_col,1,key);
126 					++cursor_col;
127 					if (0 == cursor_x) ++cursor_y;
128 					break;
129 			}
130 		}
131 
132 		/* print the text, place cursor, ... */
133 		wstring str = _getStr(offset, lines);
134 		win._erase();
135 		win._addstr(0,0,str);
136 		win._move(cursor_y, cursor_x);
137 		win._refresh();
138 		isKeyCode = (win._getch(key) == KEY_CODE_YES);
139 	}
140 	curs_set(0);
141 	print(win);
142 	cursor_y = INT_MIN;
143 
144 	if (resized) ungetch(KEY_RESIZE);
145 	return;
146 }
147 
getStr()148 wstring Text::getStr()
149 {
150 	wstring s = L"";
151 
152 	for (list<wstring>::iterator i = text.begin(); i != text.end(); ++i)
153 	{
154 		s += *i;
155 		s += L'\n';
156 	}
157 	return s;
158 }
159 
_getStr(list<wstring>::iterator begin,int length)160 wstring Text::_getStr(list<wstring>::iterator begin, int length)
161 {
162 	int rows = 0;
163 	wstring s = L"";
164 	list<wstring>::iterator i = begin;
165 
166 	if (text.end() == begin) return s;
167 	for (;i != text.end(); ++i)
168 	{
169 		if (length)
170 		{
171 			int rows_line = rows_in_line(i);
172 			if (rows+rows_line > length)
173 			{
174 				for (int row = 0; row < length-rows; ++row)
175 				{
176 					s += i->substr(row*cols, cols);
177 				}
178 				s += '\n';
179 				break;
180 			}
181 			rows += rows_line;
182 		}
183 		s += *i;
184 		s += '\n';
185 		if ((length) && (rows == length)) break;
186 	}
187 	s.erase(s.length()-1); /* del the last '\n' */
188 	return s;
189 }
190 
scroll_up(Window & win)191 void Text::scroll_up(Window& win)
192 {
193 	lines = win._lines();
194 	cols = win._cols();
195 	_scroll_up();
196 	print(win);
197 }
198 
scroll_down(Window & win)199 void Text::scroll_down(Window& win)
200 {
201 	lines = win._lines();
202 	cols = win._cols();
203 	_scroll_down();
204 	print(win);
205 }
206 
_scroll_up()207 bool Text::_scroll_up()
208 {
209 	if (text.begin() != offset)
210 	{
211 		--offset;
212 
213 		/* update cursor_y if is editing */
214 		if (cursor_y != INT_MIN)
215 		{
216 			cursor_y += rows_in_line(offset);
217 			while (cursor_y >= lines)
218 			{
219 				cursor_y -= rows_in_line(cursor_line);
220 				--cursor_line;
221 				if ((int)cursor_line->length() < cursor_col)
222 					cursor_col = cursor_line->length();
223 			}
224 		}
225 		/* if is not in edit mode scroll also the cursor */
226 		else
227 		{
228 				--cursor_line;
229 				if ((int)cursor_line->length() < cursor_col)
230 					cursor_col = cursor_line->length();
231 		}
232 		return true;
233 	}
234 	else
235 		return false;
236 }
237 
_scroll_down()238 bool Text::_scroll_down()
239 {
240 	++offset;
241 	if (text.end() != offset)
242 	{
243 		--offset;
244 		/* update cursor_y if is editing */
245 		if (cursor_y != INT_MIN)
246 		{
247 			cursor_y -= rows_in_line(offset);
248 			if (cursor_y < 0)
249 			{
250 				cursor_y = 0;
251 				++cursor_line;
252 				if (cursor_x < (int)cursor_line->length())
253 					cursor_col = cursor_x;
254 				else
255 					cursor_col = cursor_line->length();
256 			}
257 		}
258 		/* if is not in edit mode scroll also the cursor */
259 		else
260 		{
261 				++cursor_line;
262 				if (cursor_line == text.end())
263 				{
264 					--cursor_line;
265 				}
266 				else
267 				{
268 					if (cursor_x < (int)cursor_line->length())
269 						cursor_col = cursor_x;
270 					else
271 						cursor_col = cursor_line->length();
272 				}
273 		}
274 		++offset;
275 		return true;
276 	}
277 	else
278 	{
279 		--offset;
280 		return false;
281 	}
282 }
283 
left()284 void Text::left()
285 {
286 	if (cursor_col>0)
287 	{
288 		if (0 == cursor_x) --cursor_y;
289 		--cursor_col;
290 	}
291 	else if (cursor_line != text.begin())
292 	{
293 		--cursor_y;
294 		--cursor_line;
295 		cursor_col = cursor_line->length();
296 	}
297 	if (cursor_y < 0) _scroll_up();
298 }
299 
right()300 void Text::right()
301 {
302 	if (cursor_col < (int)cursor_line->length())
303 	{
304 		++cursor_col;
305 		if (0 == cursor_x) ++cursor_y;
306 	}
307 	else if (cursor_line != --text.end())
308 	{
309 		cursor_col = 0;
310 		++cursor_y;
311 		++cursor_line;
312 	}
313 	if (cursor_y >= lines) _scroll_down();
314 }
315 
316 #define last_line() ((int)rows_in_line(cursor_line)-1 == cursor_col / cols)
down()317 void Text::down()
318 {
319 	if (last_line())
320 	{
321 		if (cursor_line != --text.end())
322 		{
323 			++cursor_y;
324 			++cursor_line;
325 			cursor_col = cursor_x;
326 		}
327 	}
328 	else
329 	{
330 		++cursor_y;
331 		cursor_col += cols;
332 	}
333 
334 	if (cursor_col > (int)cursor_line->length())
335 	{
336 		cursor_col = cursor_line->length();
337 	}
338 	if (cursor_y >= lines) _scroll_down();
339 }
340 
up()341 void Text::up()
342 {
343 	if ((cursor_col >= cols) || (cursor_line != text.begin()))
344 	{
345 		--cursor_y;
346 
347 		if (cursor_col < cols)
348 		{
349 			--cursor_line;
350 			if (cursor_x < (int)cursor_line->length() % cols)
351 				cursor_col = cursor_x + (rows_in_line(cursor_line)-1) * cols;
352 			else
353 				cursor_col = cursor_line->length();
354 		}
355 		else
356 		{
357 			cursor_col -= cols;
358 		}
359 
360 		if (cursor_y < 0) _scroll_up();
361 	}
362 }
363 
backspace()364 void Text::backspace()
365 {
366 	if (cursor_col > 0)
367 	{
368 		if (0 == cursor_x) --cursor_y;
369 		if (cursor_y < 0) _scroll_up();
370 		--cursor_col;
371 		cursor_line->erase(cursor_col, 1);
372 	}
373 	else if (cursor_line != text.begin()) /* delete line break */
374 	{
375 		list<wstring>::iterator i = cursor_line;
376 
377 		--cursor_y;
378 		--cursor_line;
379 		cursor_col = cursor_line->length();
380 		if (cursor_y < 0) _scroll_up();
381 		*cursor_line += *i;
382 		text.erase(i);
383 	}
384 }
385 
supr()386 void Text::supr()
387 {
388 	if ((cursor_col == (int)cursor_line->length()) &&  (cursor_line != --text.end()))
389 	{
390 		list<wstring>::iterator i = cursor_line;
391 
392 		++i;
393 		*cursor_line += *i;
394 		text.erase(i);
395 	}
396 	else
397 	{
398 		cursor_line->erase(cursor_col, 1);
399 	}
400 	if (cursor_y >= lines) _scroll_down();
401 }
402 
home()403 void Text::home()
404 {
405 	for (int i = 0; i < cursor_col/cols; --cursor_y, ++i);
406 	cursor_col = 0;
407 	if (cursor_y >= lines) _scroll_down();
408 }
409 
end()410 void Text::end()
411 {
412 	for (int i = 0; i < ((int)cursor_line->length()-cursor_col)/cols;
413 			++cursor_y, ++i);
414 	cursor_col = cursor_line->length();
415 	if (cursor_y >= lines) _scroll_down();
416 }
417 
next_page()418 void Text::next_page()
419 {
420 	int line_count = rows_in_line(offset);
421 
422 	while ((line_count < lines) && (_scroll_down()))
423 	{
424 		line_count += rows_in_line(offset);
425 	}
426 }
427 
prev_page()428 void Text::prev_page()
429 {
430 	int line_count = rows_in_line(offset);
431 
432 	while ((line_count < lines) && (_scroll_up()))
433 	{
434 		line_count += rows_in_line(offset);
435 	}
436 }
437 
new_line()438 void Text::new_line()
439 {
440 	list<wstring>::iterator i = cursor_line;
441 
442 	++i;
443 	text.insert(i, cursor_line->substr(cursor_col));
444 	cursor_line->erase(cursor_col);
445 	++cursor_y;
446 	++cursor_line;
447 	cursor_col = 0;
448 	if (cursor_y >= lines) _scroll_down();
449 }
450 
451 #define TAB_SPACES 4
tab()452 void Text::tab()
453 {
454 	cursor_line->insert(cursor_col,TAB_SPACES,' ');
455 	cursor_col+=TAB_SPACES;
456 	if (TAB_SPACES > cursor_x) ++cursor_y;
457 }
458 
operator <<(wostream & os,Text & t)459 wostream& operator<<(wostream& os, Text& t)
460 {
461 	os << t.getStr();
462 	return os;
463 }
464 
operator >>(wistream & is,Text & t)465 wistream& operator>>(wistream& is, Text& t)
466 {
467 	wstring str;
468 	wchar_t c;
469 
470    	while (is.get(c))
471 		str += c;
472 
473 	t = str;
474 	return is;
475 }
476