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
409409}
410410
411411void GMenu2X::about() {
412    vector<string> text;
413    string line;
412    string str, line;
414413    string fn(GMENU2X_SYSTEM_DIR);
415414    string build_date("Build date: ");
416415    fn.append("/about.txt");
...... 
418417
419418    ifstream inf(fn.c_str(), ios_base::in);
420419
421    while(getline(inf, line, '\n'))
422        text.push_back(line);
420    while(getline(inf, line, '\n')) {
421        str.append(line).append("\n");
422    }
423423    inf.close();
424424
425    TextDialog td(this, "GMenu2X", build_date, "icons/about.png", &text);
425    TextDialog td(this, "GMenu2X", build_date, "icons/about.png", str);
426426    td.exec();
427427}
428428
...... 
431431    if (fileExists(logfile)) {
432432        ifstream inf(logfile.c_str(), ios_base::in);
433433        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            }
439438            inf.close();
440439
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);
442441            td.exec();
443442
444443            MessageBox mb(this, tr["Do you want to delete the log file?"], "icons/ebook.png");
src/linkapp.cpp
375375#ifdef HAVE_LIBOPK
376376    if (isOPK) {
377377        vector<string> readme;
378        char *token, *ptr;
378        char *ptr;
379379        struct OPK *opk;
380380        int err;
381381        void *buf;
...... 
395395        opk_close(opk);
396396
397397        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);
409399        free(buf);
410400
411401        if (manual.substr(manual.size()-8,8)==".man.txt") {
412            TextManualDialog tmd(gmenu2x, getTitle(), getIconPath(), &readme);
402            TextManualDialog tmd(gmenu2x, getTitle(), getIconPath(), str);
413403            tmd.exec();
414404        } else {
415            TextDialog td(gmenu2x, getTitle(), "ReadMe", getIconPath(), &readme);
405            TextDialog td(gmenu2x, getTitle(), "ReadMe", getIconPath(), str);
416406            td.exec();
417407        }
418408        return;
...... 
501491
502492    // Txt manuals
503493    if (manual.substr(manual.size()-8,8)==".man.txt") {
504        vector<string> txtman;
505
506        string line;
494        string str, line;
507495        ifstream infile(manual.c_str(), ios_base::in);
508496        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            }
510500            infile.close();
511501
512            TextManualDialog tmd(gmenu2x, getTitle(), getIconPath(), &txtman);
502            TextManualDialog tmd(gmenu2x, getTitle(), getIconPath(), str);
513503            tmd.exec();
514504        }
515505
...... 
517507    }
518508
519509    //Readmes
520    vector<string> readme;
521
522    string line;
510    string str, line;
523511    ifstream infile(manual.c_str(), ios_base::in);
524512    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        }
526516        infile.close();
527517
528        TextDialog td(gmenu2x, getTitle(), "ReadMe", getIconPath(), &readme);
518        TextDialog td(gmenu2x, getTitle(), "ReadMe", getIconPath(), str);
529519        td.exec();
530520    }
531521}
src/textdialog.cpp
2525
2626using namespace std;
2727
28TextDialog::TextDialog(GMenu2X *gmenu2x, const string &title, const string &description, const string &icon, vector<string> *text)
28TextDialog::TextDialog(GMenu2X *gmenu2x, const string &title, const string &description, const string &icon, const string &text)
2929    : Dialog(gmenu2x)
3030{
31    this->text = text;
31    split(this->text, gmenu2x->font->wordWrap(text, (int) gmenu2x->resX - 15), "\n");
3232    this->title = title;
3333    this->description = description;
3434    this->icon = icon;
35    preProcess();
3635}
3736
38void 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
117void TextDialog::drawText(vector<string> *text, unsigned int y,
37void TextDialog::drawText(const vector<string> &text, unsigned int y,
11838        unsigned int firstRow, unsigned int rowsPerPage)
11939{
12040    const int fontHeight = gmenu2x->font->getLineSpacing();
12141
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);
12444        int rowY = y + (i - firstRow) * fontHeight;
12545        if (line == "----") { // horizontal ruler
12646            rowY += fontHeight / 2;
...... 
13151        }
13252    }
13353
134    gmenu2x->drawScrollBar(rowsPerPage, text->size(), firstRow);
54    gmenu2x->drawScrollBar(rowsPerPage, text.size(), firstRow);
13555}
13656
13757void TextDialog::exec() {
...... 
17191                if (firstRow > 0) firstRow--;
17292                break;
17393            case InputManager::DOWN:
174                if (firstRow + rowsPerPage < text->size()) firstRow++;
94                if (firstRow + rowsPerPage < text.size()) firstRow++;
17595                break;
17696            case InputManager::ALTLEFT:
17797                if (firstRow >= rowsPerPage-1) firstRow -= rowsPerPage-1;
17898                else firstRow = 0;
17999                break;
180100            case InputManager::ALTRIGHT:
181                if (firstRow + rowsPerPage*2 -1 < text->size()) {
101                if (firstRow + rowsPerPage*2 -1 < text.size()) {
182102                    firstRow += rowsPerPage-1;
183103                } else {
184                    firstRow = text->size() < rowsPerPage ?
185                        0 : text->size() - rowsPerPage;
104                    firstRow = text.size() < rowsPerPage ?
105                        0 : text.size() - rowsPerPage;
186106                }
187107                break;
188108            case InputManager::SETTINGS:
src/textdialog.h
2828
2929class TextDialog : protected Dialog {
3030protected:
31    std::vector<std::string> *text;
31    std::vector<std::string> text;
3232    std::string title, description, icon;
3333
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,
3635            unsigned int firstRow, unsigned int rowsPerPage);
3736
3837public:
3938    TextDialog(GMenu2X *gmenu2x, const std::string &title,
4039            const std::string &description, const std::string &icon,
41            std::vector<std::string> *text);
40            const std::string &text);
4241    void exec();
4342};
4443
src/textmanualdialog.cpp
2929
3030using namespace std;
3131
32TextManualDialog::TextManualDialog(GMenu2X *gmenu2x, const string &title, const string &icon, vector<string> *text)
32TextManualDialog::TextManualDialog(GMenu2X *gmenu2x, const string &title, const string &icon, const string &text)
3333    : TextDialog(gmenu2x,title,"",icon,text) {
3434
3535    //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));
3838        if (line[0]=='[' && line[line.length()-1]==']') {
3939            ManualPage mp;
4040            mp.title = line.substr(1,line.length()-2);
...... 
4545                mp.title = gmenu2x->tr["Untitled"];
4646                pages.push_back(mp);
4747            }
48            pages[pages.size()-1].text.push_back(text->at(i));
48            pages[pages.size()-1].text.push_back(this->text.at(i));
4949        }
5050    }
5151    if (pages.size()==0) {
...... 
9999    while (!close) {
100100        bg.blit(gmenu2x->s,0,0);
101101        writeSubTitle(pages[page].title);
102        drawText(&pages[page].text, 42 /* TODO */, firstRow, rowsPerPage);
102        drawText(pages[page].text, 42 /* TODO */, firstRow, rowsPerPage);
103103
104104        ss.clear();
105105        ss << page+1;
src/textmanualdialog.h
3737
3838public:
3939    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);
4141    void exec();
4242};
4343

Archive Download the corresponding diff file



interactive