|
|
|
|
@@ -5,17 +5,19 @@
|
|
|
|
|
#include <poincare/expression.h>
|
|
|
|
|
#include "../shared/poincare_helpers.h"
|
|
|
|
|
#include <poincare/undefined.h>
|
|
|
|
|
#include "read_book_controller.h"
|
|
|
|
|
|
|
|
|
|
namespace Reader
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
WordWrapTextView::WordWrapTextView() :
|
|
|
|
|
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_lastPagesOffsetsIndex(0),
|
|
|
|
|
m_readBookController(readBookController)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < k_lastOffsetsBufferSize; i++) {
|
|
|
|
|
m_lastPagesOffsets[i] = -1; // -1 Means : no informations
|
|
|
|
|
@@ -96,7 +98,8 @@ void WordWrapTextView::richTextPreviousPage() {
|
|
|
|
|
endOfWord = startOfWord - 1; // Update next endOfWord
|
|
|
|
|
continue;
|
|
|
|
|
} else {
|
|
|
|
|
// TODO: print error
|
|
|
|
|
m_readBookController->throwError();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -110,7 +113,7 @@ void WordWrapTextView::richTextPreviousPage() {
|
|
|
|
|
if (expressionStart < text()) {
|
|
|
|
|
break; // File isn't rightly formated
|
|
|
|
|
}
|
|
|
|
|
expressionStart ++;
|
|
|
|
|
expressionStart --;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TexParser parser = TexParser(expressionStart, endOfWord);
|
|
|
|
|
@@ -175,13 +178,16 @@ void WordWrapTextView::plainTextPreviousPage() {
|
|
|
|
|
|
|
|
|
|
KDPoint textEndPosition(m_frame.width() - k_margin, m_frame.height() - k_margin);
|
|
|
|
|
|
|
|
|
|
while(startOfWord>=text()) {
|
|
|
|
|
startOfWord = UTF8Helper::BeginningOfWord(text(), endOfWord);
|
|
|
|
|
endOfWord = UTF8Helper::EndOfWord(startOfWord);
|
|
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
@@ -203,16 +209,23 @@ void WordWrapTextView::plainTextPreviousPage() {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (textStartPosition.y() != textEndPosition.y()) { // If line changed, x is at start of line
|
|
|
|
|
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
|
|
|
|
|
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 {
|
|
|
|
|
@@ -242,11 +255,12 @@ void WordWrapTextView::richTextDrawRect(KDContext * ctx, KDRect rect) const {
|
|
|
|
|
const int wordMaxLength = (m_frame.width() - 2*k_margin ) / charWidth;
|
|
|
|
|
char word[wordMaxLength];
|
|
|
|
|
|
|
|
|
|
Layout layout;
|
|
|
|
|
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) {
|
|
|
|
|
while (!endOfPage && (startOfWord < endOfFile || !tooBigLayout.isEmpty())) {
|
|
|
|
|
// We process line by line
|
|
|
|
|
|
|
|
|
|
const char * firstReadIndex = startOfWord;
|
|
|
|
|
@@ -256,8 +270,19 @@ void WordWrapTextView::richTextDrawRect(KDContext * ctx, KDRect rect) const {
|
|
|
|
|
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') {
|
|
|
|
|
@@ -265,12 +290,20 @@ void WordWrapTextView::richTextDrawRect(KDContext * ctx, KDRect rect) const {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 1.2. Check if we are in a color change
|
|
|
|
|
if (*firstReadIndex == '%') { // We assume each '%' non-escaped is announcing a color change // TODO : check file is rightly formated
|
|
|
|
|
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 != '%');
|
|
|
|
|
} while (*firstReadIndex != '%' && firstReadIndex < endOfFile && startIndex - firstReadIndex < 5);
|
|
|
|
|
firstReadIndex ++;
|
|
|
|
|
|
|
|
|
|
if (firstReadIndex - startIndex > 5) {
|
|
|
|
|
m_readBookController->throwError();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -286,16 +319,17 @@ void WordWrapTextView::richTextDrawRect(KDContext * ctx, KDRect rect) const {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TexParser parser = TexParser(expressionStart, firstReadIndex);
|
|
|
|
|
Layout layout = parser.getLayout();
|
|
|
|
|
Layout firstReadLayout = parser.getLayout();
|
|
|
|
|
|
|
|
|
|
KDCoordinate layoutBaseline = layout.baseline();
|
|
|
|
|
// We check if we must change baseline
|
|
|
|
|
KDCoordinate layoutBaseline = firstReadLayout.baseline();
|
|
|
|
|
|
|
|
|
|
updatedBaseline = baseline; // We really update baseline after, if the layout fit on the line
|
|
|
|
|
if (layoutBaseline > baseline) {
|
|
|
|
|
baseline = layoutBaseline;
|
|
|
|
|
updatedBaseline = layoutBaseline;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
KDSize layoutSize = layout.layoutSize();
|
|
|
|
|
textSize = KDSize(layoutSize.width(), layoutSize.height() + baseline - layoutBaseline);
|
|
|
|
|
KDSize layoutSize = firstReadLayout.layoutSize();
|
|
|
|
|
textSize = KDSize(layoutSize.width(), layoutSize.height() + updatedBaseline - layoutBaseline);
|
|
|
|
|
|
|
|
|
|
firstReadIndex ++;
|
|
|
|
|
}
|
|
|
|
|
@@ -315,11 +349,16 @@ void WordWrapTextView::richTextDrawRect(KDContext * ctx, KDRect rect) const {
|
|
|
|
|
|
|
|
|
|
// 1.5. We update size
|
|
|
|
|
int newWidth = lineSize.width() + textSize.width();
|
|
|
|
|
// We check if the new text fit on the line
|
|
|
|
|
if (newWidth > m_frame.width() - 2 * k_margin) {
|
|
|
|
|
// 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();
|
|
|
|
|
@@ -345,7 +384,30 @@ void WordWrapTextView::richTextDrawRect(KDContext * ctx, KDRect rect) const {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. And now... we read the line again to draw it !
|
|
|
|
|
while (startOfWord < endOfFile) {
|
|
|
|
|
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') {
|
|
|
|
|
@@ -355,7 +417,6 @@ void WordWrapTextView::richTextDrawRect(KDContext * ctx, KDRect rect) const {
|
|
|
|
|
// 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
|
|
|
|
|
@@ -369,7 +430,8 @@ void WordWrapTextView::richTextDrawRect(KDContext * ctx, KDRect rect) const {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// TODO: Print exception
|
|
|
|
|
m_readBookController->throwError();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -412,14 +474,34 @@ void WordWrapTextView::richTextDrawRect(KDContext * ctx, KDRect rect) const {
|
|
|
|
|
|
|
|
|
|
// 2.4.1. Check if we need to go to the next line
|
|
|
|
|
if(endTextPosition.x() > m_frame.width() - k_margin) {
|
|
|
|
|
textPosition = KDPoint(k_margin, textPosition.y() + lineSize.height());
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
@@ -435,6 +517,11 @@ void WordWrapTextView::richTextDrawRect(KDContext * ctx, KDRect rect) const {
|
|
|
|
|
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();
|
|
|
|
|
@@ -446,7 +533,7 @@ void WordWrapTextView::plainTextDrawRect(KDContext * ctx, KDRect rect) const {
|
|
|
|
|
|
|
|
|
|
const char * endOfFile = text() + m_length;
|
|
|
|
|
const char * startOfWord = text() + m_pageOffset;
|
|
|
|
|
const char * endOfWord = UTF8Helper::EndOfWord(startOfWord);
|
|
|
|
|
const char * endOfWord = UTF8Helper::EndOfWord(startOfWord, endOfFile);
|
|
|
|
|
KDPoint textPosition(k_margin, k_margin);
|
|
|
|
|
|
|
|
|
|
const int wordMaxLength = 128;
|
|
|
|
|
@@ -458,8 +545,13 @@ void WordWrapTextView::plainTextDrawRect(KDContext * ctx, KDRect rect) const {
|
|
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
@@ -488,15 +580,15 @@ void WordWrapTextView::plainTextDrawRect(KDContext * ctx, KDRect rect) const {
|
|
|
|
|
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
|
|
|
|
|
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
|
|
|
|
|
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);
|
|
|
|
|
endOfWord = UTF8Helper::EndOfWord(startOfWord, endOfFile);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_nextPageOffset = startOfWord - text();
|
|
|
|
|
@@ -523,7 +615,7 @@ bool WordWrapTextView::updateTextColorForward(const char * colorStart) const {
|
|
|
|
|
|
|
|
|
|
int keySize = 1;
|
|
|
|
|
KDColor lastColor = m_textColor;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch (*(colorStart+1))
|
|
|
|
|
{
|
|
|
|
|
case 'r':
|
|
|
|
|
@@ -666,6 +758,6 @@ bool WordWrapTextView::updateTextColorBackward(const char * colorStart) const {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|