コンソールベースの ToDo ソフトウェアの私的改良版
修订版 | b91a9e95ed44c76b0331474cfabf5fe20b0073f0 (tree) |
---|---|
时间 | 2013-02-04 22:59:46 |
作者 | Takeshi Hamasaki <hmatrjp@user...> |
Commiter | Takeshi Hamasaki |
added multi-width caracter support on editor.cc, editor.h, screen.cc
@@ -116,13 +116,13 @@ void Editor::tab() {} | ||
116 | 116 | void Editor::other() {} |
117 | 117 | |
118 | 118 | void Editor::esc() |
119 | -{ | |
119 | +{ | |
120 | 120 | exit = true; |
121 | 121 | result = NOT_SAVED; |
122 | 122 | } |
123 | 123 | |
124 | 124 | void Editor::enter() |
125 | -{ | |
125 | +{ | |
126 | 126 | exit = true; |
127 | 127 | } |
128 | 128 |
@@ -143,7 +143,7 @@ void LineEditor::updateText() | ||
143 | 143 | |
144 | 144 | window->_move(y, x); |
145 | 145 | window->_addstr(text.substr(offset, cols)); |
146 | - for (unsigned int i = text.length()-offset; i < cols; i++) | |
146 | + for (unsigned int i = text.length()-offset; i < cols; i++) | |
147 | 147 | window->_addch(' '); |
148 | 148 | window->_move(y, x+cursor-offset); |
149 | 149 | window->_refresh(); |
@@ -202,32 +202,51 @@ void LineEditor::other() | ||
202 | 202 | */ |
203 | 203 | void TitleEditor::initialize() |
204 | 204 | { |
205 | - textLines = ((int)text.length()-1) / (int)cols; | |
205 | + if (MB_CUR_MAX == 1) { // only single-width char | |
206 | + textLines = ((int)text.length()-1) / (int)cols; | |
206 | 207 | |
207 | - for (int i = 0; i <= textLines; i++) | |
208 | - { | |
209 | - wstring line(text.substr(i*cols, cols)); | |
210 | - window->_move(y+i, x); | |
211 | - window->_addstr(line); | |
208 | + for (int i = 0; i <= textLines; i++) | |
209 | + { | |
210 | + wstring line(text.substr(i*cols, cols)); | |
211 | + window->_move(y+i, x); | |
212 | + window->_addstr(line); | |
213 | + } | |
214 | + | |
215 | + } else { // wide char may appear. | |
216 | + textLines = displaySplitLines(); | |
212 | 217 | } |
218 | + | |
213 | 219 | window->_move(y+cursorLine(), x+cursorCol()); |
214 | 220 | window->_refresh(); |
215 | 221 | } |
216 | 222 | |
217 | 223 | void TitleEditor::updateText() |
218 | 224 | { |
219 | - if (textLines != ((int)(text.length()-1) / (int)cols)) | |
220 | - { | |
221 | - exit = true; | |
222 | - result = REDRAW; | |
223 | - return; | |
224 | - } | |
225 | + if (MB_CUR_MAX == 1) { | |
226 | + if (textLines != ((int)(text.length()-1) / (int)cols)) | |
227 | + { | |
228 | + exit = true; | |
229 | + result = REDRAW; | |
230 | + return; | |
231 | + } | |
225 | 232 | |
226 | - for (int i = 0; i <= textLines; i++) | |
227 | - { | |
228 | - wstring line(text.substr(i*cols, cols)); | |
229 | - window->_move(y+i, x); | |
230 | - window->_addstr(line); | |
233 | + for (int i = 0; i <= textLines; i++) | |
234 | + { | |
235 | + wstring line(text.substr(i*cols, cols)); | |
236 | + window->_move(y+i, x); | |
237 | + window->_addstr(line); | |
238 | + } | |
239 | + | |
240 | + } else { // wide char may appear. | |
241 | + // try self first, | |
242 | + int lineCt = displaySplitLines(); | |
243 | + | |
244 | + // request redraw if line count differs from initialize. | |
245 | + if (textLines != lineCt) { | |
246 | + exit = true; | |
247 | + result = REDRAW; | |
248 | + return; | |
249 | + } | |
231 | 250 | } |
232 | 251 | |
233 | 252 | for (unsigned int i = ((text.length()-1) % cols); i < cols-1; i++) |
@@ -236,30 +255,203 @@ void TitleEditor::updateText() | ||
236 | 255 | window->_refresh(); |
237 | 256 | } |
238 | 257 | |
258 | +// returns counter of actual line(s) -1: 0 means one line. | |
259 | +int TitleEditor::displaySplitLines() { | |
260 | +// window->_move(y, x); | |
261 | + int linect = 0; // to be used in move(); | |
262 | + | |
263 | + const wchar_t *widestr = text.c_str(); | |
264 | + int wch; // index for widestr[] | |
265 | + char linebuf[cols * MB_CUR_MAX]; // to be used in wctomb() function | |
266 | + char *plinebuf = linebuf; | |
267 | + | |
268 | + unsigned int colCount = 0; // counter of filled line length | |
269 | + | |
270 | + int wstrlen = wcslen(widestr); | |
271 | + for ( wch=0; wch < wstrlen; wch++) { | |
272 | + int convertedCharLen = wctomb(plinebuf ,widestr[wch]); | |
273 | + if (convertedCharLen >= 1) { // TODO: not preventing overflow. | |
274 | + plinebuf += convertedCharLen; | |
275 | + | |
276 | + if ( convertedCharLen > 1) { // wide char | |
277 | + colCount += 2; | |
278 | + } else { // TODO: not processing possible conversion error, | |
279 | + colCount ++; | |
280 | + } | |
281 | + | |
282 | + // reached line end? | |
283 | + if (colCount < cols) { // not yet. | |
284 | + ; | |
285 | + } else if (colCount == cols) { // just end position | |
286 | + *plinebuf = L'\0'; | |
287 | + | |
288 | + window->_move(y+ linect++,x); | |
289 | + window->_addstr(linebuf); | |
290 | + | |
291 | + colCount = 0; | |
292 | + plinebuf = linebuf; | |
293 | + } else { // colCount > cols, thus exceeded | |
294 | + plinebuf -= convertedCharLen; | |
295 | + char tmpChar[MB_CUR_MAX]; | |
296 | + | |
297 | + // temporary keep the last char | |
298 | + for (int i=0; i < convertedCharLen; i++) { | |
299 | + tmpChar[i] = *(plinebuf+i); | |
300 | + } | |
301 | + | |
302 | + *plinebuf = L'\0'; | |
303 | + window->_move(y+ linect++,x); | |
304 | + window->_addstr(linebuf); | |
305 | + | |
306 | + plinebuf = linebuf; | |
307 | + // copy back from temporary buffer | |
308 | + for (int i=0; i < convertedCharLen; i++) { | |
309 | + *plinebuf++ = tmpChar[i]; | |
310 | + } | |
311 | + colCount = 2; | |
312 | + } | |
313 | + } | |
314 | + } | |
315 | + | |
316 | + if (plinebuf != linebuf) { // the last line, if something remains | |
317 | + *plinebuf = L'\0'; | |
318 | + | |
319 | + window->_move(y+ linect++,x); | |
320 | + window->_addstr(linebuf); | |
321 | + } | |
322 | + | |
323 | + return --linect; | |
324 | +} | |
325 | + | |
239 | 326 | void TitleEditor::up() |
240 | 327 | { |
241 | - if (cursorLine() > 0) | |
242 | - cursor -= cols; | |
328 | + if (cursorLine() > 0) { | |
329 | + if (MB_CUR_MAX == 1) { | |
330 | + cursor -= cols; | |
331 | + } else { // wide char may appear. | |
332 | + unsigned int colCount = 0; | |
333 | + int bytesLen; | |
334 | + int colWidth; | |
335 | + char dummyBuf[MB_CUR_MAX]; | |
336 | + while(cursor > 0) { | |
337 | + bytesLen = wctomb( dummyBuf, text.at(--cursor) ); | |
338 | + if (bytesLen > 1) { | |
339 | + colWidth = 2; | |
340 | + } else { | |
341 | + colWidth = 1; | |
342 | + } | |
343 | + if (colCount + colWidth < cols) { | |
344 | + colCount += colWidth; | |
345 | + } else { | |
346 | + break; | |
347 | + } | |
348 | + } | |
349 | + cursor++; | |
350 | + } | |
351 | + } | |
243 | 352 | } |
244 | 353 | |
245 | 354 | void TitleEditor::down() |
246 | 355 | { |
247 | 356 | if ((int)cursorLine() < textLines) |
248 | 357 | { |
249 | - cursor += cols; | |
250 | - if (cursor > (int)text.length()) | |
251 | - cursor = text.length(); | |
358 | + if (MB_CUR_MAX == 1) { | |
359 | + cursor += cols; | |
360 | + if (cursor > (int)text.length()) | |
361 | + cursor = text.length(); | |
362 | + } else { // wide char may appear. | |
363 | + unsigned int colCount = 0; | |
364 | + int bytesLen; | |
365 | + int colWidth; | |
366 | + char dummyBuf[MB_CUR_MAX]; | |
367 | + while(cursor < text.length()) { | |
368 | + bytesLen = wctomb( dummyBuf, text.at(cursor) ); | |
369 | + if (bytesLen > 1) { | |
370 | + colWidth = 2; | |
371 | + } else { | |
372 | + colWidth = 1; | |
373 | + } | |
374 | + if (colCount + colWidth < cols) { | |
375 | + colCount += colWidth; | |
376 | + cursor++; | |
377 | + } else { | |
378 | + break; | |
379 | + } | |
380 | + } | |
381 | + } | |
252 | 382 | } |
253 | 383 | } |
254 | 384 | |
255 | 385 | unsigned int TitleEditor::cursorLine() |
256 | 386 | { |
257 | - return cursor / cols; | |
387 | + unsigned int retLine = 0; | |
388 | + | |
389 | + if (MB_CUR_MAX == 1) { | |
390 | + | |
391 | + retLine = cursor / cols; | |
392 | + | |
393 | + } else { // wide char may appear. | |
394 | + | |
395 | + const wchar_t *widestr = text.c_str(); | |
396 | + unsigned int colCount = 0; | |
397 | + int bytesLen; | |
398 | + char dummyBuf[MB_CUR_MAX]; | |
399 | + for (int i =0; i < cursor; i++) { | |
400 | + bytesLen = wctomb(dummyBuf , widestr[i]); | |
401 | + if ( bytesLen > 1) { | |
402 | + colCount += 2; | |
403 | + } else { // TODO: may be conversion error, | |
404 | + colCount ++; | |
405 | + } | |
406 | + | |
407 | + // reached line end? | |
408 | + if (colCount < cols) { // not yet. | |
409 | + ; | |
410 | + } else if (colCount == cols) { // just end position | |
411 | + retLine++; | |
412 | + colCount = 0; | |
413 | + } else { // colCount > cols, thus exceeded | |
414 | + retLine++; | |
415 | + colCount = 2; | |
416 | + } | |
417 | + } | |
418 | + } | |
419 | + | |
420 | + return retLine; | |
258 | 421 | } |
259 | 422 | |
260 | 423 | unsigned int TitleEditor::cursorCol() |
261 | 424 | { |
262 | - return cursor % cols; | |
425 | + unsigned int retCol = 0; | |
426 | + | |
427 | + if (MB_CUR_MAX == 1) { | |
428 | + retCol = cursor % cols; | |
429 | + | |
430 | + } else { // wide char may appear. | |
431 | + | |
432 | + const wchar_t *widestr = text.c_str(); | |
433 | + int bytesLen; | |
434 | + char dummyBuf[MB_CUR_MAX]; | |
435 | + for (int i =0; i < cursor; i++) { | |
436 | + bytesLen = wctomb(dummyBuf , widestr[i]); | |
437 | + if ( bytesLen > 1) { | |
438 | + retCol += 2; | |
439 | + } else { // TODO: may be conversion error, | |
440 | + retCol ++; | |
441 | + } | |
442 | + | |
443 | + // reached line end? | |
444 | + if (retCol < cols) { // not yet. | |
445 | + ; | |
446 | + } else if (retCol == cols) { // just end position | |
447 | + retCol = 0; | |
448 | + } else { // retCol > cols, thus exceeded | |
449 | + retCol = 2; | |
450 | + } | |
451 | + } | |
452 | + } | |
453 | + | |
454 | + return retCol; | |
263 | 455 | } |
264 | 456 | |
265 | 457 |
@@ -298,7 +490,7 @@ void CategoryEditor::tab() /* do completion */ | ||
298 | 490 | (cat == *search)) |
299 | 491 | { |
300 | 492 | search++; |
301 | - if ((search != categories.end()) && | |
493 | + if ((search != categories.end()) && | |
302 | 494 | (!cmp(i, *search))) |
303 | 495 | { |
304 | 496 | text = pre + *search + pos; |
@@ -315,11 +507,11 @@ void CategoryEditor::tab() /* do completion */ | ||
315 | 507 | else |
316 | 508 | { |
317 | 509 | length = j-i; |
318 | - for (search = categories.begin(); | |
319 | - (search != categories.end()) && | |
320 | - (cmp(i, *search)); | |
510 | + for (search = categories.begin(); | |
511 | + (search != categories.end()) && | |
512 | + (cmp(i, *search)); | |
321 | 513 | search++); |
322 | - if ((search != categories.end()) && | |
514 | + if ((search != categories.end()) && | |
323 | 515 | (!cmp(i, *search))) |
324 | 516 | { |
325 | 517 | text = pre + *search + pos; |
@@ -383,7 +575,7 @@ void HistoryEditor::backspace() | ||
383 | 575 | */ |
384 | 576 | void CmdEditor::initialize() |
385 | 577 | { |
386 | - /* initialize if is new command, | |
578 | + /* initialize if is new command, | |
387 | 579 | * in other case we expect it to be already initialized */ |
388 | 580 | if (text == L"") |
389 | 581 | { |
@@ -412,7 +604,7 @@ void CmdEditor::tab() /* do completion */ | ||
412 | 604 | { |
413 | 605 | params.push_back(text.substr(begin, end-begin)); |
414 | 606 | } |
415 | - if (wstring::npos != end) | |
607 | + if (wstring::npos != end) | |
416 | 608 | { |
417 | 609 | params.push_back(text.substr(begin, end-begin)); |
418 | 610 | rest_params = text.substr(end+1); |
@@ -456,7 +648,7 @@ void CmdEditor::command_completion(wstring& com) | ||
456 | 648 | (com == com_search->first)) |
457 | 649 | { |
458 | 650 | com_search++; |
459 | - if ((com_search != commands.end()) && | |
651 | + if ((com_search != commands.end()) && | |
460 | 652 | (!cmp(com_search->first))) |
461 | 653 | { |
462 | 654 | com = com_search->first; |
@@ -474,11 +666,11 @@ void CmdEditor::command_completion(wstring& com) | ||
474 | 666 | { |
475 | 667 | length = com.length(); |
476 | 668 | //TODO: try upper_bound |
477 | - for (com_search = commands.begin(); | |
478 | - (com_search != commands.end()) && | |
479 | - (cmp(com_search->first)); | |
669 | + for (com_search = commands.begin(); | |
670 | + (com_search != commands.end()) && | |
671 | + (cmp(com_search->first)); | |
480 | 672 | com_search++); |
481 | - if ((com_search != commands.end()) && | |
673 | + if ((com_search != commands.end()) && | |
482 | 674 | (!cmp(com_search->first))) |
483 | 675 | { |
484 | 676 | com = com_search->first; |
@@ -498,7 +690,7 @@ void CmdEditor::category_completion(wstring& cat, int num_param) | ||
498 | 690 | (cat == *search)) |
499 | 691 | { |
500 | 692 | search++; |
501 | - if ((search != categories.end()) && | |
693 | + if ((search != categories.end()) && | |
502 | 694 | (!param_cmp(*search))) |
503 | 695 | { |
504 | 696 | cat = *search; |
@@ -513,11 +705,11 @@ void CmdEditor::category_completion(wstring& cat, int num_param) | ||
513 | 705 | else |
514 | 706 | { |
515 | 707 | length = cat.length(); |
516 | - for (search = categories.begin(); | |
517 | - (search != categories.end()) && | |
518 | - (param_cmp(*search)); | |
708 | + for (search = categories.begin(); | |
709 | + (search != categories.end()) && | |
710 | + (param_cmp(*search)); | |
519 | 711 | search++); |
520 | - if ((search != categories.end()) && | |
712 | + if ((search != categories.end()) && | |
521 | 713 | (!param_cmp(*search))) |
522 | 714 | { |
523 | 715 | cat = *search; |
@@ -89,6 +89,7 @@ protected: | ||
89 | 89 | int textLines; |
90 | 90 | |
91 | 91 | void initialize(); |
92 | + int displaySplitLines(); | |
92 | 93 | void updateText(); |
93 | 94 | void up(); |
94 | 95 | void down(); |
@@ -343,13 +343,81 @@ void Screen::drawTitle(int line, int depth, wstring& title, int startLine) | ||
343 | 343 | { |
344 | 344 | int lines, cols; |
345 | 345 | wtree->_getmaxyx(lines, cols); |
346 | + unsigned int lenTitleLine = cols-startTitle(depth); | |
346 | 347 | |
347 | - for (unsigned int i = startLine; i <= (title.length() / (cols-startTitle(depth))); i++) | |
348 | - { | |
349 | - if ((int)(line + i) >= lines) break; | |
350 | - unsigned int lenTitleLine = cols-startTitle(depth); | |
351 | - wstring titleLine = title.substr(i*lenTitleLine,lenTitleLine); | |
352 | - wtree->_addstr(line + i, startTitle(depth), titleLine); | |
348 | + if (MB_CUR_MAX == 1) { | |
349 | + for (unsigned int i = startLine; i <= (title.length() / lenTitleLine); i++) | |
350 | + { | |
351 | + if ((int)(line + i) >= lines) break; | |
352 | + wstring titleLine = title.substr(i*lenTitleLine,lenTitleLine); | |
353 | + wtree->_addstr(line + i, startTitle(depth), titleLine); | |
354 | + } | |
355 | + | |
356 | + } else { // wide char may appear. | |
357 | + | |
358 | + // for passed title string | |
359 | + const wchar_t *titleStr = title.c_str(); | |
360 | + int wstrlen = wcslen(titleStr); | |
361 | + int titleIndex=0; | |
362 | + | |
363 | + // for a line to be composed | |
364 | + char linebuf[lenTitleLine * MB_CUR_MAX]; | |
365 | + char *plinebuf = linebuf; | |
366 | + | |
367 | + unsigned int colCount = 0; // counter of filled line length | |
368 | + int linect = 0; // to be used in move(); | |
369 | + | |
370 | + for (titleIndex = 0; titleIndex < wstrlen; titleIndex++) { | |
371 | + if ((int)(line + linect) >= lines) break; | |
372 | + | |
373 | + int convertedCharLen = wctomb(plinebuf ,titleStr[titleIndex]); | |
374 | + if (convertedCharLen >= 1) { // TODO: not processing conversion error. | |
375 | + plinebuf += convertedCharLen; | |
376 | + | |
377 | + if ( convertedCharLen > 1) { // wide char | |
378 | + colCount += 2; | |
379 | + } else { // TODO: not processing possible conversion error, | |
380 | + colCount ++; | |
381 | + } | |
382 | + | |
383 | + // reached line end? | |
384 | + if (colCount < lenTitleLine) { // not yet. | |
385 | + ; | |
386 | + } else if (colCount == lenTitleLine) { // just end position | |
387 | + *plinebuf = L'\0'; | |
388 | + wtree->_addstr(line + linect, startTitle(depth), linebuf); | |
389 | + plinebuf = linebuf; | |
390 | + linect++; | |
391 | + colCount = 0; | |
392 | + } else { // colCount > colToBeUsed, thus exceeded | |
393 | + plinebuf -= convertedCharLen; | |
394 | + char tmpChar[MB_CUR_MAX]; | |
395 | + | |
396 | + // temporary keep the last char | |
397 | + for (int i=0; i < convertedCharLen; i++) { | |
398 | + tmpChar[i] = *(plinebuf+i); | |
399 | + } | |
400 | + | |
401 | + *plinebuf = L'\0'; | |
402 | + wtree->_addstr(line + linect, startTitle(depth), linebuf); | |
403 | + | |
404 | + plinebuf = linebuf; | |
405 | + linect++; | |
406 | + | |
407 | + // copy back from temporary buffer | |
408 | + for (int i=0; i < convertedCharLen; i++) { | |
409 | + *plinebuf++ = tmpChar[i]; | |
410 | + } | |
411 | + colCount = 2; | |
412 | + } | |
413 | + | |
414 | + } | |
415 | + } | |
416 | + if (plinebuf != linebuf) { // the last line, if something remains | |
417 | + *plinebuf = L'\0'; | |
418 | + | |
419 | + wtree->_addstr(line + linect, startTitle(depth), linebuf); | |
420 | + } | |
353 | 421 | } |
354 | 422 | } |
355 | 423 |
@@ -760,10 +828,61 @@ int Screen::treeLines() | ||
760 | 828 | int Screen::taskLines(int depth, ToDo &t) |
761 | 829 | { |
762 | 830 | int titleLength = t.getTitle().length(); |
763 | - int titleLines = (titleLength / (coor.coor[WTREE].cols-startTitle(depth))) + 1; | |
831 | + unsigned int colToBeUsed = coor.coor[WTREE].cols-startTitle(depth); | |
832 | + int titleLines; | |
833 | + | |
834 | + if (MB_CUR_MAX == 1) { | |
835 | + titleLines = (titleLength / colToBeUsed) + 1; | |
836 | + | |
837 | + if (titleLength && !(titleLength % colToBeUsed)) | |
838 | + titleLines -= 1; | |
839 | + | |
840 | + } else { // wide char may appear. | |
841 | + | |
842 | + // for passed title string | |
843 | + const wchar_t *titleStr = t.getTitle().c_str(); | |
844 | + int wstrlen = wcslen(titleStr); | |
845 | + int titleIndex=0; | |
846 | + | |
847 | + // for a line to be composed | |
848 | + char linebuf[colToBeUsed * MB_CUR_MAX]; | |
849 | + char *plinebuf = linebuf; | |
850 | + | |
851 | + unsigned int colCount = 0; // counter of filled line length | |
852 | + titleLines = 1; | |
853 | + | |
854 | + for (titleIndex = 0; titleIndex < wstrlen; titleIndex++) { | |
855 | + | |
856 | + int convertedCharLen = wctomb(plinebuf ,titleStr[titleIndex]); | |
857 | + if (convertedCharLen >= 1) { // TODO: not processing conversion error. | |
858 | + plinebuf += convertedCharLen; | |
859 | + | |
860 | + if ( convertedCharLen > 1) { // wide char | |
861 | + colCount += 2; | |
862 | + } else { // TODO: not processing possible conversion error, | |
863 | + colCount ++; | |
864 | + } | |
865 | + | |
866 | + // reached line end? | |
867 | + if (colCount < colToBeUsed) { // not yet. | |
868 | + ; | |
869 | + } else if (colCount == colToBeUsed) { // just end position | |
870 | + titleLines++; | |
871 | + colCount = 0; | |
872 | + plinebuf = linebuf; | |
873 | + } else { // colCount > colToBeUsed, thus exceeded | |
874 | + titleLines++; | |
875 | + colCount = 2; | |
876 | + plinebuf = linebuf + convertedCharLen; // we don't need the actual content here. | |
877 | + } | |
878 | + } | |
879 | + } | |
880 | + if (titleLength && (plinebuf == linebuf)) { // at the last line, if nothing remains | |
881 | + titleLines--; // when a child is just created, its titleLength is 0 | |
882 | + } // still it has occupy a line. | |
883 | + | |
884 | + } | |
764 | 885 | |
765 | - if (titleLength && !(titleLength % (coor.coor[WTREE].cols-startTitle(depth)))) | |
766 | - titleLines -= 1; | |
767 | 886 | return titleLines; |
768 | 887 | } |
769 | 888 |