mirror of
git://projects.qi-hardware.com/gmenu2x.git
synced 2024-11-25 18:05:20 +02:00
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.
This commit is contained in:
parent
0c860e8b90
commit
d0de870180
@ -409,8 +409,7 @@ void GMenu2X::initMenu() {
|
||||
}
|
||||
|
||||
void GMenu2X::about() {
|
||||
vector<string> text;
|
||||
string line;
|
||||
string str, line;
|
||||
string fn(GMENU2X_SYSTEM_DIR);
|
||||
string build_date("Build date: ");
|
||||
fn.append("/about.txt");
|
||||
@ -418,11 +417,12 @@ void GMenu2X::about() {
|
||||
|
||||
ifstream inf(fn.c_str(), ios_base::in);
|
||||
|
||||
while(getline(inf, line, '\n'))
|
||||
text.push_back(line);
|
||||
while(getline(inf, line, '\n')) {
|
||||
str.append(line).append("\n");
|
||||
}
|
||||
inf.close();
|
||||
|
||||
TextDialog td(this, "GMenu2X", build_date, "icons/about.png", &text);
|
||||
TextDialog td(this, "GMenu2X", build_date, "icons/about.png", str);
|
||||
td.exec();
|
||||
}
|
||||
|
||||
@ -431,14 +431,13 @@ void GMenu2X::viewLog() {
|
||||
if (fileExists(logfile)) {
|
||||
ifstream inf(logfile.c_str(), ios_base::in);
|
||||
if (inf.is_open()) {
|
||||
vector<string> log;
|
||||
|
||||
string line;
|
||||
while (getline(inf, line, '\n'))
|
||||
log.push_back(line);
|
||||
string str, line;
|
||||
while (getline(inf, line, '\n')) {
|
||||
str.append(line).append("\n");
|
||||
}
|
||||
inf.close();
|
||||
|
||||
TextDialog td(this, tr["Log Viewer"], tr["Displays last launched program's output"], "icons/ebook.png", &log);
|
||||
TextDialog td(this, tr["Log Viewer"], tr["Displays last launched program's output"], "icons/ebook.png", str);
|
||||
td.exec();
|
||||
|
||||
MessageBox mb(this, tr["Do you want to delete the log file?"], "icons/ebook.png");
|
||||
|
@ -375,7 +375,7 @@ void LinkApp::showManual() {
|
||||
#ifdef HAVE_LIBOPK
|
||||
if (isOPK) {
|
||||
vector<string> readme;
|
||||
char *token, *ptr;
|
||||
char *ptr;
|
||||
struct OPK *opk;
|
||||
int err;
|
||||
void *buf;
|
||||
@ -395,24 +395,14 @@ void LinkApp::showManual() {
|
||||
opk_close(opk);
|
||||
|
||||
ptr = (char *) buf;
|
||||
while((token = strchr(ptr, '\n'))) {
|
||||
*token = '\0';
|
||||
|
||||
string str(ptr);
|
||||
readme.push_back(str);
|
||||
ptr = token + 1;
|
||||
}
|
||||
|
||||
/* Add the last line */
|
||||
string str(ptr);
|
||||
readme.push_back(str);
|
||||
string str(ptr, len);
|
||||
free(buf);
|
||||
|
||||
if (manual.substr(manual.size()-8,8)==".man.txt") {
|
||||
TextManualDialog tmd(gmenu2x, getTitle(), getIconPath(), &readme);
|
||||
TextManualDialog tmd(gmenu2x, getTitle(), getIconPath(), str);
|
||||
tmd.exec();
|
||||
} else {
|
||||
TextDialog td(gmenu2x, getTitle(), "ReadMe", getIconPath(), &readme);
|
||||
TextDialog td(gmenu2x, getTitle(), "ReadMe", getIconPath(), str);
|
||||
td.exec();
|
||||
}
|
||||
return;
|
||||
@ -501,15 +491,15 @@ void LinkApp::showManual() {
|
||||
|
||||
// Txt manuals
|
||||
if (manual.substr(manual.size()-8,8)==".man.txt") {
|
||||
vector<string> txtman;
|
||||
|
||||
string line;
|
||||
string str, line;
|
||||
ifstream infile(manual.c_str(), ios_base::in);
|
||||
if (infile.is_open()) {
|
||||
while (getline(infile, line, '\n')) txtman.push_back(line);
|
||||
while (getline(infile, line, '\n')) {
|
||||
str.append(line).append("\n");
|
||||
}
|
||||
infile.close();
|
||||
|
||||
TextManualDialog tmd(gmenu2x, getTitle(), getIconPath(), &txtman);
|
||||
TextManualDialog tmd(gmenu2x, getTitle(), getIconPath(), str);
|
||||
tmd.exec();
|
||||
}
|
||||
|
||||
@ -517,15 +507,15 @@ void LinkApp::showManual() {
|
||||
}
|
||||
|
||||
//Readmes
|
||||
vector<string> readme;
|
||||
|
||||
string line;
|
||||
string str, line;
|
||||
ifstream infile(manual.c_str(), ios_base::in);
|
||||
if (infile.is_open()) {
|
||||
while (getline(infile, line, '\n')) readme.push_back(line);
|
||||
while (getline(infile, line, '\n')) {
|
||||
str.append(line).append("\n");
|
||||
}
|
||||
infile.close();
|
||||
|
||||
TextDialog td(gmenu2x, getTitle(), "ReadMe", getIconPath(), &readme);
|
||||
TextDialog td(gmenu2x, getTitle(), "ReadMe", getIconPath(), str);
|
||||
td.exec();
|
||||
}
|
||||
}
|
||||
|
@ -25,102 +25,22 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
TextDialog::TextDialog(GMenu2X *gmenu2x, const string &title, const string &description, const string &icon, vector<string> *text)
|
||||
TextDialog::TextDialog(GMenu2X *gmenu2x, const string &title, const string &description, const string &icon, const string &text)
|
||||
: Dialog(gmenu2x)
|
||||
{
|
||||
this->text = text;
|
||||
split(this->text, gmenu2x->font->wordWrap(text, (int) gmenu2x->resX - 15), "\n");
|
||||
this->title = title;
|
||||
this->description = description;
|
||||
this->icon = icon;
|
||||
preProcess();
|
||||
}
|
||||
|
||||
void TextDialog::preProcess() {
|
||||
unsigned i = 0;
|
||||
|
||||
while (i < text->size()) {
|
||||
/* Clean the end of the string, allowing lines that are indented at
|
||||
* the start to stay as such. */
|
||||
string line = rtrim(text->at(i));
|
||||
|
||||
if (gmenu2x->font->getTextWidth(line) > (int) gmenu2x->resX - 15) {
|
||||
/* At least one full character must fit, in order to advance. */
|
||||
size_t fits = 1;
|
||||
while (fits < line.length() && !isUTF8Starter(line[fits])) {
|
||||
fits++;
|
||||
}
|
||||
size_t doesntFit = fits;
|
||||
|
||||
/* This preprocessing finds an upper bound on the number of
|
||||
* bytes of full characters that fit on the screen, 2^n, in
|
||||
* n steps. */
|
||||
do {
|
||||
fits = doesntFit; /* what didn't fit has been determined to fit by a previous iteration */
|
||||
doesntFit = min(2 * fits, line.length());
|
||||
while (doesntFit < line.length() && !isUTF8Starter(line[doesntFit])) {
|
||||
doesntFit++;
|
||||
}
|
||||
} while (doesntFit <= line.length()
|
||||
&& gmenu2x->font->getTextWidth(line.substr(0, doesntFit)) <= (int) gmenu2x->resX - 15);
|
||||
|
||||
/* End this loop when N characters fit but N + 1 don't. */
|
||||
while (fits + 1 < doesntFit) {
|
||||
size_t guess = fits + (doesntFit - fits) / 2;
|
||||
if (!isUTF8Starter(line[guess]))
|
||||
{
|
||||
size_t oldGuess = guess;
|
||||
/* Adjust the guess to the nearest UTF-8 starter that is
|
||||
* not 'fits' or 'doesntFit'. */
|
||||
for (size_t offset = 1; offset < (doesntFit - fits) / 2 - 1; offset++) {
|
||||
if (isUTF8Starter(line[guess - offset])) {
|
||||
guess -= offset;
|
||||
break;
|
||||
} else if (isUTF8Starter(line[guess + offset])) {
|
||||
guess += offset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* If there's no such character, exit early. */
|
||||
if (guess == oldGuess) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (gmenu2x->font->getTextWidth(line.substr(0, guess)) <= (int) gmenu2x->resX - 15) {
|
||||
fits = guess;
|
||||
} else {
|
||||
doesntFit = guess;
|
||||
}
|
||||
}
|
||||
|
||||
/* The line shall be split at the last space-separated word that
|
||||
* fully fits, or otherwise at the last character that fits. */
|
||||
size_t lastSpace = line.find_last_of(" \t\r", fits);
|
||||
if (lastSpace != string::npos) {
|
||||
fits = lastSpace;
|
||||
}
|
||||
|
||||
/* Insert the rest in a new slot after this line.
|
||||
* TODO (Nebuleon) Don't use a vector for this, because all later
|
||||
* elements are moved, which is inefficient. */
|
||||
text->insert(text->begin() + i + 1, ltrim(line.substr(fits)));
|
||||
line = rtrim(line.substr(0, fits));
|
||||
}
|
||||
|
||||
/* Put the trimmed whole line or the smaller split of the split line
|
||||
* back into the same slot */
|
||||
text->at(i) = line;
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void TextDialog::drawText(vector<string> *text, unsigned int y,
|
||||
void TextDialog::drawText(const vector<string> &text, unsigned int y,
|
||||
unsigned int firstRow, unsigned int rowsPerPage)
|
||||
{
|
||||
const int fontHeight = gmenu2x->font->getLineSpacing();
|
||||
|
||||
for (unsigned i = firstRow; i < firstRow + rowsPerPage && i < text->size(); i++) {
|
||||
const string &line = text->at(i);
|
||||
for (unsigned i = firstRow; i < firstRow + rowsPerPage && i < text.size(); i++) {
|
||||
const string &line = text.at(i);
|
||||
int rowY = y + (i - firstRow) * fontHeight;
|
||||
if (line == "----") { // horizontal ruler
|
||||
rowY += fontHeight / 2;
|
||||
@ -131,7 +51,7 @@ void TextDialog::drawText(vector<string> *text, unsigned int y,
|
||||
}
|
||||
}
|
||||
|
||||
gmenu2x->drawScrollBar(rowsPerPage, text->size(), firstRow);
|
||||
gmenu2x->drawScrollBar(rowsPerPage, text.size(), firstRow);
|
||||
}
|
||||
|
||||
void TextDialog::exec() {
|
||||
@ -171,18 +91,18 @@ void TextDialog::exec() {
|
||||
if (firstRow > 0) firstRow--;
|
||||
break;
|
||||
case InputManager::DOWN:
|
||||
if (firstRow + rowsPerPage < text->size()) firstRow++;
|
||||
if (firstRow + rowsPerPage < text.size()) firstRow++;
|
||||
break;
|
||||
case InputManager::ALTLEFT:
|
||||
if (firstRow >= rowsPerPage-1) firstRow -= rowsPerPage-1;
|
||||
else firstRow = 0;
|
||||
break;
|
||||
case InputManager::ALTRIGHT:
|
||||
if (firstRow + rowsPerPage*2 -1 < text->size()) {
|
||||
if (firstRow + rowsPerPage*2 -1 < text.size()) {
|
||||
firstRow += rowsPerPage-1;
|
||||
} else {
|
||||
firstRow = text->size() < rowsPerPage ?
|
||||
0 : text->size() - rowsPerPage;
|
||||
firstRow = text.size() < rowsPerPage ?
|
||||
0 : text.size() - rowsPerPage;
|
||||
}
|
||||
break;
|
||||
case InputManager::SETTINGS:
|
||||
|
@ -28,17 +28,16 @@
|
||||
|
||||
class TextDialog : protected Dialog {
|
||||
protected:
|
||||
std::vector<std::string> *text;
|
||||
std::vector<std::string> text;
|
||||
std::string title, description, icon;
|
||||
|
||||
void preProcess();
|
||||
void drawText(std::vector<std::string> *text, unsigned int y,
|
||||
void drawText(const std::vector<std::string> &text, unsigned int y,
|
||||
unsigned int firstRow, unsigned int rowsPerPage);
|
||||
|
||||
public:
|
||||
TextDialog(GMenu2X *gmenu2x, const std::string &title,
|
||||
const std::string &description, const std::string &icon,
|
||||
std::vector<std::string> *text);
|
||||
const std::string &text);
|
||||
void exec();
|
||||
};
|
||||
|
||||
|
@ -29,12 +29,12 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
TextManualDialog::TextManualDialog(GMenu2X *gmenu2x, const string &title, const string &icon, vector<string> *text)
|
||||
TextManualDialog::TextManualDialog(GMenu2X *gmenu2x, const string &title, const string &icon, const string &text)
|
||||
: TextDialog(gmenu2x,title,"",icon,text) {
|
||||
|
||||
//split the text in multiple pages
|
||||
for (uint i=0; i<text->size(); i++) {
|
||||
string line = trim(text->at(i));
|
||||
for (uint i=0; i<this->text.size(); i++) {
|
||||
string line = trim(this->text.at(i));
|
||||
if (line[0]=='[' && line[line.length()-1]==']') {
|
||||
ManualPage mp;
|
||||
mp.title = line.substr(1,line.length()-2);
|
||||
@ -45,7 +45,7 @@ TextManualDialog::TextManualDialog(GMenu2X *gmenu2x, const string &title, const
|
||||
mp.title = gmenu2x->tr["Untitled"];
|
||||
pages.push_back(mp);
|
||||
}
|
||||
pages[pages.size()-1].text.push_back(text->at(i));
|
||||
pages[pages.size()-1].text.push_back(this->text.at(i));
|
||||
}
|
||||
}
|
||||
if (pages.size()==0) {
|
||||
@ -99,7 +99,7 @@ void TextManualDialog::exec() {
|
||||
while (!close) {
|
||||
bg.blit(gmenu2x->s,0,0);
|
||||
writeSubTitle(pages[page].title);
|
||||
drawText(&pages[page].text, 42 /* TODO */, firstRow, rowsPerPage);
|
||||
drawText(pages[page].text, 42 /* TODO */, firstRow, rowsPerPage);
|
||||
|
||||
ss.clear();
|
||||
ss << page+1;
|
||||
|
@ -37,7 +37,7 @@ private:
|
||||
|
||||
public:
|
||||
TextManualDialog(GMenu2X *gmenu2x, const std::string &title,
|
||||
const std::string &icon, std::vector<std::string> *text);
|
||||
const std::string &icon, const std::string &text);
|
||||
void exec();
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user