1
0
Fork 0
mirror of https://github.com/geometer/FBReaderJ.git synced 2025-10-03 09:49:19 +02:00

new style selection & selection cursors

This commit is contained in:
Nikolay Pultsin 2015-06-11 20:31:08 +01:00
parent ad6faeb64f
commit 943b01dc5d
23 changed files with 696 additions and 436 deletions

View file

@ -8,6 +8,7 @@
* ComicBook plugin icon
* Updated Armenian localisation (by Yavruhrat, Marat Yavrumyan)
* Added Korean intro text
* New style selection rendering
===== 2.4.7 (May 20, 2015) =====
* Added Korean localisation

View file

@ -151,8 +151,17 @@ class ProcessHyperlinkAction extends FBAndroidAction {
}
}
} else if (soul instanceof ZLTextWordRegionSoul) {
DictionaryUtil.openWordInDictionary(
BaseActivity, ((ZLTextWordRegionSoul)soul).Word, region
DictionaryUtil.openTextInDictionary(
BaseActivity,
((ZLTextWordRegionSoul)soul).Word.getString(),
true,
region.getTop(),
region.getBottom(),
new Runnable() {
public void run() {
BaseActivity.outlineRegion(soul);
}
}
);
}
}

View file

@ -19,8 +19,7 @@
package org.geometerplus.android.fbreader;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
import org.geometerplus.fbreader.fbreader.FBView;
import org.geometerplus.fbreader.fbreader.*;
import org.geometerplus.android.fbreader.dict.DictionaryUtil;
public class SelectionTranslateAction extends FBAndroidAction {
@ -31,13 +30,19 @@ public class SelectionTranslateAction extends FBAndroidAction {
@Override
protected void run(Object ... params) {
final FBView fbview = Reader.getTextView();
final DictionaryHighlighting dictionaryHilite = new DictionaryHighlighting(fbview);
DictionaryUtil.openTextInDictionary(
BaseActivity,
fbview.getSelectedSnippet().getText(),
fbview.getCountOfSelectedWords() == 1,
fbview.getSelectionStartY(),
fbview.getSelectionEndY(),
fbview.getSelectionSoul()
new Runnable() {
public void run() {
fbview.addHighlighting(dictionaryHilite);
Reader.getViewWidget().repaint();
}
}
);
fbview.clearSelection();
}

View file

@ -51,7 +51,6 @@ import org.geometerplus.zlibrary.core.options.ZLStringOption;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.zlibrary.core.util.XmlUtil;
import org.geometerplus.zlibrary.text.view.ZLTextRegion;
import org.geometerplus.zlibrary.text.view.ZLTextWord;
import org.geometerplus.zlibrary.ui.android.library.ZLAndroidLibrary;
@ -122,7 +121,7 @@ public abstract class DictionaryUtil {
}
}
abstract void open(String text, ZLTextRegion.Soul soulToSelect, FBReader fbreader, PopupFrameMetric frameMetrics);
abstract void open(String text, Runnable outliner, FBReader fbreader, PopupFrameMetric frameMetrics);
}
private static class PlainPackageInfo extends PackageInfo {
@ -131,7 +130,7 @@ public abstract class DictionaryUtil {
}
@Override
void open(String text, ZLTextRegion.Soul soulToSelect, FBReader fbreader, PopupFrameMetric frameMetrics) {
void open(String text, Runnable outliner, FBReader fbreader, PopupFrameMetric frameMetrics) {
final Intent intent = getDictionaryIntent(text);
try {
final String id = getId();
@ -167,7 +166,7 @@ public abstract class DictionaryUtil {
}
@Override
void open(String text, ZLTextRegion.Soul soulToSelect, FBReader fbreader, PopupFrameMetric frameMetrics) {
void open(String text, Runnable outliner, FBReader fbreader, PopupFrameMetric frameMetrics) {
final Intent intent = getDictionaryIntent(text);
try {
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
@ -176,7 +175,9 @@ public abstract class DictionaryUtil {
intent.putExtra("article.text.size.max", MAX_LENGTH_FOR_TOAST);
fbreader.startActivityForResult(intent, FBReader.REQUEST_DICTIONARY);
fbreader.overridePendingTransition(0, 0);
fbreader.outlineRegion(soulToSelect);
if (outliner != null) {
outliner.run();
}
} catch (ActivityNotFoundException e) {
fbreader.hideOutline();
installDictionaryIfNotInstalled(fbreader, this);
@ -199,7 +200,7 @@ public abstract class DictionaryUtil {
}
@Override
void open(String text, ZLTextRegion.Soul soulToSelect, FBReader fbreader, PopupFrameMetric frameMetrics) {
void open(String text, Runnable outliner, FBReader fbreader, PopupFrameMetric frameMetrics) {
Flyout.showTranslation(fbreader, text, frameMetrics);
}
}
@ -455,7 +456,7 @@ public abstract class DictionaryUtil {
}
}
public static void openTextInDictionary(final FBReader fbreader, String text, boolean singleWord, int selectionTop, int selectionBottom, final ZLTextRegion.Soul soulToSelect) {
public static void openTextInDictionary(final FBReader fbreader, String text, boolean singleWord, int selectionTop, int selectionBottom, final Runnable outliner) {
final String textToTranslate;
if (singleWord) {
int start = 0;
@ -478,17 +479,11 @@ public abstract class DictionaryUtil {
final PackageInfo info = getCurrentDictionaryInfo(singleWord);
fbreader.runOnUiThread(new Runnable() {
public void run() {
info.open(textToTranslate, soulToSelect, fbreader, frameMetrics);
info.open(textToTranslate, outliner, fbreader, frameMetrics);
}
});
}
public static void openWordInDictionary(FBReader fbreader, ZLTextWord word, ZLTextRegion region) {
openTextInDictionary(
fbreader, word.toString(), true, region.getTop(), region.getBottom(), region.getSoul()
);
}
public static void installDictionaryIfNotInstalled(final Activity activity, final PackageInfo info) {
if (PackageUtil.canBeStarted(activity, info.getDictionaryIntent("test"), false)) {
return;

View file

@ -59,4 +59,9 @@ public final class BookmarkHighlighting extends ZLTextSimpleHighlighting {
final HighlightingStyle bmStyle = Collection.getHighlightingStyle(Bookmark.getStyleId());
return bmStyle != null ? bmStyle.getForegroundColor() : null;
}
@Override
public ZLColor getOutlineColor() {
return null;
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (C) 2007-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.fbreader.fbreader;
import org.geometerplus.zlibrary.core.util.ZLColor;
import org.geometerplus.zlibrary.text.view.*;
public final class DictionaryHighlighting extends ZLTextSimpleHighlighting {
public DictionaryHighlighting(ZLTextView view) {
this(view, view.getSelectionHighlighting());
}
private DictionaryHighlighting(ZLTextView view, ZLTextHighlighting selection) {
super(view, selection.getStartPosition(), selection.getEndPosition());
}
@Override
public ZLColor getBackgroundColor() {
return View.getSelectionBackgroundColor();
}
@Override
public ZLColor getForegroundColor() {
return null;
}
@Override
public ZLColor getOutlineColor() {
return null;
}
}

View file

@ -26,6 +26,7 @@ import org.geometerplus.zlibrary.core.filesystem.ZLResourceFile;
import org.geometerplus.zlibrary.core.fonts.FontEntry;
import org.geometerplus.zlibrary.core.library.ZLibrary;
import org.geometerplus.zlibrary.core.util.ZLColor;
import org.geometerplus.zlibrary.core.view.SelectionCursor;
import org.geometerplus.zlibrary.core.view.ZLPaintContext;
import org.geometerplus.zlibrary.text.model.ZLTextModel;
@ -158,10 +159,10 @@ public final class FBView extends ZLTextView {
return true;
}
final ZLTextSelectionCursor cursor = findSelectionCursor(x, y, maxSelectionDistance());
if (cursor != ZLTextSelectionCursor.None) {
final SelectionCursor.Which cursor = findSelectionCursor(x, y, maxSelectionDistance());
if (cursor != null) {
myReader.runAction(ActionCode.SELECTION_HIDE_PANEL);
moveSelectionCursorTo(cursor, x, y, true);
moveSelectionCursorTo(cursor, x, y);
return true;
}
@ -199,9 +200,9 @@ public final class FBView extends ZLTextView {
return true;
}
final ZLTextSelectionCursor cursor = getSelectionCursorInMovement();
if (cursor != ZLTextSelectionCursor.None) {
moveSelectionCursorTo(cursor, x, y, true);
final SelectionCursor.Which cursor = getSelectionCursorInMovement();
if (cursor != null) {
moveSelectionCursorTo(cursor, x, y);
return true;
}
@ -229,8 +230,8 @@ public final class FBView extends ZLTextView {
return true;
}
final ZLTextSelectionCursor cursor = getSelectionCursorInMovement();
if (cursor != ZLTextSelectionCursor.None) {
final SelectionCursor.Which cursor = getSelectionCursorInMovement();
if (cursor != null) {
releaseSelectionCursor();
return true;
}
@ -266,9 +267,9 @@ public final class FBView extends ZLTextView {
case startSelecting:
myReader.runAction(ActionCode.SELECTION_HIDE_PANEL);
initSelection(x, y);
final ZLTextSelectionCursor cursor = findSelectionCursor(x, y);
if (cursor != ZLTextSelectionCursor.None) {
moveSelectionCursorTo(cursor, x, y, false);
final SelectionCursor.Which cursor = findSelectionCursor(x, y);
if (cursor != null) {
moveSelectionCursorTo(cursor, x, y);
}
return true;
case selectSingleWord:
@ -300,9 +301,9 @@ public final class FBView extends ZLTextView {
return true;
}
final ZLTextSelectionCursor cursor = getSelectionCursorInMovement();
if (cursor != ZLTextSelectionCursor.None) {
moveSelectionCursorTo(cursor, x, y, true);
final SelectionCursor.Which cursor = getSelectionCursorInMovement();
if (cursor != null) {
moveSelectionCursorTo(cursor, x, y);
return true;
}
@ -334,8 +335,8 @@ public final class FBView extends ZLTextView {
return true;
}
final ZLTextSelectionCursor cursor = getSelectionCursorInMovement();
if (cursor != ZLTextSelectionCursor.None) {
final SelectionCursor.Which cursor = getSelectionCursorInMovement();
if (cursor != null) {
releaseSelectionCursor();
return true;
}

View file

@ -0,0 +1,203 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.zlibrary.core.view;
import java.util.*;
import android.graphics.Rect;
public final class HorizontalConvexHull implements Hull {
private final LinkedList<Rect> myRectangles = new LinkedList<Rect>();
public HorizontalConvexHull(Collection<Rect> rects) {
for (Rect r : rects) {
addRect(r);
}
normalize();
}
private void addRect(Rect rectangle) {
if (myRectangles.isEmpty()) {
myRectangles.add(new Rect(rectangle));
return;
}
final int top = rectangle.top;
final int bottom = rectangle.bottom;
for (ListIterator<Rect> iter = myRectangles.listIterator(); iter.hasNext(); ) {
Rect r = iter.next();
if (r.bottom <= top) {
continue;
}
if (r.top >= bottom) {
break;
}
if (r.top < top) {
final Rect before = new Rect(r);
before.bottom = top;
r.top = top;
iter.previous();
iter.add(before);
iter.next();
}
if (r.bottom > bottom) {
final Rect after = new Rect(r);
after.top = bottom;
r.bottom = bottom;
iter.add(after);
}
r.left = Math.min(r.left, rectangle.left);
r.right = Math.max(r.right, rectangle.right);
}
final Rect first = myRectangles.getFirst();
if (top < first.top) {
myRectangles.add(0, new Rect(rectangle.left, top, rectangle.right, Math.min(bottom, first.top)));
}
final Rect last = myRectangles.getLast();
if (bottom > last.bottom) {
myRectangles.add(new Rect(rectangle.left, Math.max(top, last.bottom), rectangle.right, bottom));
}
}
private void normalize() {
Rect previous = null;
for (ListIterator<Rect> iter = myRectangles.listIterator(); iter.hasNext(); ) {
final Rect current = iter.next();
if (previous != null) {
if ((previous.left == current.left) && (previous.right == current.right)) {
previous.bottom = current.bottom;
iter.remove();
continue;
}
if ((previous.bottom != current.top) &&
(current.left <= previous.right) &&
(previous.left <= current.right)) {
iter.previous();
iter.add(new Rect(
Math.max(previous.left, current.left),
previous.bottom,
Math.min(previous.right, current.right),
current.top
));
iter.next();
}
}
previous = current;
}
}
public int distanceTo(int x, int y) {
int distance = Integer.MAX_VALUE;
for (Rect r : myRectangles) {
final int xd = r.left > x ? r.left - x : (r.right < x ? x - r.right : 0);
final int yd = r.top > y ? r.top - y : (r.bottom < y ? y - r.bottom : 0);
distance = Math.min(distance, Math.max(xd, yd));
if (distance == 0) {
break;
}
}
return distance;
}
public boolean isBefore(int x, int y) {
for (Rect r : myRectangles) {
if (r.bottom < y || (r.top < y && r.right < x)) {
return true;
}
}
return false;
}
public void draw(ZLPaintContext context, int mode) {
if (mode == DrawMode.None) {
return;
}
final LinkedList<Rect> rectangles = new LinkedList<Rect>(myRectangles);
while (!rectangles.isEmpty()) {
final LinkedList<Rect> connected = new LinkedList<Rect>();
Rect previous = null;
for (final Iterator<Rect> iter = rectangles.iterator(); iter.hasNext(); ) {
final Rect current = iter.next();
if ((previous != null) &&
((previous.left > current.right) || (current.left > previous.right))) {
break;
}
iter.remove();
connected.add(current);
previous = current;
}
final LinkedList<Integer> xList = new LinkedList<Integer>();
final LinkedList<Integer> yList = new LinkedList<Integer>();
int x = 0, xPrev = 0;
final ListIterator<Rect> iter = connected.listIterator();
Rect r = iter.next();
x = r.right + 2;
xList.add(x); yList.add(r.top);
while (iter.hasNext()) {
xPrev = x;
r = iter.next();
x = r.right + 2;
if (x != xPrev) {
final int y = (x < xPrev) ? r.top + 2 : r.top;
xList.add(xPrev); yList.add(y);
xList.add(x); yList.add(y);
}
}
xList.add(x); yList.add(r.bottom + 2);
r = iter.previous();
x = r.left - 2;
xList.add(x); yList.add(r.bottom + 2);
while (iter.hasPrevious()) {
xPrev = x;
r = iter.previous();
x = r.left - 2;
if (x != xPrev) {
final int y = (x > xPrev) ? r.bottom : r.bottom + 2;
xList.add(xPrev); yList.add(y);
xList.add(x); yList.add(y);
}
}
xList.add(x); yList.add(r.top);
final int xs[] = new int[xList.size()];
final int ys[] = new int[yList.size()];
int count = 0;
for (int xx : xList) {
xs[count++] = xx;
}
count = 0;
for (int yy : yList) {
ys[count++] = yy;
}
if ((mode & DrawMode.Fill) == DrawMode.Fill) {
context.fillPolygon(xs, ys);
}
if ((mode & DrawMode.Outline) == DrawMode.Outline) {
context.drawOutline(xs, ys);
}
}
}
}

View file

@ -0,0 +1,32 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.zlibrary.core.view;
public interface Hull {
interface DrawMode {
int None = 0;
int Outline = 1;
int Fill = 2;
};
void draw(ZLPaintContext context, int mode);
int distanceTo(int x, int y);
boolean isBefore(int x, int y);
}

View file

@ -17,40 +17,27 @@
* 02110-1301, USA.
*/
package org.geometerplus.zlibrary.text.view;
package org.geometerplus.zlibrary.core.view;
import org.geometerplus.zlibrary.core.library.ZLibrary;
import org.geometerplus.zlibrary.core.util.ZLColor;
public enum ZLTextSelectionCursor {
None,
Left,
Right;
public abstract class SelectionCursor {
public enum Which {
Left,
Right
}
private static int ourHeight;
private static int ourWidth;
private static int ourAccent;
private static void init() {
if (ourHeight == 0) {
final int dpi = ZLibrary.Instance().getDisplayDPI();
ourAccent = dpi / 12;
ourWidth = dpi / 6;
ourHeight = dpi / 4;
public static void draw(ZLPaintContext context, Which which, int x, int y, ZLColor color) {
context.setFillColor(color);
final int dpi = ZLibrary.Instance().getDisplayDPI();
final int unit = dpi / 120;
final int xCenter = which == Which.Left ? x - unit - 1 : x + unit + 1;
context.fillRectangle(xCenter - unit, y + dpi / 8, xCenter + unit, y - dpi / 8);
if (which == Which.Left) {
context.fillCircle(xCenter, y - dpi / 8, unit * 6);
} else {
context.fillCircle(xCenter, y + dpi / 8, unit * 6);
}
}
static int getHeight() {
init();
return ourHeight;
}
static int getWidth() {
init();
return ourWidth;
}
static int getAccent() {
init();
return ourAccent;
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.zlibrary.core.view;
import java.util.*;
public class UnionHull implements Hull {
private final List<Hull> myComponents;
public UnionHull(Hull ... components) {
myComponents = new ArrayList<Hull>(Arrays.asList(components));
}
public void draw(ZLPaintContext context, int mode) {
for (Hull h : myComponents) {
h.draw(context, mode);
}
}
public int distanceTo(int x, int y) {
int dist = Integer.MAX_VALUE;
for (Hull h : myComponents) {
dist = Math.min(dist, h.distanceTo(x, y));
}
return dist;
}
public boolean isBefore(int x, int y) {
for (Hull h : myComponents) {
if (h.isBefore(x, y)) {
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.zlibrary.text.view;
import java.util.*;
import android.graphics.Rect;
import org.geometerplus.zlibrary.core.view.*;
abstract class HullUtil {
static Hull hull(ZLTextElementArea[] areas) {
return hull(Arrays.asList(areas));
}
static Hull hull(List<ZLTextElementArea> areas) {
final List<Rect> rectangles0 = new ArrayList<Rect>(areas.size());
final List<Rect> rectangles1 = new ArrayList<Rect>(areas.size());
for (ZLTextElementArea a : areas) {
final Rect rect = new Rect(a.XStart, a.YStart, a.XEnd, a.YEnd);
if (a.ColumnIndex == 0) {
rectangles0.add(rect);
} else {
rectangles1.add(rect);
}
}
if (rectangles0.isEmpty()) {
return new HorizontalConvexHull(rectangles1);
} else if (rectangles1.isEmpty()) {
return new HorizontalConvexHull(rectangles0);
} else {
return new UnionHull(
new HorizontalConvexHull(rectangles0),
new HorizontalConvexHull(rectangles1)
);
}
}
}

View file

@ -24,6 +24,7 @@ public final class ZLTextElementArea extends ZLTextFixedPosition {
public final int XEnd;
public final int YStart;
public final int YEnd;
public final int ColumnIndex;
final int Length;
final boolean AddHyphenationSign;
@ -33,13 +34,14 @@ public final class ZLTextElementArea extends ZLTextFixedPosition {
private final boolean myIsLastInElement;
ZLTextElementArea(int paragraphIndex, int elementIndex, int charIndex, int length, boolean lastInElement, boolean addHyphenationSign, boolean changeStyle, ZLTextStyle style, ZLTextElement element, int xStart, int xEnd, int yStart, int yEnd) {
ZLTextElementArea(int paragraphIndex, int elementIndex, int charIndex, int length, boolean lastInElement, boolean addHyphenationSign, boolean changeStyle, ZLTextStyle style, ZLTextElement element, int xStart, int xEnd, int yStart, int yEnd, int columnIndex) {
super(paragraphIndex, elementIndex, charIndex);
XStart = xStart;
XEnd = xEnd;
YStart = yStart;
YEnd = yEnd;
ColumnIndex = columnIndex;
Length = length;
myIsLastInElement = lastInElement;

View file

@ -38,9 +38,10 @@ final class ZLTextElementAreaVector {
return myAreas.size();
}
// TODO: remove this unsafe method
public ZLTextElementArea get(int index) {
return myAreas.get(index);
public List<ZLTextElementArea> areas() {
synchronized (myAreas) {
return new ArrayList<ZLTextElementArea>(myAreas);
}
}
public ZLTextElementArea getFirstArea() {
@ -168,6 +169,28 @@ final class ZLTextElementAreaVector {
return bestRegion;
}
static class RegionPair {
ZLTextRegion Before;
ZLTextRegion After;
}
RegionPair findRegionsPair(int x, int y, int columnIndex, ZLTextRegion.Filter filter) {
RegionPair pair = new RegionPair();
synchronized (myElementRegions) {
for (ZLTextRegion region : myElementRegions) {
if (filter.accepts(region)) {
if (region.isBefore(x, y, columnIndex)) {
pair.Before = region;
} else {
pair.After = region;
break;
}
}
}
}
return pair;
}
protected ZLTextRegion nextRegion(ZLTextRegion currentRegion, ZLTextView.Direction direction, ZLTextRegion.Filter filter) {
synchronized (myElementRegions) {
if (myElementRegions.isEmpty()) {

View file

@ -19,7 +19,10 @@
package org.geometerplus.zlibrary.text.view;
import java.util.List;
import org.geometerplus.zlibrary.core.util.ZLColor;
import org.geometerplus.zlibrary.core.view.Hull;
public abstract class ZLTextHighlighting implements Comparable<ZLTextHighlighting> {
public abstract boolean isEmpty();
@ -31,6 +34,7 @@ public abstract class ZLTextHighlighting implements Comparable<ZLTextHighlightin
public abstract ZLColor getForegroundColor();
public abstract ZLColor getBackgroundColor();
public abstract ZLColor getOutlineColor();
boolean intersects(ZLTextPage page) {
return
@ -48,6 +52,24 @@ public abstract class ZLTextHighlighting implements Comparable<ZLTextHighlightin
soul.compareTo(getEndPosition()) <= 0;
}
final Hull hull(ZLTextPage page) {
final ZLTextPosition startPosition = getStartPosition();
final ZLTextPosition endPosition = getEndPosition();
final List<ZLTextElementArea> areas = page.TextElementMap.areas();
int startIndex = 0;
int endIndex = 0;
for (int i = 0; i < areas.size(); ++i) {
final ZLTextElementArea a = areas.get(i);
if (i == startIndex && startPosition.compareTo(a) > 0) {
++startIndex;
} else if (endPosition.compareTo(a) < 0) {
break;
}
++endIndex;
}
return HullUtil.hull(areas.subList(startIndex, endIndex));
}
public int compareTo(ZLTextHighlighting highlighting) {
final int cmp = getStartPosition().compareTo(highlighting.getStartPosition());
return cmp != 0 ? cmp : getEndPosition().compareTo(highlighting.getEndPosition());

View file

@ -1,205 +0,0 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.zlibrary.text.view;
import java.util.*;
import org.geometerplus.zlibrary.core.view.ZLPaintContext;
class ZLTextHorizontalConvexHull {
private final LinkedList<Rectangle> myRectangles = new LinkedList<Rectangle>();
ZLTextHorizontalConvexHull(ZLTextElementArea[] textAreas) {
for (ZLTextElementArea area : textAreas) {
addArea(area);
}
normalize();
}
private void addArea(ZLTextElementArea area) {
if (myRectangles.isEmpty()) {
myRectangles.add(new Rectangle(area.XStart, area.XEnd, area.YStart, area.YEnd));
return;
}
final int top = area.YStart;
final int bottom = area.YEnd;
for (ListIterator<Rectangle> iter = myRectangles.listIterator(); iter.hasNext(); ) {
Rectangle r = iter.next();
if (r.Bottom <= top) {
continue;
}
if (r.Top >= bottom) {
break;
}
if (r.Top < top) {
final Rectangle before = new Rectangle(r);
before.Bottom = top;
r.Top = top;
iter.previous();
iter.add(before);
iter.next();
}
if (r.Bottom > bottom) {
final Rectangle after = new Rectangle(r);
after.Top = bottom;
r.Bottom = bottom;
iter.add(after);
}
r.Left = Math.min(r.Left, area.XStart);
r.Right = Math.max(r.Right, area.XEnd);
}
final Rectangle first = myRectangles.getFirst();
if (top < first.Top) {
myRectangles.add(0, new Rectangle(area.XStart, area.XEnd, top, Math.min(bottom, first.Top)));
}
final Rectangle last = myRectangles.getLast();
if (bottom > last.Bottom) {
myRectangles.add(new Rectangle(area.XStart, area.XEnd, Math.max(top, last.Bottom), bottom));
}
}
private void normalize() {
Rectangle previous = null;
for (ListIterator<Rectangle> iter = myRectangles.listIterator(); iter.hasNext(); ) {
final Rectangle current = iter.next();
if (previous != null) {
if ((previous.Left == current.Left) && (previous.Right == current.Right)) {
previous.Bottom = current.Bottom;
iter.remove();
continue;
}
if ((previous.Bottom != current.Top) &&
(current.Left <= previous.Right) &&
(previous.Left <= current.Right)) {
iter.previous();
iter.add(new Rectangle(
Math.max(previous.Left, current.Left),
Math.min(previous.Right, current.Right),
previous.Bottom,
current.Top
));
iter.next();
}
}
previous = current;
}
}
int distanceTo(int x, int y) {
int distance = Integer.MAX_VALUE;
for (Rectangle r : myRectangles) {
final int xd = (r.Left > x) ? r.Left - x : ((r.Right < x) ? x - r.Right : 0);
final int yd = (r.Top > y) ? r.Top - y : ((r.Bottom < y) ? y - r.Bottom : 0);
distance = Math.min(distance, Math.max(xd, yd));
if (distance == 0) {
break;
}
}
return distance;
}
void draw(ZLPaintContext context) {
final LinkedList<Rectangle> rectangles = new LinkedList<Rectangle>(myRectangles);
while (!rectangles.isEmpty()) {
final LinkedList<Rectangle> connected = new LinkedList<Rectangle>();
Rectangle previous = null;
for (final Iterator<Rectangle> iter = rectangles.iterator(); iter.hasNext(); ) {
final Rectangle current = iter.next();
if ((previous != null) &&
((previous.Left > current.Right) || (current.Left > previous.Right))) {
break;
}
iter.remove();
connected.add(current);
previous = current;
}
final LinkedList<Integer> xList = new LinkedList<Integer>();
final LinkedList<Integer> yList = new LinkedList<Integer>();
int x = 0, xPrev = 0;
final ListIterator<Rectangle> iter = connected.listIterator();
Rectangle r = iter.next();
x = r.Right + 2;
xList.add(x); yList.add(r.Top);
while (iter.hasNext()) {
xPrev = x;
r = iter.next();
x = r.Right + 2;
if (x != xPrev) {
final int y = (x < xPrev) ? r.Top + 2 : r.Top;
xList.add(xPrev); yList.add(y);
xList.add(x); yList.add(y);
}
}
xList.add(x); yList.add(r.Bottom + 2);
r = iter.previous();
x = r.Left - 2;
xList.add(x); yList.add(r.Bottom + 2);
while (iter.hasPrevious()) {
xPrev = x;
r = iter.previous();
x = r.Left - 2;
if (x != xPrev) {
final int y = (x > xPrev) ? r.Bottom : r.Bottom + 2;
xList.add(xPrev); yList.add(y);
xList.add(x); yList.add(y);
}
}
xList.add(x); yList.add(r.Top);
final int xs[] = new int[xList.size()];
final int ys[] = new int[yList.size()];
int count = 0;
for (int xx : xList) {
xs[count++] = xx;
}
count = 0;
for (int yy : yList) {
ys[count++] = yy;
}
context.drawOutline(xs, ys);
}
}
private static final class Rectangle {
int Left;
int Right;
int Top;
int Bottom;
Rectangle(int left, int right, int top, int bottom) {
Left = left;
Right = right;
Top = top;
Bottom = bottom;
}
Rectangle(Rectangle orig) {
Left = orig.Left;
Right = orig.Right;
Top = orig.Top;
Bottom = orig.Bottom;
}
}
}

View file

@ -35,4 +35,9 @@ class ZLTextManualHighlighting extends ZLTextSimpleHighlighting {
public ZLColor getForegroundColor() {
return View.getHighlightingForegroundColor();
}
@Override
public ZLColor getOutlineColor() {
return null;
}
}

View file

@ -21,6 +21,7 @@ package org.geometerplus.zlibrary.text.view;
import java.util.*;
import org.geometerplus.zlibrary.core.view.Hull;
import org.geometerplus.zlibrary.core.view.ZLPaintContext;
public final class ZLTextRegion {
@ -139,7 +140,8 @@ public final class ZLTextRegion {
private ZLTextElementArea[] myAreas;
private final int myFromIndex;
private int myToIndex;
private ZLTextHorizontalConvexHull myHull;
private Hull myHull;
private Hull myHull0; // convex hull for left page column
ZLTextRegion(Soul soul, List<ZLTextElementArea> list, int fromIndex) {
mySoul = soul;
@ -157,7 +159,7 @@ public final class ZLTextRegion {
return mySoul;
}
private ZLTextElementArea[] textAreas() {
ZLTextElementArea[] textAreas() {
if (myAreas == null || myAreas.length != myToIndex - myFromIndex) {
synchronized (myAreaList) {
myAreas = new ZLTextElementArea[myToIndex - myFromIndex];
@ -168,12 +170,24 @@ public final class ZLTextRegion {
}
return myAreas;
}
private ZLTextHorizontalConvexHull convexHull() {
Hull hull() {
if (myHull == null) {
myHull = new ZLTextHorizontalConvexHull(textAreas());
myHull = HullUtil.hull(textAreas());
}
return myHull;
}
Hull hull0() {
if (myHull0 == null) {
final List<ZLTextElementArea> column0 = new ArrayList<ZLTextElementArea>();
for (ZLTextElementArea a : textAreas()) {
if (a.ColumnIndex == 0) {
column0.add(a);
}
}
myHull0 = HullUtil.hull(column0);
}
return myHull0;
}
ZLTextElementArea getFirstArea() {
return textAreas()[0];
@ -208,12 +222,42 @@ public final class ZLTextRegion {
return getLastArea().YEnd;
}
void draw(ZLPaintContext context) {
convexHull().draw(context);
int distanceTo(int x, int y) {
return hull().distanceTo(x, y);
}
int distanceTo(int x, int y) {
return convexHull().distanceTo(x, y);
boolean isBefore(int x, int y, int columnIndex) {
switch (columnIndex) {
default:
case -1:
return hull().isBefore(x, y);
case 0:
{
int count0 = 0;
int count1 = 0;
for (ZLTextElementArea area : textAreas()) {
if (area.ColumnIndex == 0) {
++count0;
} else {
++count1;
}
}
if (count0 == 0) {
return false;
} else if (count1 == 0) {
return hull().isBefore(x, y);
} else {
return hull0().isBefore(x, y);
}
}
case 1:
for (ZLTextElementArea area : textAreas()) {
if (area.ColumnIndex == 0) {
return true;
}
}
return hull().isBefore(x, y);
}
}
boolean isAtRightOf(ZLTextRegion other) {

View file

@ -20,6 +20,7 @@
package org.geometerplus.zlibrary.text.view;
import org.geometerplus.zlibrary.core.util.ZLColor;
import org.geometerplus.zlibrary.core.view.SelectionCursor;
class ZLTextSelection extends ZLTextHighlighting {
static class Point {
@ -37,7 +38,7 @@ class ZLTextSelection extends ZLTextHighlighting {
private ZLTextRegion.Soul myLeftMostRegionSoul;
private ZLTextRegion.Soul myRightMostRegionSoul;
private ZLTextSelectionCursor myCursorInMovement = ZLTextSelectionCursor.None;
private SelectionCursor.Which myCursorInMovement = null;
private final Point myCursorInMovementPoint = new Point(-1, -1);
private Scroller myScroller;
@ -46,10 +47,6 @@ class ZLTextSelection extends ZLTextHighlighting {
myView = view;
}
ZLTextRegion.Soul getSoul() {
return myLeftMostRegionSoul != null && myLeftMostRegionSoul.equals(myRightMostRegionSoul) ? myLeftMostRegionSoul : null;
}
@Override
public boolean isEmpty() {
return myLeftMostRegionSoul == null;
@ -63,17 +60,17 @@ class ZLTextSelection extends ZLTextHighlighting {
stop();
myLeftMostRegionSoul = null;
myRightMostRegionSoul = null;
myCursorInMovement = ZLTextSelectionCursor.None;
myCursorInMovement = null;
return true;
}
void setCursorInMovement(ZLTextSelectionCursor cursor, int x, int y) {
myCursorInMovement = cursor;
void setCursorInMovement(SelectionCursor.Which which, int x, int y) {
myCursorInMovement = which;
myCursorInMovementPoint.X = x;
myCursorInMovementPoint.Y = y;
}
ZLTextSelectionCursor getCursorInMovement() {
SelectionCursor.Which getCursorInMovement() {
return myCursorInMovement;
}
@ -96,7 +93,7 @@ class ZLTextSelection extends ZLTextHighlighting {
}
void stop() {
myCursorInMovement = ZLTextSelectionCursor.None;
myCursorInMovement = null;
if (myScroller != null) {
myScroller.stop();
myScroller = null;
@ -120,7 +117,8 @@ class ZLTextSelection extends ZLTextHighlighting {
myScroller = new Scroller(page, false, x, y);
return;
}
} else if (lastArea != null && y + ZLTextSelectionCursor.getHeight() / 2 + ZLTextSelectionCursor.getAccent() / 2 > lastArea.YEnd) {
//} else if (lastArea != null && y + ZLTextSelectionCursor.getHeight() / 2 + ZLTextSelectionCursor.getAccent() / 2 > lastArea.YEnd) {
} else if (lastArea != null && y > lastArea.YEnd) {
if (myScroller != null && !myScroller.scrollsForward()) {
myScroller.stop();
myScroller = null;
@ -141,21 +139,40 @@ class ZLTextSelection extends ZLTextHighlighting {
}
ZLTextRegion region = myView.findRegion(x, y, myView.maxSelectionDistance(), ZLTextRegion.AnyRegionFilter);
if (region == null && myScroller != null) {
region = myView.findRegion(x, y, ZLTextRegion.AnyRegionFilter);
if (region == null) {
final ZLTextElementAreaVector.RegionPair pair =
myView.findRegionsPair(x, y, ZLTextRegion.AnyRegionFilter);
if (pair.Before != null || pair.After != null) {
final ZLTextRegion.Soul base =
myCursorInMovement == SelectionCursor.Which.Right
? myLeftMostRegionSoul : myRightMostRegionSoul;
if (pair.Before != null) {
if (base.compareTo(pair.Before.getSoul()) <= 0) {
region = pair.Before;
} else {
region = pair.After;
}
} else {
if (base.compareTo(pair.After.getSoul()) >= 0) {
region = pair.After;
} else {
region = pair.Before;
}
}
}
}
if (region == null) {
return;
}
final ZLTextRegion.Soul soul = region.getSoul();
if (myCursorInMovement == ZLTextSelectionCursor.Right) {
if (myCursorInMovement == SelectionCursor.Which.Right) {
if (myLeftMostRegionSoul.compareTo(soul) <= 0) {
myRightMostRegionSoul = soul;
} else {
myRightMostRegionSoul = myLeftMostRegionSoul;
myLeftMostRegionSoul = soul;
myCursorInMovement = ZLTextSelectionCursor.Left;
myCursorInMovement = SelectionCursor.Which.Left;
}
} else {
if (myRightMostRegionSoul.compareTo(soul) >= 0) {
@ -163,11 +180,11 @@ class ZLTextSelection extends ZLTextHighlighting {
} else {
myLeftMostRegionSoul = myRightMostRegionSoul;
myRightMostRegionSoul = soul;
myCursorInMovement = ZLTextSelectionCursor.Right;
myCursorInMovement = SelectionCursor.Which.Right;
}
}
if (myCursorInMovement == ZLTextSelectionCursor.Right) {
if (myCursorInMovement == SelectionCursor.Which.Right) {
if (hasPartAfterPage(page)) {
myView.turnPage(true, ZLTextView.ScrollingMode.SCROLL_LINES, 1);
myView.Application.getViewWidget().reset();
@ -276,6 +293,11 @@ class ZLTextSelection extends ZLTextHighlighting {
return myView.getSelectionForegroundColor();
}
@Override
public ZLColor getOutlineColor() {
return null;
}
private class Scroller implements Runnable {
private final ZLTextPage myPage;
private final boolean myScrollForward;

View file

@ -19,6 +19,8 @@
package org.geometerplus.zlibrary.text.view;
import org.geometerplus.zlibrary.core.view.HorizontalConvexHull;
public abstract class ZLTextSimpleHighlighting extends ZLTextHighlighting {
protected final ZLTextView View;
private final ZLTextPosition myStartPosition;

View file

@ -21,13 +21,15 @@ package org.geometerplus.zlibrary.text.view;
import java.util.*;
import android.util.FloatMath;
import org.geometerplus.zlibrary.core.application.ZLApplication;
import org.geometerplus.zlibrary.core.filesystem.ZLFile;
import org.geometerplus.zlibrary.core.image.ZLImageData;
import org.geometerplus.zlibrary.core.library.ZLibrary;
import org.geometerplus.zlibrary.core.util.RationalNumber;
import org.geometerplus.zlibrary.core.util.ZLColor;
import org.geometerplus.zlibrary.core.view.ZLPaintContext;
import org.geometerplus.zlibrary.core.view.*;
import org.geometerplus.zlibrary.text.model.*;
import org.geometerplus.zlibrary.text.hyphenation.*;
@ -327,13 +329,9 @@ public abstract class ZLTextView extends ZLTextViewBase {
}
}
protected void moveSelectionCursorTo(ZLTextSelectionCursor cursor, int x, int y, boolean inMovement) {
if (inMovement) {
y -= ZLTextSelectionCursor.getHeight() / 2 + ZLTextSelectionCursor.getAccent() / 2;
} else {
y -= getTextStyleCollection().getBaseStyle().getFontSize() / 2;
}
mySelection.setCursorInMovement(cursor, x, y);
protected void moveSelectionCursorTo(SelectionCursor.Which which, int x, int y) {
y -= getTextStyleCollection().getBaseStyle().getFontSize() / 2;
mySelection.setCursorInMovement(which, x, y);
mySelection.expandTo(myCurrentPage, x, y);
Application.getViewWidget().reset();
Application.getViewWidget().repaint();
@ -345,104 +343,68 @@ public abstract class ZLTextView extends ZLTextViewBase {
Application.getViewWidget().repaint();
}
protected ZLTextSelectionCursor getSelectionCursorInMovement() {
protected SelectionCursor.Which getSelectionCursorInMovement() {
return mySelection.getCursorInMovement();
}
private ZLTextSelection.Point getSelectionCursorPoint(ZLTextPage page, ZLTextSelectionCursor cursor) {
if (cursor == ZLTextSelectionCursor.None) {
private ZLTextSelection.Point getSelectionCursorPoint(ZLTextPage page, SelectionCursor.Which which) {
if (which == null) {
return null;
}
if (cursor == mySelection.getCursorInMovement()) {
if (which == mySelection.getCursorInMovement()) {
return mySelection.getCursorInMovementPoint();
}
if (cursor == ZLTextSelectionCursor.Left) {
if (which == SelectionCursor.Which.Left) {
if (mySelection.hasPartBeforePage(page)) {
return null;
}
final ZLTextElementArea selectionStartArea = mySelection.getStartArea(page);
if (selectionStartArea != null) {
return new ZLTextSelection.Point(selectionStartArea.XStart, selectionStartArea.YEnd);
final ZLTextElementArea area = mySelection.getStartArea(page);
if (area != null) {
return new ZLTextSelection.Point(area.XStart, (area.YStart + area.YEnd) / 2);
}
} else {
if (mySelection.hasPartAfterPage(page)) {
return null;
}
final ZLTextElementArea selectionEndArea = mySelection.getEndArea(page);
if (selectionEndArea != null) {
return new ZLTextSelection.Point(selectionEndArea.XEnd, selectionEndArea.YEnd);
final ZLTextElementArea area = mySelection.getEndArea(page);
if (area != null) {
return new ZLTextSelection.Point(area.XEnd, (area.YStart + area.YEnd) / 2);
}
}
return null;
}
private int distanceToCursor(int x, int y, ZLTextSelection.Point cursorPoint) {
if (cursorPoint == null) {
return Integer.MAX_VALUE;
}
final int dX, dY;
final int w = ZLTextSelectionCursor.getWidth() / 2;
if (x < cursorPoint.X - w) {
dX = cursorPoint.X - w - x;
} else if (x > cursorPoint.X + w) {
dX = x - cursorPoint.X - w;
} else {
dX = 0;
}
final int h = ZLTextSelectionCursor.getHeight();
if (y < cursorPoint.Y) {
dY = cursorPoint.Y - y;
} else if (y > cursorPoint.Y + h) {
dY = y - cursorPoint.Y - h;
} else {
dY = 0;
}
return Math.max(dX, dY);
private float distanceToCursor(int x, int y, SelectionCursor.Which which) {
final ZLTextSelection.Point point = getSelectionCursorPoint(myCurrentPage, which);
return point != null ? FloatMath.hypot(x - point.X, y - point.Y) : Float.MAX_VALUE;
}
protected ZLTextSelectionCursor findSelectionCursor(int x, int y) {
return findSelectionCursor(x, y, Integer.MAX_VALUE);
protected SelectionCursor.Which findSelectionCursor(int x, int y) {
return findSelectionCursor(x, y, Float.MAX_VALUE);
}
protected ZLTextSelectionCursor findSelectionCursor(int x, int y, int maxDistance) {
protected SelectionCursor.Which findSelectionCursor(int x, int y, float maxDistance) {
if (mySelection.isEmpty()) {
return ZLTextSelectionCursor.None;
return null;
}
final int leftDistance = distanceToCursor(
x, y, getSelectionCursorPoint(myCurrentPage, ZLTextSelectionCursor.Left)
);
final int rightDistance = distanceToCursor(
x, y, getSelectionCursorPoint(myCurrentPage, ZLTextSelectionCursor.Right)
);
final float leftDistance = distanceToCursor(x, y, SelectionCursor.Which.Left);
final float rightDistance = distanceToCursor(x, y, SelectionCursor.Which.Right);
if (rightDistance < leftDistance) {
return rightDistance <= maxDistance ? ZLTextSelectionCursor.Right : ZLTextSelectionCursor.None;
return rightDistance <= maxDistance ? SelectionCursor.Which.Right : null;
} else {
return leftDistance <= maxDistance ? ZLTextSelectionCursor.Left : ZLTextSelectionCursor.None;
return leftDistance <= maxDistance ? SelectionCursor.Which.Left : null;
}
}
private void drawSelectionCursor(ZLPaintContext context, ZLTextSelection.Point pt) {
if (pt == null) {
return;
private void drawSelectionCursor(ZLPaintContext context, ZLTextPage page, SelectionCursor.Which which) {
final ZLTextSelection.Point pt = getSelectionCursorPoint(page, which);
if (pt != null) {
SelectionCursor.draw(context, which, pt.X, pt.Y, getSelectionBackgroundColor());
}
final int w = ZLTextSelectionCursor.getWidth() / 2;
final int h = ZLTextSelectionCursor.getHeight();
final int a = ZLTextSelectionCursor.getAccent();
final int[] xs = { pt.X, pt.X + w, pt.X + w, pt.X - w, pt.X - w };
final int[] ys = { pt.Y - a, pt.Y, pt.Y + h, pt.Y + h, pt.Y };
context.setFillColor(context.getBackgroundColor(), 192);
context.fillPolygon(xs, ys);
context.setLineColor(getTextColor(ZLTextHyperlink.NO_LINK));
context.drawPolygonalLine(xs, ys);
}
@Override
@ -501,34 +463,22 @@ public abstract class ZLTextView extends ZLTextViewBase {
int x = getLeftMargin();
int y = getTopMargin();
int index = 0;
int columnIndex = 0;
ZLTextLineInfo previousInfo = null;
for (ZLTextLineInfo info : lineInfos) {
info.adjust(previousInfo);
prepareTextLine(page, info, x, y);
prepareTextLine(page, info, x, y, columnIndex);
y += info.Height + info.Descent + info.VSpaceAfter;
labels[++index] = page.TextElementMap.size();
if (index == page.Column0Height) {
y = getTopMargin();
x += page.getTextWidth() + getSpaceBetweenColumns();
columnIndex = 1;
}
previousInfo = info;
}
final List<ZLTextHighlighting> hilites = findHilites(page);
if (!hilites.isEmpty()) {
x = getLeftMargin();
y = getTopMargin();
index = 0;
for (ZLTextLineInfo info : lineInfos) {
drawHighlightings(page, hilites, info, labels[index], labels[index + 1], x, y);
y += info.Height + info.Descent + info.VSpaceAfter;
++index;
if (index == page.Column0Height) {
y = getTopMargin();
x += page.getTextWidth() + getSpaceBetweenColumns();
}
}
}
x = getLeftMargin();
y = getTopMargin();
@ -543,13 +493,34 @@ public abstract class ZLTextView extends ZLTextViewBase {
}
}
final ZLTextRegion outlinedElementRegion = getOutlinedRegion(page);
if (outlinedElementRegion != null && myShowOutline) {
outlinedElementRegion.draw(context);
for (ZLTextHighlighting h : hilites) {
int mode = Hull.DrawMode.None;
final ZLColor bgColor = h.getBackgroundColor();
if (bgColor != null) {
context.setFillColor(bgColor, 128);
mode |= Hull.DrawMode.Fill;
}
final ZLColor outlineColor = h.getOutlineColor();
if (outlineColor != null) {
context.setLineColor(outlineColor);
mode |= Hull.DrawMode.Outline;
}
if (mode != Hull.DrawMode.None) {
h.hull(page).draw(getContext(), mode);
}
}
drawSelectionCursor(context, getSelectionCursorPoint(page, ZLTextSelectionCursor.Left));
drawSelectionCursor(context, getSelectionCursorPoint(page, ZLTextSelectionCursor.Right));
final ZLTextRegion outlinedElementRegion = getOutlinedRegion(page);
if (outlinedElementRegion != null && myShowOutline) {
context.setLineColor(getSelectionBackgroundColor());
outlinedElementRegion.hull().draw(context, Hull.DrawMode.Outline);
}
drawSelectionCursor(context, page, SelectionCursor.Which.Left);
drawSelectionCursor(context, page, SelectionCursor.Which.Right);
}
private ZLTextPage getPage(PageIndex pageIndex) {
@ -847,45 +818,6 @@ public abstract class ZLTextView extends ZLTextViewBase {
return hilites;
}
private void drawHighlightings(ZLTextPage page, List<ZLTextHighlighting> hilites, ZLTextLineInfo info, int from, int to, int x, int y) {
if (from == to) {
return;
}
final ZLTextElementArea fromArea = page.TextElementMap.get(from);
final ZLTextElementArea toArea = page.TextElementMap.get(to - 1);
for (ZLTextHighlighting h : hilites) {
final ZLColor bgColor = h.getBackgroundColor();
if (bgColor == null) {
continue;
}
final ZLTextElementArea selectionStartArea = h.getStartArea(page);
if (selectionStartArea == null || selectionStartArea.compareTo(toArea) > 0) {
continue;
}
final ZLTextElementArea selectionEndArea = h.getEndArea(page);
if (selectionEndArea == null || selectionEndArea.compareTo(fromArea) < 0) {
continue;
}
final int top = y + 1;
int left, right, bottom = y + info.Height + info.Descent;
if (selectionStartArea.compareTo(fromArea) < 0) {
left = x;
} else {
left = selectionStartArea.XStart;
}
if (selectionEndArea.compareTo(toArea) > 0) {
right = x + page.getTextWidth() - 1;
bottom += info.VSpaceAfter;
} else {
right = selectionEndArea.XEnd;
}
getContext().setFillColor(bgColor);
getContext().fillRectangle(left, top, right, bottom);
}
}
protected abstract ZLPaintContext.ColorAdjustingMode getAdjustingModeForImages();
private static final char[] SPACE = new char[] { ' ' };
@ -895,9 +827,13 @@ public abstract class ZLTextView extends ZLTextViewBase {
int index = from;
final int endElementIndex = info.EndElementIndex;
int charIndex = info.RealStartCharIndex;
final List<ZLTextElementArea> pageAreas = page.TextElementMap.areas();
if (to > pageAreas.size()) {
return;
}
for (int wordIndex = info.RealStartElementIndex; wordIndex != endElementIndex && index < to; ++wordIndex, charIndex = 0) {
final ZLTextElement element = paragraph.getElement(wordIndex);
final ZLTextElementArea area = page.TextElementMap.get(index);
final ZLTextElementArea area = pageAreas.get(index);
if (element == area.Element) {
++index;
if (area.ChangeStyle) {
@ -961,7 +897,7 @@ public abstract class ZLTextView extends ZLTextViewBase {
}
}
if (index != to) {
ZLTextElementArea area = page.TextElementMap.get(index++);
ZLTextElementArea area = pageAreas.get(index++);
if (area.ChangeStyle) {
setTextStyle(area.Style);
}
@ -1306,7 +1242,7 @@ public abstract class ZLTextView extends ZLTextViewBase {
return info;
}
private void prepareTextLine(ZLTextPage page, ZLTextLineInfo info, int x, int y) {
private void prepareTextLine(ZLTextPage page, ZLTextLineInfo info, int x, int y, int columnIndex) {
y = Math.min(y + info.Height, getTopMargin() + page.getTextHeight() - 1);
final ZLPaintContext context = getContext();
@ -1357,7 +1293,7 @@ public abstract class ZLTextView extends ZLTextViewBase {
true, // is last in element
false, // add hyphenation sign
false, // changed style
getTextStyle(), element, x, x + spaceLength, y, y
getTextStyle(), element, x, x + spaceLength, y, y, columnIndex
);
} else {
spaceElement = null;
@ -1381,7 +1317,7 @@ public abstract class ZLTextView extends ZLTextViewBase {
true, // is last in element
false, // add hyphenation sign
changeStyle, getTextStyle(), element,
x, x + width - 1, y - height + 1, y + descent
x, x + width - 1, y - height + 1, y + descent, columnIndex
));
changeStyle = false;
wordOccurred = true;
@ -1407,7 +1343,7 @@ public abstract class ZLTextView extends ZLTextViewBase {
false, // is last in element
addHyphenationSign,
changeStyle, getTextStyle(), word,
x, x + width - 1, y - height + 1, y + descent
x, x + width - 1, y - height + 1, y + descent, columnIndex
)
);
}
@ -1773,6 +1709,10 @@ public abstract class ZLTextView extends ZLTextViewBase {
return myCurrentPage.TextElementMap.findRegion(x, y, maxDistance, filter);
}
protected ZLTextElementAreaVector.RegionPair findRegionsPair(int x, int y, ZLTextRegion.Filter filter) {
return myCurrentPage.TextElementMap.findRegionsPair(x, y, getColumnIndex(x), filter);
}
protected boolean initSelection(int x, int y) {
y -= getTextStyleCollection().getBaseStyle().getFontSize() / 2;
if (!mySelection.start(x, y)) {
@ -1790,8 +1730,8 @@ public abstract class ZLTextView extends ZLTextViewBase {
}
}
public ZLTextRegion.Soul getSelectionSoul() {
return mySelection.getSoul();
public ZLTextHighlighting getSelectionHighlighting() {
return mySelection;
}
public int getSelectionStartY() {

View file

@ -110,6 +110,13 @@ abstract class ZLTextViewBase extends ZLView {
return getContextHeight() - getTopMargin() - getBottomMargin();
}
protected int getColumnIndex(int x) {
if (!twoColumnView()) {
return -1;
}
return 2 * x <= getContextWidth() + getLeftMargin() - getRightMargin() ? 0 : 1;
}
public int getTextColumnWidth() {
return twoColumnView()
? (getContextWidth() - getLeftMargin() - getSpaceBetweenColumns() - getRightMargin()) / 2

View file

@ -103,6 +103,10 @@ public final class ZLTextWord extends ZLTextElement {
@Override
public String toString() {
return getString();
}
public String getString() {
return new String(Data, Offset, Length);
}
}