Files
Upsilon/apps/reader/word_wrap_view.cpp

424 lines
11 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>
namespace Reader
{
WordWrapTextView::WordWrapTextView() :
PointerTextView(GlobalPreferences::sharedGlobalPreferences()->font()),
m_pageOffset(0),
m_nextPageOffset(0),
m_length(0)
{
}
void WordWrapTextView::nextPage() {
if(m_nextPageOffset >= m_length) {
return;
}
m_pageOffset = m_nextPageOffset;
markRectAsDirty(bounds());
}
void WordWrapTextView::setText(const char* text, int length) {
PointerTextView::setText(text);
m_length = length;
}
void WordWrapTextView::previousPage() {
if(m_pageOffset <= 0) {
return;
}
const int charWidth = m_font->glyphSize().width();
const int charHeight = m_font->glyphSize().height();
const char * endOfFile = text() + m_length;
const char * endOfWord = text() + m_pageOffset;
const char * startOfWord = StartOfPrintableWord(endOfWord, text());
KDSize textSize = KDSizeZero;
KDPoint textEndPosition(m_frame.width() - k_margin, m_frame.height() - k_margin);
while(startOfWord>=text()) {
startOfWord = StartOfPrintableWord(endOfWord-1, text());
//endOfWord = EndOfPrintableWord(startOfWord, endOfFile);
if (*startOfWord == '%') {
if (updateTextColorBackward(startOfWord)) {
endOfWord = startOfWord - 1;
continue;
}
}
if (*endOfWord == '$') {
startOfWord = endOfWord - 1;
while (*startOfWord != '$') {
if (startOfWord < text()) {
break; // File isn't rightly formated
}
startOfWord --;
}
startOfWord --;
TexParser parser = TexParser(startOfWord + 1, endOfWord - 2);
Poincare::Layout layout = parser.getLayout();
textSize = layout.layoutSize();
}
else {
if (*startOfWord == '\\' || *(startOfWord + 1) == '$') {
textSize = m_font->stringSizeUntil(startOfWord + 1, endOfWord);
}
else {
textSize = m_font->stringSizeUntil(startOfWord, endOfWord);
}
}
KDPoint textStartPosition = KDPoint(textEndPosition.x()-textSize.width(), textEndPosition.y());
if(textStartPosition.x() < k_margin) {
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;
}
if(startOfWord + 1 == text()) {
m_pageOffset = 0;
}
else {
m_pageOffset = EndOfPrintableWord(startOfWord, endOfFile) - text() + 1;
}
markRectAsDirty(bounds());
}
void WordWrapTextView::drawRect(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;
if (*startOfWord != '$') {
endOfWord = EndOfPrintableWord(startOfWord, endOfFile);
} // Else we don't need to update endOfWord
KDPoint textPosition(k_margin, k_margin);
const int wordMaxLength = 128;
char word[wordMaxLength];
Poincare::Layout layout;
enum class ToDraw {
Text,
Expression,
Nothing
};
ToDraw toDraw = ToDraw::Text;
const int charWidth = m_font->glyphSize().width();
const int charHeight = m_font->glyphSize().height();
int nextLineOffset = charHeight;
KDSize textSize = KDSizeZero;
while(startOfWord < endOfFile) {
if (*startOfWord == '%') { // Look for color keyword (ex '%bl%')
if (updateTextColorForward(startOfWord)) {
startOfWord = endOfWord + 1;
endOfWord = EndOfPrintableWord(startOfWord, endOfFile);
continue;
}
}
if (*startOfWord == '$') { // Look for expression
endOfWord = startOfWord + 1;
while (*endOfWord != '$') {
if (endOfWord > endOfFile) {
break; // If we are here, it's bad...
}
endOfWord ++;
}
endOfWord ++;
TexParser parser = TexParser(startOfWord + 1, endOfWord - 1);
layout = parser.getLayout();
textSize = layout.layoutSize();
toDraw = ToDraw::Expression;
}
else {
if (*startOfWord == '\\' || *(startOfWord + 1) == '$') {
startOfWord ++;
}
textSize = m_font->stringSizeUntil(startOfWord, endOfWord);
stringNCopy(word, wordMaxLength, startOfWord, endOfWord-startOfWord);
toDraw = ToDraw::Text;
}
KDPoint nextTextPosition = KDPoint(textPosition.x()+textSize.width(), textPosition.y());
if(nextTextPosition.x() > m_frame.width() - k_margin) { // Right overflow
textPosition = KDPoint(k_margin, textPosition.y() + nextLineOffset);
nextTextPosition = KDPoint(k_margin + textSize.width(), textPosition.y());
nextLineOffset = charHeight;
}
if (nextLineOffset < textSize.height()) {
nextLineOffset = textSize.height();
}
if(textPosition.y() + textSize.height() > m_frame.height() - k_margin) { // Bottom overflow
break;
}
if (toDraw == ToDraw::Expression) {
layout.draw(ctx, textPosition, m_textColor);
}
else if (toDraw == ToDraw::Text) {
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() + nextLineOffset);
nextLineOffset = 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 (endOfWord >= endOfFile) {
break;
}
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() + nextLineOffset);
nextLineOffset = charHeight;
}
textPosition = nextTextPosition;
if (*startOfWord != '$') {
endOfWord = EndOfPrintableWord(startOfWord+1, endOfFile);
} // Else we don't need to update endOfWord
}
m_nextPageOffset = startOfWord - text();
};
BookSave WordWrapTextView::getBookSave() const {
return {
m_pageOffset,
m_textColor
};
}
void WordWrapTextView::setBookSave(BookSave save) {
m_pageOffset = save.offset;
m_textColor = save.color;
}
bool WordWrapTextView::updateTextColorForward(const char * colorStart) const {
if (*(colorStart + 1) == '\\') {
m_textColor = Palette::PrimaryText;
return (*(colorStart + 3) == '%' || *(colorStart + 4) == '%');
}
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;
}
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++) != '\\') {
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;
}
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;
}
}