mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
I just disabled color restoration, but I am still not understanding why it isn't working Fix #307
790 lines
25 KiB
C++
790 lines
25 KiB
C++
|
|
#include "word_wrap_view.h"
|
|
#include "utility.h"
|
|
#include "tex_parser.h"
|
|
#include <poincare/expression.h>
|
|
#include "../shared/poincare_helpers.h"
|
|
#include <poincare/undefined.h>
|
|
#include "read_book_controller.h"
|
|
|
|
namespace Reader
|
|
{
|
|
|
|
WordWrapTextView::WordWrapTextView(ReadBookController * readBookController) :
|
|
PointerTextView(GlobalPreferences::sharedGlobalPreferences()->font()),
|
|
m_pageOffset(0),
|
|
m_nextPageOffset(0),
|
|
m_length(0),
|
|
m_isRichTextFile(false), // Value isn't important, it will change when the file is loaded
|
|
m_lastPagesOffsetsIndex(0),
|
|
m_textColor(Palette::PrimaryText),
|
|
m_readBookController(readBookController)
|
|
{
|
|
for (int i = 0; i < k_lastOffsetsBufferSize; i++) {
|
|
m_lastPagesOffsets[i] = -1; // -1 Means : no informations
|
|
}
|
|
}
|
|
|
|
void WordWrapTextView::nextPage() {
|
|
if(m_nextPageOffset >= m_length) {
|
|
return;
|
|
}
|
|
m_lastPagesOffsets[m_lastPagesOffsetsIndex] = m_pageOffset;
|
|
m_lastPagesOffsetsIndex = (m_lastPagesOffsetsIndex + 1) % k_lastOffsetsBufferSize;
|
|
m_pageOffset = m_nextPageOffset;
|
|
markRectAsDirty(bounds());
|
|
}
|
|
|
|
void WordWrapTextView::setText(const char* text, int length, bool isRichTextFile) {
|
|
PointerTextView::setText(text);
|
|
m_length = length;
|
|
m_isRichTextFile = isRichTextFile;
|
|
}
|
|
|
|
void WordWrapTextView::previousPage() {
|
|
if(m_pageOffset <= 0) {
|
|
return;
|
|
}
|
|
|
|
/* We check if we have available data in our buffer */
|
|
int offsetToCheck = (m_lastPagesOffsetsIndex + k_lastOffsetsBufferSize - 1) % k_lastOffsetsBufferSize;
|
|
if (m_lastPagesOffsets[offsetToCheck] != -1) {
|
|
m_lastPagesOffsetsIndex = offsetToCheck;
|
|
m_pageOffset = m_lastPagesOffsets[offsetToCheck];
|
|
m_lastPagesOffsets[offsetToCheck] = -1;
|
|
} else if (m_isRichTextFile) {
|
|
richTextPreviousPage();
|
|
} else {
|
|
plainTextPreviousPage();
|
|
}
|
|
|
|
markRectAsDirty(bounds());
|
|
}
|
|
|
|
void WordWrapTextView::richTextPreviousPage() {
|
|
const int charWidth = m_font->glyphSize().width();
|
|
const int charHeight = m_font->glyphSize().height();
|
|
|
|
const char * endOfWord = text() + m_pageOffset - 1;
|
|
if (*endOfWord == '\n') {
|
|
endOfWord --;
|
|
}
|
|
|
|
KDCoordinate baseline = charHeight / 2;
|
|
|
|
KDPoint textBottomEndPosition = KDPoint(m_frame.width() - k_margin, m_frame.height() - k_margin);
|
|
KDCoordinate lineHeight = charHeight;
|
|
|
|
while(endOfWord >= text()) {
|
|
// 1. Skip whitespaces and line jumps
|
|
const char * invisiblesCharJumped = endOfWord; // We use this to update endOfWord only if we don't change page
|
|
bool changePage = false;
|
|
while(invisiblesCharJumped >= text() && (*invisiblesCharJumped == ' ' || *invisiblesCharJumped == '\n')) {
|
|
if(*invisiblesCharJumped == '\n') {
|
|
textBottomEndPosition = KDPoint(m_frame.width() - k_margin, textBottomEndPosition.y() - lineHeight);
|
|
lineHeight = charHeight;
|
|
baseline = charHeight / 2;
|
|
// We check if we must change page
|
|
if (textBottomEndPosition.y() - lineHeight <= k_margin) {
|
|
// We don't let text on a new line or a space
|
|
endOfWord ++;
|
|
changePage = true;
|
|
break;
|
|
}
|
|
} else {
|
|
textBottomEndPosition = KDPoint(textBottomEndPosition.x() - charWidth, textBottomEndPosition.y());
|
|
}
|
|
invisiblesCharJumped--;
|
|
}
|
|
|
|
if (changePage) {
|
|
break;
|
|
}
|
|
endOfWord = invisiblesCharJumped;
|
|
|
|
// 3. If word is a color change
|
|
if (*endOfWord == '%' && *(endOfWord - 1) != '\\') {
|
|
const char * startOfWord = endOfWord - 2;
|
|
while (*startOfWord != '%') {
|
|
startOfWord--;
|
|
}
|
|
|
|
if (updateTextColorBackward(startOfWord)) {
|
|
endOfWord = startOfWord - 1; // Update next endOfWord
|
|
continue;
|
|
} else {
|
|
m_readBookController->throwError();
|
|
return;
|
|
}
|
|
}
|
|
|
|
KDSize textSize = KDSizeZero;
|
|
|
|
// 4. If word is a mathematical expression
|
|
if (*endOfWord == '$' && *(endOfWord - 1) != '\\') {
|
|
// We go to the end of the expression + 1
|
|
const char * expressionStart = --endOfWord;
|
|
while (*expressionStart != '$') {
|
|
if (expressionStart < text()) {
|
|
break; // File isn't rightly formated
|
|
}
|
|
expressionStart --;
|
|
}
|
|
|
|
TexParser parser = TexParser(expressionStart, endOfWord);
|
|
Layout layout = parser.getLayout();
|
|
|
|
KDCoordinate layoutBaseline = layout.baseline();
|
|
|
|
// We check if we must change baseline
|
|
if (layoutBaseline > baseline) {
|
|
baseline = layoutBaseline;
|
|
}
|
|
|
|
KDSize layoutSize = layout.layoutSize();
|
|
textSize = KDSize(layoutSize.width(), layoutSize.height() + baseline - layoutBaseline);
|
|
|
|
endOfWord = expressionStart;
|
|
}
|
|
|
|
// 5. Else it's text
|
|
else {
|
|
// We go to the start of the word
|
|
const char * startOfWord = StartOfPrintableWord(endOfWord, text());
|
|
|
|
textSize = m_font->stringSizeUntil(startOfWord, endOfWord + 1);
|
|
|
|
endOfWord = startOfWord;
|
|
}
|
|
|
|
// 6. We check if we must change line
|
|
if (textBottomEndPosition.x() - textSize.width() <= k_margin) {
|
|
textBottomEndPosition = KDPoint(m_frame.width() - k_margin, textBottomEndPosition.y() - lineHeight);
|
|
lineHeight = 0;
|
|
baseline = charHeight;
|
|
// We will check if we must change page below
|
|
}
|
|
textBottomEndPosition = KDPoint(textBottomEndPosition.x() - textSize.width(), textBottomEndPosition.y());
|
|
|
|
// 7. We update height of the line if needed
|
|
if (textSize.height() > lineHeight) {
|
|
lineHeight = textSize.height();
|
|
// We check if we must change page
|
|
if (textBottomEndPosition.y() - lineHeight <= k_margin) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
endOfWord -= 1;
|
|
}
|
|
|
|
if (endOfWord + 1 == text()) {
|
|
m_pageOffset = 0;
|
|
} else {
|
|
m_pageOffset = endOfWord - text();
|
|
}
|
|
}
|
|
|
|
void WordWrapTextView::plainTextPreviousPage() {
|
|
const int charWidth = m_font->glyphSize().width();
|
|
const int charHeight = m_font->glyphSize().height();
|
|
|
|
const char * endOfWord = text() + m_pageOffset - 1;
|
|
const char * startOfWord = UTF8Helper::BeginningOfWord(text(), endOfWord);
|
|
|
|
KDPoint textEndPosition(m_frame.width() - k_margin, m_frame.height() - k_margin);
|
|
|
|
while(endOfWord>text()) {
|
|
KDSize textSize = m_font->stringSizeUntil(startOfWord, endOfWord);
|
|
KDPoint textStartPosition = KDPoint(textEndPosition.x()-textSize.width(), textEndPosition.y());
|
|
|
|
if (textStartPosition.x() < k_margin) {
|
|
// We check if the word is too long to be displayed entirely on just one line
|
|
if(textSize.width() > m_frame.width() - 2 * k_margin) {
|
|
startOfWord = endOfWord - (textEndPosition.x() - k_margin) / charWidth;
|
|
continue;
|
|
}
|
|
textEndPosition = KDPoint(m_frame.width() - k_margin, textEndPosition.y() - charHeight);
|
|
textStartPosition = KDPoint(textEndPosition.x() - textSize.width(), textEndPosition.y());
|
|
}
|
|
if (textEndPosition.y() - textSize.height() < k_margin) {
|
|
break;
|
|
}
|
|
|
|
--startOfWord;
|
|
while (startOfWord >= text() && (*startOfWord == ' ' || *startOfWord == '\n')) {
|
|
if (*startOfWord == ' ') {
|
|
textStartPosition = KDPoint(textStartPosition.x() - charWidth, textStartPosition.y());
|
|
} else {
|
|
textStartPosition = KDPoint(m_frame.width() - k_margin, textStartPosition.y() - charHeight);
|
|
}
|
|
--startOfWord;
|
|
}
|
|
|
|
if (textStartPosition.y() < k_margin) { // If out of page, quit
|
|
break;
|
|
}
|
|
|
|
if (textStartPosition.y() != textEndPosition.y()) { // If line changed, x is at start of line
|
|
textStartPosition = KDPoint(m_frame.width() - k_margin, textStartPosition.y());
|
|
}
|
|
if (textStartPosition.x() <= k_margin) { // Go to line if left overflow
|
|
textStartPosition = KDPoint(m_frame.width() - k_margin, textStartPosition.y() - charHeight);
|
|
}
|
|
|
|
textEndPosition = textStartPosition;
|
|
endOfWord = startOfWord + 1;
|
|
startOfWord = UTF8Helper::BeginningOfWord(text(), endOfWord);
|
|
}
|
|
|
|
while (endOfWord < text() + m_length && (*endOfWord == ' ' || *endOfWord == '\n')) {
|
|
endOfWord++;
|
|
}
|
|
|
|
m_pageOffset = endOfWord - text();
|
|
}
|
|
|
|
void WordWrapTextView::drawRect(KDContext * ctx, KDRect rect) const {
|
|
ctx->fillRect(KDRect(0, 0, bounds().width(), bounds().height()), m_backgroundColor);
|
|
|
|
if (m_isRichTextFile) {
|
|
richTextDrawRect(ctx, rect);
|
|
} else {
|
|
plainTextDrawRect(ctx, rect);
|
|
}
|
|
}
|
|
|
|
void WordWrapTextView::richTextDrawRect(KDContext * ctx, KDRect rect) const {
|
|
enum class ToDraw {
|
|
Text,
|
|
Expression
|
|
};
|
|
|
|
bool endOfPage = false;
|
|
|
|
const char * endOfFile = text() + m_length;
|
|
const char * startOfWord = text() + m_pageOffset;
|
|
|
|
const int charWidth = m_font->glyphSize().width();
|
|
const int charHeight = m_font->glyphSize().height();
|
|
|
|
const int wordMaxLength = (m_frame.width() - 2*k_margin ) / charWidth;
|
|
char word[wordMaxLength];
|
|
|
|
Layout tooBigLayout = EmptyLayout::Builder(); // To store layouts that are too big to be displayed entirely on one line
|
|
KDCoordinate tooBigLayoutAlreadyWroteWidth = 0; // We store here the part of the layout that has already been written
|
|
|
|
KDPoint textPosition = KDPoint(k_margin, k_margin);
|
|
|
|
while (!endOfPage && (startOfWord < endOfFile || !tooBigLayout.isEmpty())) {
|
|
// We process line by line
|
|
|
|
const char * firstReadIndex = startOfWord;
|
|
|
|
// 1. We compute the size of what we are going to draw and the baseline
|
|
KDSize lineSize = KDSize(0, charHeight);
|
|
KDCoordinate baseline = charHeight / 2;
|
|
|
|
while (firstReadIndex < endOfFile) {
|
|
// 1.0. We check if we are drawing a too big layout
|
|
if (!tooBigLayout.isEmpty()) {
|
|
lineSize = tooBigLayout.layoutSize();
|
|
baseline = tooBigLayout.baseline();
|
|
|
|
if (tooBigLayout.layoutSize().width() - tooBigLayoutAlreadyWroteWidth > m_frame.width() - 2 * k_margin) {
|
|
// Remaining part of the layout don't fit on the line
|
|
break;
|
|
}
|
|
}
|
|
|
|
KDSize textSize = KDSizeZero;
|
|
KDCoordinate updatedBaseline = 0; // 0 if it's not a layout
|
|
|
|
// 1.1. And we check if we are at the end of the line
|
|
if(*firstReadIndex == '\n') {
|
|
break;
|
|
}
|
|
|
|
// 1.2. Check if we are in a color change
|
|
if (*firstReadIndex == '%') { // We assume each '%' non-escaped is announcing a color change
|
|
// We go to the end of the color change + 1
|
|
const char * startIndex = firstReadIndex;
|
|
|
|
do {
|
|
firstReadIndex ++;
|
|
} while (*firstReadIndex != '%' && firstReadIndex < endOfFile && startIndex - firstReadIndex < 5);
|
|
firstReadIndex ++;
|
|
|
|
if (firstReadIndex - startIndex > 5) {
|
|
m_readBookController->throwError();
|
|
return;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// 1.3. Check if we are in a math expression
|
|
if (*firstReadIndex == '$') {
|
|
// We go to the end of the expression + 1
|
|
const char * expressionStart = ++firstReadIndex;
|
|
while (*firstReadIndex != '$') {
|
|
if (firstReadIndex > endOfFile) {
|
|
break; // File isn't rightly formated
|
|
}
|
|
firstReadIndex ++;
|
|
}
|
|
|
|
TexParser parser = TexParser(expressionStart, firstReadIndex);
|
|
Layout firstReadLayout = parser.getLayout();
|
|
|
|
KDCoordinate layoutBaseline = firstReadLayout.baseline();
|
|
|
|
updatedBaseline = baseline; // We really update baseline after, if the layout fit on the line
|
|
if (layoutBaseline > baseline) {
|
|
updatedBaseline = layoutBaseline;
|
|
}
|
|
|
|
KDSize layoutSize = firstReadLayout.layoutSize();
|
|
textSize = KDSize(layoutSize.width(), layoutSize.height() + updatedBaseline - layoutBaseline);
|
|
|
|
firstReadIndex ++;
|
|
}
|
|
|
|
// 1.4. Else it's text
|
|
else {
|
|
if ((*firstReadIndex == '\\' && *(firstReadIndex + 1) == '$') || (*firstReadIndex == '\\' && *(firstReadIndex + 1) == '%')) { // We escape '$' and '%' if needed
|
|
firstReadIndex ++;
|
|
}
|
|
|
|
const char * endOfWord = EndOfPrintableWord(firstReadIndex + 1, endOfFile);
|
|
|
|
textSize = m_font->stringSizeUntil(firstReadIndex, endOfWord);
|
|
|
|
firstReadIndex = endOfWord;
|
|
}
|
|
|
|
// 1.5. We update size
|
|
int newWidth = lineSize.width() + textSize.width();
|
|
// We check if the new text fit on the line.
|
|
// If not, we look if only the word cannot fit on one line. If so, we do not go to the line
|
|
if (newWidth > m_frame.width() - 2 * k_margin && !(textSize.width() > m_frame.width() - 2 * k_margin)) {
|
|
break;
|
|
}
|
|
|
|
if (updatedBaseline) { // Now we update baseline
|
|
baseline = updatedBaseline;
|
|
}
|
|
|
|
int newHeight;
|
|
if (lineSize.height() > textSize.height()) {
|
|
newHeight = lineSize.height();
|
|
} else {
|
|
newHeight = textSize.height();
|
|
// We check if all the content can be displayed
|
|
if (textPosition.y() + newHeight > bounds().height() - k_margin) {
|
|
endOfPage = true;
|
|
break;
|
|
}
|
|
}
|
|
lineSize = KDSize(lineSize.width() + textSize.width(), newHeight);
|
|
|
|
// 1.6. We go to the next word
|
|
while (*firstReadIndex == ' ') {
|
|
lineSize = KDSize(lineSize.width() + charWidth, lineSize.height());
|
|
++firstReadIndex;
|
|
}
|
|
}
|
|
|
|
if (endOfPage) {
|
|
break;
|
|
}
|
|
|
|
// 2. And now... we read the line again to draw it !
|
|
while (startOfWord < endOfFile || !tooBigLayout.isEmpty()) {
|
|
// 2.0 Before all, we check if we were drawing a layout that was too big to fit on one line.
|
|
// In this case, we do all the routine here.
|
|
if (!tooBigLayout.isEmpty()) {
|
|
KDPoint position = KDPoint(textPosition.x() - tooBigLayoutAlreadyWroteWidth, textPosition.y());
|
|
tooBigLayout.draw(ctx, position, m_textColor, m_backgroundColor);
|
|
// We fill the left margin
|
|
ctx->fillRect(KDRect(0, textPosition.y(), k_margin, tooBigLayout.layoutSize().height()), m_backgroundColor);
|
|
|
|
KDCoordinate drawnWidth = tooBigLayout.layoutSize().width() - tooBigLayoutAlreadyWroteWidth;
|
|
tooBigLayoutAlreadyWroteWidth += drawnWidth;
|
|
|
|
if (drawnWidth > m_frame.width() - 2 * k_margin) {
|
|
// We have to fill the margin with the background color
|
|
ctx->fillRect(KDRect(textPosition.x() + drawnWidth, textPosition.y(), k_margin, lineSize.height()), m_backgroundColor);
|
|
textPosition = KDPoint(k_margin, textPosition.y() + lineSize.height());
|
|
break;
|
|
} else {
|
|
tooBigLayout = EmptyLayout::Builder(); // We have finished drawing the tooBigLayout
|
|
}
|
|
continue;
|
|
}
|
|
|
|
Layout layout;
|
|
|
|
//2.1. We check if we are at the end of the line
|
|
if (*startOfWord == '\n') {
|
|
startOfWord++;
|
|
textPosition = KDPoint(k_margin, textPosition.y() + lineSize.height());
|
|
break;
|
|
// We aren't supposed to be at the end of the page, else the loop on top would have stopped drawing
|
|
}
|
|
|
|
const char * endOfWord;
|
|
|
|
// 2.2. Check if we are in a color change
|
|
if (*startOfWord == '%') {
|
|
if (updateTextColorForward(startOfWord)) {
|
|
startOfWord += 2; // We can add at least 2 ('%' + the color first char)
|
|
while (*startOfWord != '%') {
|
|
startOfWord ++;
|
|
}
|
|
startOfWord ++;
|
|
continue;
|
|
}
|
|
else {
|
|
m_readBookController->throwError();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 2.3. Check what we are going to draw and his size
|
|
|
|
KDSize textSize = KDSizeZero;
|
|
ToDraw toDraw;
|
|
|
|
// 2.3.1. Check if we are in a math expression
|
|
if (*startOfWord == '$') {
|
|
endOfWord = startOfWord + 1;
|
|
while (*endOfWord != '$') {
|
|
if (endOfWord > endOfFile) {
|
|
break; // File isn't rightly formated
|
|
}
|
|
endOfWord ++;
|
|
}
|
|
endOfWord ++;
|
|
|
|
TexParser parser = TexParser(startOfWord + 1, endOfWord - 1);
|
|
layout = parser.getLayout();
|
|
textSize = layout.layoutSize();
|
|
|
|
toDraw = ToDraw::Expression;
|
|
}
|
|
|
|
// 2.3.2 Else it's text
|
|
else {
|
|
if ((*startOfWord == '\\' && *(startOfWord + 1) == '$') || (*startOfWord == '\\' && *(startOfWord + 1) == '%')) {
|
|
startOfWord ++;
|
|
}
|
|
endOfWord = EndOfPrintableWord(startOfWord + 1, endOfFile);
|
|
textSize = m_font->stringSizeUntil(startOfWord, endOfWord);
|
|
stringNCopy(word, wordMaxLength, startOfWord, endOfWord-startOfWord);
|
|
toDraw = ToDraw::Text;
|
|
}
|
|
|
|
// 2.4 We decide where to draw and if we must change line
|
|
KDPoint endTextPosition = KDPoint(textPosition.x() + textSize.width(), textPosition.y());
|
|
|
|
// 2.4.1. Check if we need to go to the next line
|
|
if(endTextPosition.x() > m_frame.width() - k_margin) {
|
|
|
|
// We check if the word is too long to be displayed entirely on just one line
|
|
if(textSize.width() > m_frame.width() - 2 * k_margin) {
|
|
if (toDraw == ToDraw::Text) {
|
|
startOfWord = endOfWord - (endTextPosition.x() - k_margin) / charWidth;
|
|
continue;
|
|
} else {
|
|
assert(toDraw == ToDraw::Expression);
|
|
tooBigLayout = layout;
|
|
tooBigLayoutAlreadyWroteWidth += m_frame.width() - k_margin - textPosition.x();
|
|
endTextPosition = KDPoint(k_margin, textPosition.y() + lineSize.height()); // We jump line now, because this will be the next value of textPosition
|
|
}
|
|
} else {
|
|
textPosition = KDPoint(k_margin, textPosition.y() + lineSize.height());
|
|
// endTextPosition will be updated later
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 2.5. Now we draw !
|
|
if (toDraw == ToDraw::Expression) {
|
|
KDPoint position = KDPoint(textPosition.x(), textPosition.y() + baseline - layout.baseline());
|
|
layout.draw(ctx, position, m_textColor, m_backgroundColor);
|
|
|
|
if (!tooBigLayout.isEmpty()) {
|
|
// We fill the margin
|
|
ctx->fillRect(KDRect(m_frame.width() - k_margin, textPosition.y(), k_margin, lineSize.height()), m_backgroundColor);
|
|
}
|
|
}
|
|
else {
|
|
KDPoint position = KDPoint(textPosition.x(), textPosition.y() + baseline - charHeight / 2);
|
|
ctx->drawString(word, position, m_font, m_textColor, m_backgroundColor);
|
|
}
|
|
|
|
// 2.6. Update the position
|
|
textPosition = endTextPosition;
|
|
|
|
// 2.7. And we go to the next word
|
|
while (*endOfWord == ' ') {
|
|
endOfWord++;
|
|
textPosition = KDPoint(textPosition.x() + charWidth, textPosition.y());
|
|
}
|
|
startOfWord = endOfWord;
|
|
|
|
// 2.8. We exit now if we are in the "too big layout" case
|
|
if (!tooBigLayout.isEmpty()) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
m_nextPageOffset = startOfWord - text();
|
|
}
|
|
|
|
|
|
void WordWrapTextView::plainTextDrawRect(KDContext * ctx, KDRect rect) const {
|
|
ctx->fillRect(KDRect(0, 0, bounds().width(), bounds().height()), m_backgroundColor);
|
|
|
|
const char * endOfFile = text() + m_length;
|
|
const char * startOfWord = text() + m_pageOffset;
|
|
const char * endOfWord = UTF8Helper::EndOfWord(startOfWord, endOfFile);
|
|
KDPoint textPosition(k_margin, k_margin);
|
|
|
|
const int wordMaxLength = 128;
|
|
char word[wordMaxLength];
|
|
|
|
const int charWidth = m_font->glyphSize().width();
|
|
const int charHeight = m_font->glyphSize().height();
|
|
|
|
while(startOfWord < endOfFile) {
|
|
KDSize textSize = m_font->stringSizeUntil(startOfWord, endOfWord);
|
|
KDPoint nextTextPosition = KDPoint(textPosition.x()+textSize.width(), textPosition.y());
|
|
|
|
if(nextTextPosition.x() > m_frame.width() - k_margin) { // Right overflow
|
|
// We check if the word is too long to be displayed entirely on just one line
|
|
if(textSize.width() > m_frame.width() - 2 * k_margin) {
|
|
endOfWord = startOfWord + (m_frame.width() - k_margin - textPosition.x()) / charWidth;
|
|
continue;
|
|
}
|
|
textPosition = KDPoint(k_margin, textPosition.y() + textSize.height());
|
|
nextTextPosition = KDPoint(k_margin + textSize.width(), textPosition.y());
|
|
}
|
|
|
|
if(textPosition.y() + textSize.height() > m_frame.height() - k_margin) { // Bottom overflow
|
|
break;
|
|
}
|
|
|
|
stringNCopy(word, wordMaxLength, startOfWord, endOfWord-startOfWord);
|
|
ctx->drawString(word, textPosition, m_font, m_textColor, m_backgroundColor);
|
|
|
|
while(*endOfWord == ' ' || *endOfWord == '\n') {
|
|
if(*endOfWord == ' ') {
|
|
nextTextPosition = KDPoint(nextTextPosition.x() + charWidth, nextTextPosition.y());
|
|
}
|
|
else {
|
|
nextTextPosition = KDPoint(k_margin, nextTextPosition.y() + charHeight);
|
|
}
|
|
++endOfWord;
|
|
}
|
|
|
|
//We must change value of startOfWord now to avoid having
|
|
//two times the same word if the break below is used
|
|
startOfWord = endOfWord;
|
|
|
|
if(nextTextPosition.y() + textSize.height() > m_frame.height() - k_margin) { // If out of page, quit
|
|
break;
|
|
}
|
|
if(nextTextPosition.y() != textPosition.y()) { // If line changed, x is at start of line
|
|
nextTextPosition = KDPoint(k_margin, nextTextPosition.y());
|
|
}
|
|
if(nextTextPosition.x() >= m_frame.width() - k_margin) { // Go to line if right overflow
|
|
nextTextPosition = KDPoint(k_margin, nextTextPosition.y() + textSize.height());
|
|
}
|
|
|
|
textPosition = nextTextPosition;
|
|
endOfWord = UTF8Helper::EndOfWord(startOfWord, endOfFile);
|
|
}
|
|
|
|
m_nextPageOffset = startOfWord - text();
|
|
}
|
|
|
|
BookSave WordWrapTextView::getBookSave() const {
|
|
return {
|
|
m_pageOffset,
|
|
m_textColor
|
|
};
|
|
}
|
|
|
|
void WordWrapTextView::setBookSave(BookSave save) {
|
|
m_pageOffset = save.offset;
|
|
// TODO: Understand why the color save crash the calculator and fix it
|
|
// m_textColor = save.color;
|
|
m_lastPagesOffsetsIndex = 0;
|
|
|
|
for (int i = 0; i < k_lastOffsetsBufferSize; i++) {
|
|
m_lastPagesOffsets[i] = -1; // -1 Means : no informations
|
|
}
|
|
}
|
|
|
|
bool WordWrapTextView::updateTextColorForward(const char * colorStart) const {
|
|
|
|
if (*(colorStart + 1) == '\\' && (*(colorStart + 3) == '%' || *(colorStart + 4) == '%')) {
|
|
m_textColor = Palette::PrimaryText;
|
|
return true;
|
|
}
|
|
|
|
int keySize = 1;
|
|
KDColor lastColor = m_textColor;
|
|
|
|
switch (*(colorStart+1))
|
|
{
|
|
case 'r':
|
|
if (*(colorStart+2) == 'l') {
|
|
m_textColor = Palette::RedLight;
|
|
keySize = 2;
|
|
}
|
|
else {
|
|
m_textColor = Palette::Red;
|
|
}
|
|
break;
|
|
case 'm':
|
|
m_textColor = Palette::Magenta;
|
|
break;
|
|
case 't':
|
|
m_textColor = Palette::Turquoise;
|
|
break;
|
|
case 'p':
|
|
if (*(colorStart+2) == 'k') {
|
|
m_textColor = Palette::Pink;
|
|
keySize = 2;
|
|
}
|
|
else if (*(colorStart+2) == 'p') {
|
|
m_textColor = Palette::Purple;
|
|
keySize = 2;
|
|
}
|
|
break;
|
|
case 'b':
|
|
if (*(colorStart+2) == 'r') {
|
|
m_textColor = Palette::Brown;
|
|
keySize = 2;
|
|
}
|
|
else if (*(colorStart+2) == 'l') {
|
|
m_textColor = Palette::BlueLight;
|
|
keySize = 2;
|
|
}
|
|
else {
|
|
m_textColor = Palette::Blue;
|
|
}
|
|
break;
|
|
case 'o':
|
|
m_textColor = Palette::Orange;
|
|
break;
|
|
case 'g':
|
|
if (*(colorStart+2) == 'l') {
|
|
m_textColor = Palette::GreenLight;
|
|
keySize = 2;
|
|
}
|
|
else {
|
|
m_textColor = Palette::Green;
|
|
}
|
|
break;
|
|
case 'c':
|
|
m_textColor = Palette::Cyan;
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
if (*(colorStart + keySize + 1) != '%') {
|
|
m_textColor = lastColor;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool WordWrapTextView::updateTextColorBackward(const char * colorStart) const {
|
|
|
|
if (*(++colorStart) != '\\') {
|
|
if (*(colorStart + 1) == '%' || *(colorStart + 2) == '%') {
|
|
m_textColor = Palette::PrimaryText;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int keySize = 1;
|
|
KDColor lastColor = m_textColor;
|
|
switch (*(colorStart+1))
|
|
{
|
|
case 'r':
|
|
if (*(colorStart+2) == 'l') {
|
|
m_textColor = Palette::RedLight;
|
|
keySize = 2;
|
|
}
|
|
else {
|
|
m_textColor = Palette::Red;
|
|
}
|
|
break;
|
|
case 'm':
|
|
m_textColor = Palette::Magenta;
|
|
break;
|
|
case 't':
|
|
m_textColor = Palette::Turquoise;
|
|
break;
|
|
case 'p':
|
|
if (*(colorStart+2) == 'k') {
|
|
m_textColor = Palette::Pink;
|
|
keySize = 2;
|
|
}
|
|
else if (*(colorStart+2) == 'p') {
|
|
m_textColor = Palette::Purple;
|
|
keySize = 2;
|
|
}
|
|
break;
|
|
case 'b':
|
|
if (*(colorStart+2) == 'r') {
|
|
m_textColor = Palette::Brown;
|
|
keySize = 2;
|
|
}
|
|
else if (*(colorStart+2) == 'l') {
|
|
m_textColor = Palette::BlueLight;
|
|
keySize = 2;
|
|
}
|
|
else {
|
|
m_textColor = Palette::Blue;
|
|
}
|
|
break;
|
|
case 'o':
|
|
m_textColor = Palette::Orange;
|
|
break;
|
|
case 'g':
|
|
if (*(colorStart+2) == 'l') {
|
|
m_textColor = Palette::GreenLight;
|
|
keySize = 2;
|
|
}
|
|
else {
|
|
m_textColor = Palette::Green;
|
|
}
|
|
break;
|
|
case 'c':
|
|
m_textColor = Palette::Cyan;
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
if (*(colorStart + keySize + 1) != '%') {
|
|
m_textColor = lastColor;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
}
|