Date: | 2014-07-24 03:36:01 (9 years 8 months ago) |
---|---|
Author: | Nebuleon Fumika |
Commit: | d0de870180081cc97b4b9a2f77e168e837e37917 |
Message: | Use Font::wordWrap in TextDialog and TextManualDialog The constructors of those classes now accept a string to be wrapped, instead of a vector to be modified with split lines inserted into its middle. Along with this conversion, manuals for applications stored in OPK packages are now transferred into a string without garbage at the end. |
Files: |
src/gmenu2x.cpp (3 diffs) src/linkapp.cpp (4 diffs) src/textdialog.cpp (3 diffs) src/textdialog.h (1 diff) src/textmanualdialog.cpp (3 diffs) src/textmanualdialog.h (1 diff) |
Change Details
src/gmenu2x.cpp | ||
---|---|---|
409 | 409 | } |
410 | 410 | |
411 | 411 | void GMenu2X::about() { |
412 | vector<string> text; | |
413 | string line; | |
412 | string str, line; | |
414 | 413 | string fn(GMENU2X_SYSTEM_DIR); |
415 | 414 | string build_date("Build date: "); |
416 | 415 | fn.append("/about.txt"); |
... | ... | |
418 | 417 | |
419 | 418 | ifstream inf(fn.c_str(), ios_base::in); |
420 | 419 | |
421 | while(getline(inf, line, '\n')) | |
422 | text.push_back(line); | |
420 | while(getline(inf, line, '\n')) { | |
421 | str.append(line).append("\n"); | |
422 | } | |
423 | 423 | inf.close(); |
424 | 424 | |
425 | TextDialog td(this, "GMenu2X", build_date, "icons/about.png", &text); | |
425 | TextDialog td(this, "GMenu2X", build_date, "icons/about.png", str); | |
426 | 426 | td.exec(); |
427 | 427 | } |
428 | 428 | |
... | ... | |
431 | 431 | if (fileExists(logfile)) { |
432 | 432 | ifstream inf(logfile.c_str(), ios_base::in); |
433 | 433 | if (inf.is_open()) { |
434 | vector<string> log; | |
435 | ||
436 | string line; | |
437 | while (getline(inf, line, '\n')) | |
438 | log.push_back(line); | |
434 | string str, line; | |
435 | while (getline(inf, line, '\n')) { | |
436 | str.append(line).append("\n"); | |
437 | } | |
439 | 438 | inf.close(); |
440 | 439 | |
441 | TextDialog td(this, tr["Log Viewer"], tr["Displays last launched program's output"], "icons/ebook.png", &log); | |
440 | TextDialog td(this, tr["Log Viewer"], tr["Displays last launched program's output"], "icons/ebook.png", str); | |
442 | 441 | td.exec(); |
443 | 442 | |
444 | 443 | MessageBox mb(this, tr["Do you want to delete the log file?"], "icons/ebook.png"); |
src/linkapp.cpp | ||
---|---|---|
375 | 375 | #ifdef HAVE_LIBOPK |
376 | 376 | if (isOPK) { |
377 | 377 | vector<string> readme; |
378 | char *token, *ptr; | |
378 | char *ptr; | |
379 | 379 | struct OPK *opk; |
380 | 380 | int err; |
381 | 381 | void *buf; |
... | ... | |
395 | 395 | opk_close(opk); |
396 | 396 | |
397 | 397 | ptr = (char *) buf; |
398 | while((token = strchr(ptr, '\n'))) { | |
399 | *token = '\0'; | |
400 | ||
401 | string str(ptr); | |
402 | readme.push_back(str); | |
403 | ptr = token + 1; | |
404 | } | |
405 | ||
406 | /* Add the last line */ | |
407 | string str(ptr); | |
408 | readme.push_back(str); | |
398 | string str(ptr, len); | |
409 | 399 | free(buf); |
410 | 400 | |
411 | 401 | if (manual.substr(manual.size()-8,8)==".man.txt") { |
412 | TextManualDialog tmd(gmenu2x, getTitle(), getIconPath(), &readme); | |
402 | TextManualDialog tmd(gmenu2x, getTitle(), getIconPath(), str); | |
413 | 403 | tmd.exec(); |
414 | 404 | } else { |
415 | TextDialog td(gmenu2x, getTitle(), "ReadMe", getIconPath(), &readme); | |
405 | TextDialog td(gmenu2x, getTitle(), "ReadMe", getIconPath(), str); | |
416 | 406 | td.exec(); |
417 | 407 | } |
418 | 408 | return; |
... | ... | |
501 | 491 | |
502 | 492 | // Txt manuals |
503 | 493 | if (manual.substr(manual.size()-8,8)==".man.txt") { |
504 | vector<string> txtman; | |
505 | ||
506 | string line; | |
494 | string str, line; | |
507 | 495 | ifstream infile(manual.c_str(), ios_base::in); |
508 | 496 | if (infile.is_open()) { |
509 | while (getline(infile, line, '\n')) txtman.push_back(line); | |
497 | while (getline(infile, line, '\n')) { | |
498 | str.append(line).append("\n"); | |
499 | } | |
510 | 500 | infile.close(); |
511 | 501 | |
512 | TextManualDialog tmd(gmenu2x, getTitle(), getIconPath(), &txtman); | |
502 | TextManualDialog tmd(gmenu2x, getTitle(), getIconPath(), str); | |
513 | 503 | tmd.exec(); |
514 | 504 | } |
515 | 505 | |
... | ... | |
517 | 507 | } |
518 | 508 | |
519 | 509 | //Readmes |
520 | vector<string> readme; | |
521 | ||
522 | string line; | |
510 | string str, line; | |
523 | 511 | ifstream infile(manual.c_str(), ios_base::in); |
524 | 512 | if (infile.is_open()) { |
525 | while (getline(infile, line, '\n')) readme.push_back(line); | |
513 | while (getline(infile, line, '\n')) { | |
514 | str.append(line).append("\n"); | |
515 | } | |
526 | 516 | infile.close(); |
527 | 517 | |
528 | TextDialog td(gmenu2x, getTitle(), "ReadMe", getIconPath(), &readme); | |
518 | TextDialog td(gmenu2x, getTitle(), "ReadMe", getIconPath(), str); | |
529 | 519 | td.exec(); |
530 | 520 | } |
531 | 521 | } |
src/textdialog.cpp | ||
---|---|---|
25 | 25 | |
26 | 26 | using namespace std; |
27 | 27 | |
28 | TextDialog::TextDialog(GMenu2X *gmenu2x, const string &title, const string &description, const string &icon, vector<string> *text) | |
28 | TextDialog::TextDialog(GMenu2X *gmenu2x, const string &title, const string &description, const string &icon, const string &text) | |
29 | 29 | : Dialog(gmenu2x) |
30 | 30 | { |
31 | this->text = text; | |
31 | split(this->text, gmenu2x->font->wordWrap(text, (int) gmenu2x->resX - 15), "\n"); | |
32 | 32 | this->title = title; |
33 | 33 | this->description = description; |
34 | 34 | this->icon = icon; |
35 | preProcess(); | |
36 | 35 | } |
37 | 36 | |
38 | void TextDialog::preProcess() { | |
39 | unsigned i = 0; | |
40 | ||
41 | while (i < text->size()) { | |
42 | /* Clean the end of the string, allowing lines that are indented at | |
43 | * the start to stay as such. */ | |
44 | string line = rtrim(text->at(i)); | |
45 | ||
46 | if (gmenu2x->font->getTextWidth(line) > (int) gmenu2x->resX - 15) { | |
47 | /* At least one full character must fit, in order to advance. */ | |
48 | size_t fits = 1; | |
49 | while (fits < line.length() && !isUTF8Starter(line[fits])) { | |
50 | fits++; | |
51 | } | |
52 | size_t doesntFit = fits; | |
53 | ||
54 | /* This preprocessing finds an upper bound on the number of | |
55 | * bytes of full characters that fit on the screen, 2^n, in | |
56 | * n steps. */ | |
57 | do { | |
58 | fits = doesntFit; /* what didn't fit has been determined to fit by a previous iteration */ | |
59 | doesntFit = min(2 * fits, line.length()); | |
60 | while (doesntFit < line.length() && !isUTF8Starter(line[doesntFit])) { | |
61 | doesntFit++; | |
62 | } | |
63 | } while (doesntFit <= line.length() | |
64 | && gmenu2x->font->getTextWidth(line.substr(0, doesntFit)) <= (int) gmenu2x->resX - 15); | |
65 | ||
66 | /* End this loop when N characters fit but N + 1 don't. */ | |
67 | while (fits + 1 < doesntFit) { | |
68 | size_t guess = fits + (doesntFit - fits) / 2; | |
69 | if (!isUTF8Starter(line[guess])) | |
70 | { | |
71 | size_t oldGuess = guess; | |
72 | /* Adjust the guess to the nearest UTF-8 starter that is | |
73 | * not 'fits' or 'doesntFit'. */ | |
74 | for (size_t offset = 1; offset < (doesntFit - fits) / 2 - 1; offset++) { | |
75 | if (isUTF8Starter(line[guess - offset])) { | |
76 | guess -= offset; | |
77 | break; | |
78 | } else if (isUTF8Starter(line[guess + offset])) { | |
79 | guess += offset; | |
80 | break; | |
81 | } | |
82 | } | |
83 | /* If there's no such character, exit early. */ | |
84 | if (guess == oldGuess) { | |
85 | break; | |
86 | } | |
87 | } | |
88 | if (gmenu2x->font->getTextWidth(line.substr(0, guess)) <= (int) gmenu2x->resX - 15) { | |
89 | fits = guess; | |
90 | } else { | |
91 | doesntFit = guess; | |
92 | } | |
93 | } | |
94 | ||
95 | /* The line shall be split at the last space-separated word that | |
96 | * fully fits, or otherwise at the last character that fits. */ | |
97 | size_t lastSpace = line.find_last_of(" \t\r", fits); | |
98 | if (lastSpace != string::npos) { | |
99 | fits = lastSpace; | |
100 | } | |
101 | ||
102 | /* Insert the rest in a new slot after this line. | |
103 | * TODO (Nebuleon) Don't use a vector for this, because all later | |
104 | * elements are moved, which is inefficient. */ | |
105 | text->insert(text->begin() + i + 1, ltrim(line.substr(fits))); | |
106 | line = rtrim(line.substr(0, fits)); | |
107 | } | |
108 | ||
109 | /* Put the trimmed whole line or the smaller split of the split line | |
110 | * back into the same slot */ | |
111 | text->at(i) = line; | |
112 | ||
113 | i++; | |
114 | } | |
115 | } | |
116 | ||
117 | void TextDialog::drawText(vector<string> *text, unsigned int y, | |
37 | void TextDialog::drawText(const vector<string> &text, unsigned int y, | |
118 | 38 | unsigned int firstRow, unsigned int rowsPerPage) |
119 | 39 | { |
120 | 40 | const int fontHeight = gmenu2x->font->getLineSpacing(); |
121 | 41 | |
122 | for (unsigned i = firstRow; i < firstRow + rowsPerPage && i < text->size(); i++) { | |
123 | const string &line = text->at(i); | |
42 | for (unsigned i = firstRow; i < firstRow + rowsPerPage && i < text.size(); i++) { | |
43 | const string &line = text.at(i); | |
124 | 44 | int rowY = y + (i - firstRow) * fontHeight; |
125 | 45 | if (line == "----") { // horizontal ruler |
126 | 46 | rowY += fontHeight / 2; |
... | ... | |
131 | 51 | } |
132 | 52 | } |
133 | 53 | |
134 | gmenu2x->drawScrollBar(rowsPerPage, text->size(), firstRow); | |
54 | gmenu2x->drawScrollBar(rowsPerPage, text.size(), firstRow); | |
135 | 55 | } |
136 | 56 | |
137 | 57 | void TextDialog::exec() { |
... | ... | |
171 | 91 | if (firstRow > 0) firstRow--; |
172 | 92 | break; |
173 | 93 | case InputManager::DOWN: |
174 | if (firstRow + rowsPerPage < text->size()) firstRow++; | |
94 | if (firstRow + rowsPerPage < text.size()) firstRow++; | |
175 | 95 | break; |
176 | 96 | case InputManager::ALTLEFT: |
177 | 97 | if (firstRow >= rowsPerPage-1) firstRow -= rowsPerPage-1; |
178 | 98 | else firstRow = 0; |
179 | 99 | break; |
180 | 100 | case InputManager::ALTRIGHT: |
181 | if (firstRow + rowsPerPage*2 -1 < text->size()) { | |
101 | if (firstRow + rowsPerPage*2 -1 < text.size()) { | |
182 | 102 | firstRow += rowsPerPage-1; |
183 | 103 | } else { |
184 | firstRow = text->size() < rowsPerPage ? | |
185 | 0 : text->size() - rowsPerPage; | |
104 | firstRow = text.size() < rowsPerPage ? | |
105 | 0 : text.size() - rowsPerPage; | |
186 | 106 | } |
187 | 107 | break; |
188 | 108 | case InputManager::SETTINGS: |
src/textdialog.h | ||
---|---|---|
28 | 28 | |
29 | 29 | class TextDialog : protected Dialog { |
30 | 30 | protected: |
31 | std::vector<std::string> *text; | |
31 | std::vector<std::string> text; | |
32 | 32 | std::string title, description, icon; |
33 | 33 | |
34 | void preProcess(); | |
35 | void drawText(std::vector<std::string> *text, unsigned int y, | |
34 | void drawText(const std::vector<std::string> &text, unsigned int y, | |
36 | 35 | unsigned int firstRow, unsigned int rowsPerPage); |
37 | 36 | |
38 | 37 | public: |
39 | 38 | TextDialog(GMenu2X *gmenu2x, const std::string &title, |
40 | 39 | const std::string &description, const std::string &icon, |
41 | std::vector<std::string> *text); | |
40 | const std::string &text); | |
42 | 41 | void exec(); |
43 | 42 | }; |
44 | 43 |
src/textmanualdialog.cpp | ||
---|---|---|
29 | 29 | |
30 | 30 | using namespace std; |
31 | 31 | |
32 | TextManualDialog::TextManualDialog(GMenu2X *gmenu2x, const string &title, const string &icon, vector<string> *text) | |
32 | TextManualDialog::TextManualDialog(GMenu2X *gmenu2x, const string &title, const string &icon, const string &text) | |
33 | 33 | : TextDialog(gmenu2x,title,"",icon,text) { |
34 | 34 | |
35 | 35 | //split the text in multiple pages |
36 | for (uint i=0; i<text->size(); i++) { | |
37 | string line = trim(text->at(i)); | |
36 | for (uint i=0; i<this->text.size(); i++) { | |
37 | string line = trim(this->text.at(i)); | |
38 | 38 | if (line[0]=='[' && line[line.length()-1]==']') { |
39 | 39 | ManualPage mp; |
40 | 40 | mp.title = line.substr(1,line.length()-2); |
... | ... | |
45 | 45 | mp.title = gmenu2x->tr["Untitled"]; |
46 | 46 | pages.push_back(mp); |
47 | 47 | } |
48 | pages[pages.size()-1].text.push_back(text->at(i)); | |
48 | pages[pages.size()-1].text.push_back(this->text.at(i)); | |
49 | 49 | } |
50 | 50 | } |
51 | 51 | if (pages.size()==0) { |
... | ... | |
99 | 99 | while (!close) { |
100 | 100 | bg.blit(gmenu2x->s,0,0); |
101 | 101 | writeSubTitle(pages[page].title); |
102 | drawText(&pages[page].text, 42 /* TODO */, firstRow, rowsPerPage); | |
102 | drawText(pages[page].text, 42 /* TODO */, firstRow, rowsPerPage); | |
103 | 103 | |
104 | 104 | ss.clear(); |
105 | 105 | ss << page+1; |
src/textmanualdialog.h | ||
---|---|---|
37 | 37 | |
38 | 38 | public: |
39 | 39 | TextManualDialog(GMenu2X *gmenu2x, const std::string &title, |
40 | const std::string &icon, std::vector<std::string> *text); | |
40 | const std::string &icon, const std::string &text); | |
41 | 41 | void exec(); |
42 | 42 | }; |
43 | 43 |
Branches:
install_locations
master
opkrun
packages