ZLTextWordCursor ZLTextAreaController::findStart(const ZLTextWordCursor &end, SizeUnit unit, int size) {
	ZLTextWordCursor start = end;
	size -= paragraphHeight(start, true, unit);
	bool positionChanged = !start.isStartOfParagraph();
	start.moveToParagraphStart();
	while (size > 0) {
		if (positionChanged && start.paragraphCursor().isEndOfSection()) {
			break;
		}
		if (!start.previousParagraph()) {
			break;
		}
		if (!start.paragraphCursor().isEndOfSection()) {
			positionChanged = true;
		}
		size -= paragraphHeight(start, false, unit);
	}
	skip(start, unit, -size);

	if (unit != LINE_UNIT) {
		bool sameStart = start == end;
		if (!sameStart && start.isEndOfParagraph() && end.isStartOfParagraph()) {
			ZLTextWordCursor startCopy = start;
			startCopy.nextParagraph();
			sameStart = startCopy == end;
		}
		if (sameStart) {
			start = findStart(end, LINE_UNIT, 1);
		}
	}

	return start;
}
size_t ZLTextView::PositionIndicator::sizeOfTextBeforeCursor(const ZLTextWordCursor &cursor) const {
	const size_t paragraphIndex = cursor.paragraphCursor().index();
	const size_t paragraphLength = cursor.paragraphCursor().paragraphLength();

	if (paragraphLength == 0) {
		return sizeOfTextBeforeParagraph(paragraphIndex);
	} else {
		return
			sizeOfTextBeforeParagraph(paragraphIndex) +
			muldiv(sizeOfParagraph(paragraphIndex), cursor.elementIndex(), paragraphLength);
	}
}
bool ScrollToHomeAction::isEnabled() const {
	if (!isVisible()) {
		return false;
	}
	ZLTextWordCursor cursor = FBReader::Instance().bookTextView().textArea().startCursor();
	return cursor.isNull() || !cursor.isStartOfParagraph() || !cursor.paragraphCursor().isFirst();
}
ZLTextWordCursor ZLTextAreaController::buildInfos(const ZLTextWordCursor &start) {
	myArea.myLineInfos.clear();

	ZLTextWordCursor cursor = start;
	int textHeight = myArea.height();
	int counter = 0;
	do {
		ZLTextWordCursor paragraphEnd = cursor;
		paragraphEnd.moveToParagraphEnd();
		ZLTextWordCursor paragraphStart = cursor;
		paragraphStart.moveToParagraphStart();

		ZLTextArea::Style style(myArea, myArea.myProperties.baseStyle());
		style.applyControls(paragraphStart, cursor);
		ZLTextLineInfoPtr info = new ZLTextLineInfo(cursor, style.textStyle(), style.bidiLevel());

		while (!info->End.isEndOfParagraph()) {
			info = myArea.processTextLine(style, info->End, paragraphEnd);
			textHeight -= info->Height + info->Descent;
			if ((textHeight < 0) && (counter > 0)) {
				break;
			}
			textHeight -= info->VSpaceAfter;
			cursor = info->End;
			myArea.myLineInfos.push_back(info);
			if (textHeight < 0) {
				break;
			}
			++counter;
		}
	} while (cursor.isEndOfParagraph() && cursor.nextParagraph() && !cursor.paragraphCursor().isEndOfSection() && (textHeight >= 0));

	return cursor;
}
Esempio n. 5
0
std::vector<size_t>::const_iterator ZLTextView::nextBreakIterator() const {
    ZLTextWordCursor cursor = endCursor();
    if (cursor.isNull()) {
        cursor = startCursor();
    }
    if (cursor.isNull()) {
        return myTextBreaks.begin();
    }
    return std::lower_bound(myTextBreaks.begin(), myTextBreaks.end(), cursor.paragraphCursor().index());
}
Esempio n. 6
0
bool strongContains(const ZLTextSelectionModel::Range &range, const ZLTextWordCursor &cursor) {
	const int pn = cursor.paragraphCursor().index();
	const int wn = cursor.elementIndex();
	return
		((range.first.ParagraphIndex < pn) ||
		 ((range.first.ParagraphIndex == pn) &&
			(range.first.ElementIndex < wn))) &&
		((range.second.ParagraphIndex > pn) ||
		 ((range.second.ParagraphIndex == pn) &&
			(range.second.ElementIndex > wn)));
}
Esempio n. 7
0
void IfEmptyWordList()
{
	if (acwlistcount == 0)
	{
		ZLTextWordCursor cr = is_footnote_mode() ? footview->textArea().startCursor() : bookview->textArea().startCursor();
		int paragraph = !cr.isNull() ? cr.paragraphCursor().index() : 0;

		if (acwlistcount + 3 >= acwlistsize)
		{
			acwlistsize = acwlistsize + (acwlistsize >> 1) + 64;
			acwordlist = (SynWordList *)realloc(acwordlist, acwlistsize * sizeof(SynWordList));
		}
Esempio n. 8
0
int ZLTextSelectionModel::charIndex(const ZLTextElementArea &area, int x) {
	myView.myStyle.setTextStyle(area.Style, area.BidiLevel);
	ZLTextWordCursor cursor = myView.startCursor();
	cursor.moveToParagraph(area.ParagraphIndex);
	const ZLTextWord &word = (const ZLTextWord&)cursor.paragraphCursor()[area.ElementIndex];
	const bool mainDir =
		area.BidiLevel % 2 == myView.myStyle.baseBidiLevel() % 2;
	const int deltaX = mainDir ? x - area.XStart : area.XEnd - x;
	const int len = area.Length;
	const int start = area.StartCharIndex;
	int diff = deltaX;
	int previousDiff = diff;
	int index;
	for (index = 0; (index < len) && (diff > 0); ++index) {
		previousDiff = diff;
		diff = deltaX - myView.myStyle.wordWidth(word, start, index + 1);
	}
	if (previousDiff + diff < 0) {
		--index;
	}
	return start + index;
}
int ZLTextSelectionModel::charIndex(const ZLTextElementRectangle &rectangle, int x) {
	int x1 = x - myArea.hOffset();
	ZLTextArea::Style style(myArea, rectangle.Style);
	style.setTextStyle(rectangle.Style, rectangle.BidiLevel);
	ZLTextWordCursor cursor = myArea.startCursor();
	cursor.moveToParagraph(rectangle.ParagraphIndex);
	const ZLTextWord &word = (const ZLTextWord&)cursor.paragraphCursor()[rectangle.ElementIndex];
	const bool mainDir =
		rectangle.BidiLevel % 2 == (myArea.isRtl() ? 1 : 0);
	const int deltaX = mainDir ? x1 - rectangle.XStart : rectangle.XEnd - x1;
	const int len = rectangle.Length;
	const int start = rectangle.StartCharIndex;
	int diff = deltaX;
	int previousDiff = diff;
	int index;
	for (index = 0; (index < len) && (diff > 0); ++index) {
		previousDiff = diff;
		diff = deltaX - style.wordWidth(word, start, index + 1);
	}
	if (previousDiff + diff < 0) {
		--index;
	}
	return start + index;
}
Esempio n. 10
0
BookTextView::Position BookTextView::cursorPosition(const ZLTextWordCursor &cursor) const {
	return Position(cursor.paragraphCursor().index(), cursor.elementIndex(), cursor.charIndex());
}
Esempio n. 11
0
BookTextView::Position BookTextView::cursorPosition(const ZLTextWordCursor &cursor) const {
	return Position(cursor.paragraphCursor().index(), cursor.wordNumber());
}
bool ZLTextSelectionModel::selectWord(int x, int y) {
	clear();

	const ZLTextElementRectangle *rectangle = myArea.elementByCoordinates(x, y);
	if (rectangle == 0) {
		return false;
	}

	int startIndex = 0;
	int endIndex = 1;
	switch (rectangle->Kind) {
		default:
			return false;
		case ZLTextElement::IMAGE_ELEMENT:
			break;
		case ZLTextElement::WORD_ELEMENT:
		{
			ZLTextWordCursor cursor = myArea.startCursor();
			cursor.moveToParagraph(rectangle->ParagraphIndex);
			const ZLTextWord &word = (const ZLTextWord&)cursor.paragraphCursor()[rectangle->ElementIndex];
			ZLUnicodeUtil::Ucs4String ucs4string;
			ZLUnicodeUtil::utf8ToUcs4(ucs4string, word.Data, word.Size);
			startIndex = charIndex(*rectangle, x);
			if (startIndex == word.Length) {
				--startIndex;
			}
			endIndex = startIndex + 1;
			ZLUnicodeUtil::Ucs4Char ch = ucs4string[startIndex];
			if (ZLUnicodeUtil::isLetter(ch) || (('0' <= ch) && (ch <= '9'))) {
				while (--startIndex >= 0) {
					ch = ucs4string[startIndex];
					if (!ZLUnicodeUtil::isLetter(ch) && ((ch < '0') || (ch > '9'))) {
						break;
					}
				}
				++startIndex;
				while (++endIndex <= word.Length) {
					ch = ucs4string[endIndex - 1];
					if (!ZLUnicodeUtil::isLetter(ch) && ((ch < '0') || (ch > '9'))) {
						break;
					}
				}
				--endIndex;
			}
		}
	}

	myFirstBound.Before.Exists = true;
	myFirstBound.Before.ParagraphIndex = rectangle->ParagraphIndex;
	myFirstBound.Before.ElementIndex = rectangle->ElementIndex;
	myFirstBound.Before.CharIndex = startIndex;
	myFirstBound.After = myFirstBound.Before;

	mySecondBound.Before = myFirstBound.Before;
	mySecondBound.Before.CharIndex = endIndex;
	mySecondBound.After = mySecondBound.Before;

	myIsEmpty = false;
	myTextIsUpToDate = false;
	myRangeVectorIsUpToDate = false;

	copySelectionToClipboard(ZLDialogManager::CLIPBOARD_SELECTION);

	return true;
}
ZLTextLineInfoPtr ZLTextView::processTextLine(const ZLTextWordCursor &start, const ZLTextWordCursor &end) {
	const bool useHyphenator =
		ZLTextStyleCollection::instance().baseStyle().AutoHyphenationOption.value();

	ZLTextLineInfoPtr infoPtr = new ZLTextLineInfo(start, myStyle.textStyle());

	std::set<ZLTextLineInfoPtr>::const_iterator it = myLineInfoCache.find(infoPtr);
	if (it != myLineInfoCache.end()) {
		const ZLTextLineInfoPtr &storedInfo = *it;
		myStyle.applyControls(storedInfo->Start, storedInfo->End);
		return storedInfo;
	}

	ZLTextLineInfo &info = *infoPtr;
	ZLTextWordCursor current = start;
	const ZLTextParagraphCursor &paragraphCursor = current.paragraphCursor();
	const bool isFirstLine = current.isStartOfParagraph();

	if (paragraphCursor.paragraph().kind() == ZLTextParagraph::TREE_PARAGRAPH) {
		info.NodeInfo = new ZLTextTreeNodeInfo();
		ZLTextTreeNodeInfo &nodeInfo = *info.NodeInfo;
		const ZLTextTreeParagraph &treeParagraph = (const ZLTextTreeParagraph&)paragraphCursor.paragraph();
		nodeInfo.IsLeaf = treeParagraph.children().empty();
		nodeInfo.IsOpen = treeParagraph.isOpen();
		nodeInfo.IsFirstLine = isFirstLine;
		nodeInfo.ParagraphNumber = paragraphCursor.index();

		nodeInfo.VerticalLinesStack.reserve(treeParagraph.depth() - 1);
		if (treeParagraph.depth() > 1) {
			const ZLTextTreeParagraph *ctp = treeParagraph.parent();
			nodeInfo.VerticalLinesStack.push_back(ctp->children().back() != &treeParagraph);
			for (int i = 1; i < treeParagraph.depth() - 1; ++i) {
				const ZLTextTreeParagraph *parent = ctp->parent();
				nodeInfo.VerticalLinesStack.push_back(ctp != parent->children().back());
				ctp = parent;
			}
		}
	}

	if (isFirstLine) {
		ZLTextElement::Kind elementKind = paragraphCursor[current.wordNumber()].kind();
		while ((elementKind == ZLTextElement::CONTROL_ELEMENT) ||
					 (elementKind == ZLTextElement::FORCED_CONTROL_ELEMENT)) {
			const ZLTextElement &element = paragraphCursor[current.wordNumber()];
			switch (elementKind) {
				case ZLTextElement::CONTROL_ELEMENT:
					myStyle.applyControl((const ZLTextControlElement&)element);
					break;
				case ZLTextElement::FORCED_CONTROL_ELEMENT:
					myStyle.applyControl((const ZLTextForcedControlElement&)element);
					break;
				default:
					break;
			}
			current.nextWord();
			if (current.equalWordNumber(end)) {
				break;
			}
			elementKind = paragraphCursor[current.wordNumber()].kind();
		}
		info.StartStyle = myStyle.textStyle();
		info.RealStart = current;
	}

	ZLTextStylePtr storedStyle = myStyle.textStyle();

	info.LeftIndent = myStyle.textStyle()->leftIndent();
	if (isFirstLine) {
		info.LeftIndent += myStyle.textStyle()->firstLineIndentDelta();
	}
	if (!info.NodeInfo.isNull()) {
		info.LeftIndent += (myStyle.context().stringHeight() + 2) / 3 * 4 * (info.NodeInfo->VerticalLinesStack.size() + 1);
	}
	info.Width = info.LeftIndent;

	if (info.RealStart.equalWordNumber(end)) {
	  info.End = info.RealStart;
		return infoPtr;
	}

	ZLTextPartialInfo newInfo(info, current);
	bool allowBreakAtNBSpace = true;
	const int maxWidth = viewWidth() - myStyle.textStyle()->rightIndent();
	bool wordOccured = false;
	int lastSpaceWidth = 0;
	int removeLastSpace = false;

	ZLTextElement::Kind elementKind = paragraphCursor[newInfo.End.wordNumber()].kind();

	bool breakedAtFirstWord = false;
	do {
		const ZLTextElement &element = paragraphCursor[newInfo.End.wordNumber()];
		newInfo.Width += myStyle.elementWidth(element, newInfo.End.charNumber());
		newInfo.Height = std::max(newInfo.Height, myStyle.elementHeight(element));
		newInfo.Descent = std::max(newInfo.Descent, myStyle.elementDescent(element));
		switch (elementKind) {
			case ZLTextElement::CONTROL_ELEMENT:
				myStyle.applyControl((const ZLTextControlElement&)element);
				break;
			case ZLTextElement::FORCED_CONTROL_ELEMENT:
				myStyle.applyControl((const ZLTextForcedControlElement&)element);
				break;
			case ZLTextElement::WORD_ELEMENT:
			case ZLTextElement::IMAGE_ELEMENT:
				wordOccured = true;
				newInfo.IsVisible = true;
				break;
			case ZLTextElement::HSPACE_ELEMENT:
			case ZLTextElement::NB_HSPACE_ELEMENT:
				if (wordOccured) {
					wordOccured = false;
					++newInfo.SpaceCounter;
					lastSpaceWidth = myStyle.context().spaceWidth();
					newInfo.Width += lastSpaceWidth;
				}
				break;
			case ZLTextElement::EMPTY_LINE_ELEMENT:
				newInfo.IsVisible = true;
			default:
				break;
		}

		if (newInfo.Width > maxWidth) {
			if (!info.End.equalWordNumber(start)) {
				break;
			}
			if (useHyphenator && myStyle.textStyle()->allowHyphenations() &&
					(elementKind == ZLTextElement::WORD_ELEMENT)) {
				breakedAtFirstWord = true;
				break;
			}
		}

		ZLTextElement::Kind previousKind = elementKind;
		newInfo.End.nextWord();
		bool allowBreak = newInfo.End.equalWordNumber(end);
		bool nbspaceBreak = false;
		if (!allowBreak) {
			elementKind = paragraphCursor[newInfo.End.wordNumber()].kind();
			if (elementKind == ZLTextElement::NB_HSPACE_ELEMENT) {
				if (allowBreakAtNBSpace) {
					allowBreak = true;
					nbspaceBreak = true;
				}
			} else {
				allowBreak =
					((elementKind != ZLTextElement::WORD_ELEMENT) || (previousKind == ZLTextElement::WORD_ELEMENT)) &&
					(elementKind != ZLTextElement::IMAGE_ELEMENT) &&
					(elementKind != ZLTextElement::CONTROL_ELEMENT);
			}
		}
		if (allowBreak) {
			newInfo.setTo(info);
			allowBreakAtNBSpace = nbspaceBreak;
			storedStyle = myStyle.textStyle();
			removeLastSpace = !wordOccured && (info.SpaceCounter > 0);
		}
	} while (!newInfo.End.equalWordNumber(end));

	if (!newInfo.End.equalWordNumber(end) && useHyphenator &&
		 myStyle.textStyle()->allowHyphenations()) {
		const ZLTextElement &element = paragraphCursor[newInfo.End.wordNumber()];
		if (element.kind() == ZLTextElement::WORD_ELEMENT) {
			const int startCharNumber = newInfo.End.charNumber();
			newInfo.Width -= myStyle.elementWidth(element, startCharNumber);
			const ZLTextWord &word = (ZLTextWord&)element;
			int spaceLeft = maxWidth - newInfo.Width;
			if (breakedAtFirstWord ||
					((word.Length > 3) && (spaceLeft > 2 * myStyle.context().spaceWidth()))) {
				ZLUnicodeUtil::Ucs2String ucs2string;
				ZLUnicodeUtil::utf8ToUcs2(ucs2string, word.Data, word.Size);
				ZLTextHyphenationInfo hyphenationInfo = ZLTextHyphenator::instance().info(word);
				int hyphenationPosition = word.Length - 1;
				int subwordWidth = 0;
				for (; hyphenationPosition > startCharNumber; --hyphenationPosition) {
					if (hyphenationInfo.isHyphenationPossible(hyphenationPosition)) {
						subwordWidth = myStyle.wordWidth(word, startCharNumber, hyphenationPosition - startCharNumber, ucs2string[hyphenationPosition - 1] != '-');
						if (subwordWidth <= spaceLeft) {
							break;
						}
					}
				}
				if ((hyphenationPosition == startCharNumber) &&
						(info.End.wordNumber() <= info.RealStart.wordNumber())) {
					hyphenationPosition = word.Length;
					subwordWidth = myStyle.elementWidth(element, startCharNumber);
				}
				if (hyphenationPosition > startCharNumber) {
					newInfo.Width += subwordWidth;
					newInfo.setTo(info);
					storedStyle = myStyle.textStyle();
					removeLastSpace = false;
					info.End.setCharNumber(hyphenationPosition);
				}
			}
		}
	}

	if (removeLastSpace) {
		info.Width -= lastSpaceWidth;
		--info.SpaceCounter;
	}

	myStyle.setTextStyle(storedStyle);

	if (isFirstLine) {
		info.Height += info.StartStyle->spaceBefore();
	}
	if (info.End.isEndOfParagraph()) {
		info.VSpaceAfter = myStyle.textStyle()->spaceAfter();
	}

	if (!info.End.equalWordNumber(end) || end.isEndOfParagraph()) {
		myLineInfoCache.insert(infoPtr);
	}

	return infoPtr;
}
Esempio n. 14
0
bool ZLTextSelectionModel::selectWord(const ZLTextElementArea & area, int x, int y)
{
    if (ZLTextElementArea::RangeChecker(x, y)(area)) {
        int startIndex = 0;
        int endIndex = 1;
        switch (area.Kind) {
            default:
                return false;
            case ZLTextElement::IMAGE_ELEMENT:
                break;
            case ZLTextElement::WORD_ELEMENT:
                {
                    ZLTextWordCursor cursor = myView.startCursor();
                    cursor.moveToParagraph(area.ParagraphIndex);
                    const ZLTextWord &word = (const ZLTextWord&)cursor.paragraphCursor()[area.ElementIndex];
                    ZLUnicodeUtil::Ucs4String ucs4string;
                    ZLUnicodeUtil::utf8ToUcs4(ucs4string, word.Data, word.Size);
                    startIndex = charIndex(area, x);
                    if (startIndex == word.Length) {
                        --startIndex;
                    }
                    endIndex = startIndex + 1;
                    ZLUnicodeUtil::Ucs4Char ch = ucs4string[startIndex];

                    // TODO: Get rid of non-letter character.
                    while (!ZLUnicodeUtil::isLetter(ch) && ++startIndex < word.Length)
                    {
                        ch = ucs4string[startIndex];
                    }
                    if (startIndex >= word.Length)
                    {
                        break;
                    }

                    if (ZLUnicodeUtil::isLetter(ch) || (('0' <= ch) && (ch <= '9'))) {
                        while (--startIndex >= 0) {
                            ch = ucs4string[startIndex];
                            if (!ZLUnicodeUtil::isLetter(ch) && ((ch < '0') || (ch > '9'))) {
                                break;
                            }
                        }
                        ++startIndex;
                        while (++endIndex <= word.Length) {
                            ch = ucs4string[endIndex - 1];
                            if (!ZLUnicodeUtil::isLetter(ch) && ((ch < '0') || (ch > '9'))) {
                                break;
                            }
                        }
                        --endIndex;
                    }
                }
        }

        myFirstBound.Before.Exists = true;
        myFirstBound.Before.ParagraphIndex = area.ParagraphIndex;
        myFirstBound.Before.ElementIndex = area.ElementIndex;
        myFirstBound.Before.CharIndex = startIndex;
        myFirstBound.After = myFirstBound.Before;

        mySecondBound.Before = myFirstBound.Before;
        mySecondBound.Before.CharIndex = endIndex;
        mySecondBound.After = mySecondBound.Before;

        myIsEmpty = false;
        myTextIsUpToDate = false;
        myRangeVectorIsUpToDate = false;
        myDoUpdate = false;

        myArea = area;

        return true;
    }
    return false;
}