mirror of
https://github.com/geometer/FBReaderJ.git
synced 2025-10-03 17:59:33 +02:00
selection refactoring
This commit is contained in:
parent
95d78d3c6d
commit
4a98faa9b5
9 changed files with 213 additions and 129 deletions
|
@ -49,4 +49,13 @@ final class ZLTextElementArea extends ZLTextFixedPosition {
|
|||
boolean contains(int x, int y) {
|
||||
return (y >= YStart) && (y <= YEnd) && (x >= XStart) && (x <= XEnd);
|
||||
}
|
||||
|
||||
boolean isFirstInElement() {
|
||||
return CharIndex == 0;
|
||||
}
|
||||
|
||||
boolean isLastInElement() {
|
||||
// TODO: support multi-part (> 2 part) words
|
||||
return !(Element instanceof ZLTextWord) || CharIndex > 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,11 +43,11 @@ final class ZLTextElementAreaVector extends ArrayList<ZLTextElementArea> {
|
|||
ZLTextRegion.Soul soul = null;
|
||||
final ZLTextHyperlink hyperlink = area.Style.Hyperlink;
|
||||
if (hyperlink.Id != null) {
|
||||
soul = new ZLTextHyperlinkRegionSoul(hyperlink);
|
||||
soul = new ZLTextHyperlinkRegionSoul(area, hyperlink);
|
||||
} else if (area.Element instanceof ZLTextImageElement) {
|
||||
soul = new ZLTextImageRegionSoul((ZLTextImageElement)area.Element);
|
||||
soul = new ZLTextImageRegionSoul(area, (ZLTextImageElement)area.Element);
|
||||
} else if (area.Element instanceof ZLTextWord && ((ZLTextWord)area.Element).isAWord()) {
|
||||
soul = new ZLTextWordRegionSoul((ZLTextWord)area.Element);
|
||||
soul = new ZLTextWordRegionSoul(area, (ZLTextWord)area.Element);
|
||||
}
|
||||
if (soul != null) {
|
||||
myCurrentElementRegion = new ZLTextRegion(soul, this, size());
|
||||
|
|
|
@ -22,20 +22,9 @@ package org.geometerplus.zlibrary.text.view;
|
|||
public class ZLTextHyperlinkRegionSoul extends ZLTextRegion.Soul {
|
||||
public final ZLTextHyperlink Hyperlink;
|
||||
|
||||
ZLTextHyperlinkRegionSoul(ZLTextHyperlink hyperlink) {
|
||||
ZLTextHyperlinkRegionSoul(ZLTextPosition position, ZLTextHyperlink hyperlink) {
|
||||
// TODO: fix this call
|
||||
super(position.getParagraphIndex(), position.getElementIndex(), position.getElementIndex());
|
||||
Hyperlink = hyperlink;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean accepts(ZLTextElementArea area) {
|
||||
return Hyperlink == area.Style.Hyperlink;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof ZLTextHyperlinkRegionSoul)) {
|
||||
return false;
|
||||
}
|
||||
return Hyperlink == ((ZLTextHyperlinkRegionSoul)other).Hyperlink;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,20 +22,8 @@ package org.geometerplus.zlibrary.text.view;
|
|||
public class ZLTextImageRegionSoul extends ZLTextRegion.Soul {
|
||||
public final ZLTextImageElement ImageElement;
|
||||
|
||||
ZLTextImageRegionSoul(ZLTextImageElement imageElement) {
|
||||
ZLTextImageRegionSoul(ZLTextPosition position, ZLTextImageElement imageElement) {
|
||||
super(position.getParagraphIndex(), position.getElementIndex(), position.getElementIndex());
|
||||
ImageElement = imageElement;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean accepts(ZLTextElementArea area) {
|
||||
return ImageElement == area.Element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof ZLTextImageRegionSoul)) {
|
||||
return false;
|
||||
}
|
||||
return ImageElement == ((ZLTextImageRegionSoul)other).ImageElement;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,23 +53,6 @@ public abstract class ZLTextPosition implements Comparable<ZLTextPosition> {
|
|||
return 0;
|
||||
}
|
||||
|
||||
// ignores character index
|
||||
int weakCompareTo(ZLTextPosition position) {
|
||||
final int p0 = getParagraphIndex();
|
||||
final int p1 = position.getParagraphIndex();
|
||||
if (p0 != p1) {
|
||||
return p0 < p1 ? -1 : 1;
|
||||
}
|
||||
|
||||
final int e0 = getElementIndex();
|
||||
final int e1 = position.getElementIndex();
|
||||
if (e0 != e1) {
|
||||
return e0 < e1 ? -1 : 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (getParagraphIndex() << 16) + (getElementIndex() << 8) + getCharIndex();
|
||||
|
|
|
@ -23,12 +23,62 @@ import java.util.*;
|
|||
|
||||
import org.geometerplus.zlibrary.core.view.ZLPaintContext;
|
||||
|
||||
public final class ZLTextRegion implements Comparable<ZLTextRegion> {
|
||||
public static abstract class Soul {
|
||||
abstract boolean accepts(ZLTextElementArea area);
|
||||
public final class ZLTextRegion /*implements Comparable<ZLTextRegion>*/ {
|
||||
public static abstract class Soul implements Comparable<Soul> {
|
||||
final int ParagraphIndex;
|
||||
final int StartElementIndex;
|
||||
final int EndElementIndex;
|
||||
|
||||
protected Soul(int paragraphIndex, int startElementIndex, int endElementIndex) {
|
||||
ParagraphIndex = paragraphIndex;
|
||||
StartElementIndex = startElementIndex;
|
||||
EndElementIndex = endElementIndex;
|
||||
}
|
||||
|
||||
final boolean accepts(ZLTextElementArea area) {
|
||||
return compareTo(area) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract boolean equals(Object other);
|
||||
public final boolean equals(Object other) {
|
||||
if (other == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof Soul)) {
|
||||
return false;
|
||||
}
|
||||
final Soul soul = (Soul)other;
|
||||
return
|
||||
ParagraphIndex == soul.ParagraphIndex &&
|
||||
StartElementIndex == soul.StartElementIndex &&
|
||||
EndElementIndex == soul.EndElementIndex;
|
||||
}
|
||||
|
||||
public final int compareTo(Soul soul) {
|
||||
if (ParagraphIndex != soul.ParagraphIndex) {
|
||||
return ParagraphIndex < soul.ParagraphIndex ? -1 : 1;
|
||||
}
|
||||
if (EndElementIndex < soul.StartElementIndex) {
|
||||
return -1;
|
||||
}
|
||||
if (StartElementIndex > soul.EndElementIndex) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public final int compareTo(ZLTextElementArea area) {
|
||||
if (ParagraphIndex != area.ParagraphIndex) {
|
||||
return ParagraphIndex < area.ParagraphIndex ? -1 : 1;
|
||||
}
|
||||
if (EndElementIndex < area.ElementIndex) {
|
||||
return -1;
|
||||
}
|
||||
if (StartElementIndex > area.ElementIndex) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static interface Filter {
|
||||
|
@ -155,6 +205,7 @@ public final class ZLTextRegion implements Comparable<ZLTextRegion> {
|
|||
return other == null || other.isExactlyUnder(this);
|
||||
}
|
||||
|
||||
/*
|
||||
public int compareTo(ZLTextRegion other) {
|
||||
if (myFromIndex != other.myFromIndex) {
|
||||
return myFromIndex < other.myFromIndex ? -1 : 1;
|
||||
|
@ -164,4 +215,5 @@ public final class ZLTextRegion implements Comparable<ZLTextRegion> {
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -22,8 +22,8 @@ package org.geometerplus.zlibrary.text.view;
|
|||
public class ZLTextSelection {
|
||||
private final ZLTextView myView;
|
||||
|
||||
private ZLTextRegion myLeftMostRegion;
|
||||
private ZLTextRegion myRightMostRegion;
|
||||
private ZLTextRegion.Soul myLeftMostRegionSoul;
|
||||
private ZLTextRegion.Soul myRightMostRegionSoul;
|
||||
|
||||
private Scroller myScroller;
|
||||
|
||||
|
@ -32,7 +32,7 @@ public class ZLTextSelection {
|
|||
}
|
||||
|
||||
boolean isEmpty() {
|
||||
return myLeftMostRegion == null;
|
||||
return myLeftMostRegionSoul == null;
|
||||
}
|
||||
|
||||
boolean clear() {
|
||||
|
@ -41,22 +41,22 @@ public class ZLTextSelection {
|
|||
}
|
||||
|
||||
stop();
|
||||
myLeftMostRegion = null;
|
||||
myRightMostRegion = null;
|
||||
myLeftMostRegionSoul = null;
|
||||
myRightMostRegionSoul = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean start(int x, int y) {
|
||||
clear();
|
||||
|
||||
myLeftMostRegion = myView.findRegion(
|
||||
final ZLTextRegion region = myView.findRegion(
|
||||
x, y, ZLTextView.MAX_SELECTION_DISTANCE, ZLTextRegion.AnyRegionFilter
|
||||
);
|
||||
if (myLeftMostRegion == null) {
|
||||
if (region == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
myRightMostRegion = myLeftMostRegion;
|
||||
myRightMostRegionSoul = myLeftMostRegionSoul = region.getSoul();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -111,22 +111,23 @@ public class ZLTextSelection {
|
|||
return cursorToMove;
|
||||
}
|
||||
|
||||
final ZLTextRegion.Soul soul = region.getSoul();
|
||||
if (cursorToMove == ZLTextSelectionCursor.Right) {
|
||||
if (myLeftMostRegion.compareTo(region) <= 0) {
|
||||
myRightMostRegion = region;
|
||||
if (myLeftMostRegionSoul.compareTo(soul) <= 0) {
|
||||
myRightMostRegionSoul = soul;
|
||||
return cursorToMove;
|
||||
} else {
|
||||
myRightMostRegion = myLeftMostRegion;
|
||||
myLeftMostRegion = region;
|
||||
myRightMostRegionSoul = myLeftMostRegionSoul;
|
||||
myLeftMostRegionSoul = soul;
|
||||
return ZLTextSelectionCursor.Left;
|
||||
}
|
||||
} else {
|
||||
if (myRightMostRegion.compareTo(region) >= 0) {
|
||||
myLeftMostRegion = region;
|
||||
if (myRightMostRegionSoul.compareTo(soul) >= 0) {
|
||||
myLeftMostRegionSoul = soul;
|
||||
return cursorToMove;
|
||||
} else {
|
||||
myLeftMostRegion = myRightMostRegion;
|
||||
myRightMostRegion = region;
|
||||
myLeftMostRegionSoul = myRightMostRegionSoul;
|
||||
myRightMostRegionSoul = soul;
|
||||
return ZLTextSelectionCursor.Right;
|
||||
}
|
||||
}
|
||||
|
@ -135,16 +136,92 @@ public class ZLTextSelection {
|
|||
boolean isAreaSelected(ZLTextElementArea area) {
|
||||
return
|
||||
!isEmpty()
|
||||
&& myLeftMostRegion.getFirstArea().weakCompareTo(area) <= 0
|
||||
&& myRightMostRegion.getLastArea().weakCompareTo(area) >= 0;
|
||||
&& myLeftMostRegionSoul.compareTo(area) <= 0
|
||||
&& myRightMostRegionSoul.compareTo(area) >= 0;
|
||||
}
|
||||
|
||||
ZLTextElementArea getStartArea() {
|
||||
return myLeftMostRegion.getFirstArea();
|
||||
ZLTextPosition getStartPosition() {
|
||||
if (isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new ZLTextFixedPosition(
|
||||
myLeftMostRegionSoul.ParagraphIndex,
|
||||
myLeftMostRegionSoul.StartElementIndex,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
ZLTextElementArea getEndArea() {
|
||||
return myRightMostRegion.getLastArea();
|
||||
ZLTextPosition getEndPosition() {
|
||||
if (isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new ZLTextFixedPosition(
|
||||
myRightMostRegionSoul.ParagraphIndex,
|
||||
myRightMostRegionSoul.EndElementIndex,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
ZLTextElementArea getStartArea(ZLTextPage page) {
|
||||
if (isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final ZLTextElementAreaVector vector = page.TextElementMap;
|
||||
if (vector.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final ZLTextRegion region = vector.getRegion(myLeftMostRegionSoul);
|
||||
if (region != null) {
|
||||
return region.getFirstArea();
|
||||
}
|
||||
if (myRightMostRegionSoul.compareTo(vector.get(0)) >= 0) {
|
||||
return vector.get(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
ZLTextElementArea getEndArea(ZLTextPage page) {
|
||||
if (isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final ZLTextElementAreaVector vector = page.TextElementMap;
|
||||
if (vector.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final ZLTextRegion region = vector.getRegion(myRightMostRegionSoul);
|
||||
if (region != null) {
|
||||
return region.getLastArea();
|
||||
}
|
||||
if (myRightMostRegionSoul.compareTo(vector.get(vector.size() - 1)) >= 0) {
|
||||
return vector.get(vector.size() - 1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean hasAPartBeforePage(ZLTextPage page) {
|
||||
if (isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
final ZLTextElementAreaVector vector = page.TextElementMap;
|
||||
if (vector.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
final ZLTextElementArea firstPageArea = vector.get(0);
|
||||
final int cmp = myLeftMostRegionSoul.compareTo(firstPageArea);
|
||||
return cmp < 0 || (cmp == 0 && !firstPageArea.isFirstInElement());
|
||||
}
|
||||
|
||||
boolean hasAPartAfterPage(ZLTextPage page) {
|
||||
if (isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
final ZLTextElementAreaVector vector = page.TextElementMap;
|
||||
if (vector.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
final ZLTextElementArea lastPageArea = vector.get(vector.size() - 1);
|
||||
final int cmp = myRightMostRegionSoul.compareTo(lastPageArea);
|
||||
return cmp > 0 || (cmp == 0 && !lastPageArea.isLastInElement());
|
||||
}
|
||||
|
||||
private class Scroller implements Runnable {
|
||||
|
|
|
@ -272,8 +272,7 @@ public abstract class ZLTextView extends ZLTextViewBase {
|
|||
}
|
||||
|
||||
private Point getSelectionCursorPoint(ZLTextPage page, ZLTextSelectionCursor cursor) {
|
||||
final ZLTextElementAreaVector vector = page.TextElementMap;
|
||||
if (cursor == ZLTextSelectionCursor.None || mySelection.isEmpty() || vector.isEmpty()) {
|
||||
if (cursor == ZLTextSelectionCursor.None) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -281,19 +280,20 @@ public abstract class ZLTextView extends ZLTextViewBase {
|
|||
return myMovedSelectionCursorPoint;
|
||||
}
|
||||
|
||||
final ZLTextElementArea firstArea = vector.get(0);
|
||||
final ZLTextElementArea lastArea = vector.get(vector.size() - 1);
|
||||
|
||||
if (cursor == ZLTextSelectionCursor.Left) {
|
||||
final ZLTextElementArea selectionStartArea = mySelection.getStartArea();
|
||||
if (selectionStartArea.compareTo(firstArea) >= 0
|
||||
&& selectionStartArea.compareTo(lastArea) <= 0) {
|
||||
if (mySelection.hasAPartBeforePage(page)) {
|
||||
return null;
|
||||
}
|
||||
final ZLTextElementArea selectionStartArea = mySelection.getStartArea(page);
|
||||
if (selectionStartArea != null) {
|
||||
return new Point(selectionStartArea.XStart, selectionStartArea.YEnd);
|
||||
}
|
||||
} else {
|
||||
final ZLTextElementArea selectionEndArea = mySelection.getEndArea();
|
||||
if (selectionEndArea.compareTo(firstArea) >= 0
|
||||
&& selectionEndArea.compareTo(lastArea) <= 0) {
|
||||
if (mySelection.hasAPartAfterPage(page)) {
|
||||
return null;
|
||||
}
|
||||
final ZLTextElementArea selectionEndArea = mySelection.getEndArea(page);
|
||||
if (selectionEndArea != null) {
|
||||
return new Point(selectionEndArea.XEnd, selectionEndArea.YEnd);
|
||||
}
|
||||
}
|
||||
|
@ -669,10 +669,12 @@ public abstract class ZLTextView extends ZLTextViewBase {
|
|||
if (!mySelection.isEmpty() && from != to) {
|
||||
final ZLTextElementArea fromArea = page.TextElementMap.get(from);
|
||||
final ZLTextElementArea toArea = page.TextElementMap.get(to - 1);
|
||||
final ZLTextElementArea selectionStartArea = mySelection.getStartArea();
|
||||
final ZLTextElementArea selectionEndArea = mySelection.getEndArea();
|
||||
if (fromArea.compareTo(selectionEndArea) <= 0
|
||||
&& toArea.compareTo(selectionStartArea) >= 0) {
|
||||
final ZLTextElementArea selectionStartArea = mySelection.getStartArea(page);
|
||||
final ZLTextElementArea selectionEndArea = mySelection.getEndArea(page);
|
||||
if (selectionStartArea != null
|
||||
&& selectionEndArea != null
|
||||
&& selectionStartArea.compareTo(toArea) <= 0
|
||||
&& selectionEndArea.compareTo(fromArea) >= 0) {
|
||||
final int top = y + 1;
|
||||
int left, right, bottom = y + info.Height + info.Descent;
|
||||
if (selectionStartArea.compareTo(fromArea) < 0) {
|
||||
|
@ -1416,16 +1418,15 @@ public abstract class ZLTextView extends ZLTextViewBase {
|
|||
if (mySelection.isEmpty() || vector.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
final ZLTextElementArea selectionStartArea = mySelection.getStartArea();
|
||||
final int index = vector.indexOf(selectionStartArea);
|
||||
if (index != -1) {
|
||||
return vector.get(index).YStart;
|
||||
final ZLTextElementArea selectionStartArea = mySelection.getStartArea(myCurrentPage);
|
||||
if (selectionStartArea != null) {
|
||||
return selectionStartArea.YStart;
|
||||
}
|
||||
final ZLTextElementArea endArea = vector.get(vector.size() - 1);
|
||||
if (selectionStartArea.compareTo(endArea) > 0) {
|
||||
return endArea.YEnd;
|
||||
if (mySelection.hasAPartBeforePage(myCurrentPage)) {
|
||||
return vector.get(0).YStart;
|
||||
} else {
|
||||
return vector.get(vector.size() - 1).YEnd;
|
||||
}
|
||||
return vector.get(0).YStart;
|
||||
}
|
||||
|
||||
public int getSelectionEndY() {
|
||||
|
@ -1433,26 +1434,23 @@ public abstract class ZLTextView extends ZLTextViewBase {
|
|||
if (mySelection.isEmpty() || vector.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
final ZLTextElementArea selectionEndArea = mySelection.getEndArea();
|
||||
final int index = vector.indexOf(selectionEndArea);
|
||||
if (index != -1) {
|
||||
return vector.get(index).YEnd;
|
||||
final ZLTextElementArea selectionEndArea = mySelection.getEndArea(myCurrentPage);
|
||||
if (selectionEndArea != null) {
|
||||
return selectionEndArea.YEnd;
|
||||
}
|
||||
final ZLTextElementArea endArea = vector.get(vector.size() - 1);
|
||||
if (selectionEndArea.compareTo(endArea) > 0) {
|
||||
return endArea.YEnd;
|
||||
if (mySelection.hasAPartAfterPage(myCurrentPage)) {
|
||||
return vector.get(vector.size() - 1).YEnd;
|
||||
} else {
|
||||
return vector.get(0).YStart;
|
||||
}
|
||||
return vector.get(0).YStart;
|
||||
}
|
||||
|
||||
public ZLTextPosition getSelectionStartPosition() {
|
||||
final ZLTextPosition start = mySelection.getStartArea();
|
||||
return new ZLTextFixedPosition(start.getParagraphIndex(), start.getElementIndex(), 0);
|
||||
return mySelection.getStartPosition();
|
||||
}
|
||||
|
||||
public ZLTextPosition getSelectionEndPosition() {
|
||||
final ZLTextPosition end = mySelection.getEndArea();
|
||||
return new ZLTextFixedPosition(end.getParagraphIndex(), end.getElementIndex(), Integer.MAX_VALUE);
|
||||
return mySelection.getEndPosition();
|
||||
}
|
||||
|
||||
public boolean isSelectionEmpty() {
|
||||
|
|
|
@ -22,20 +22,8 @@ package org.geometerplus.zlibrary.text.view;
|
|||
public class ZLTextWordRegionSoul extends ZLTextRegion.Soul {
|
||||
public final ZLTextWord Word;
|
||||
|
||||
ZLTextWordRegionSoul(ZLTextWord word) {
|
||||
ZLTextWordRegionSoul(ZLTextPosition position, ZLTextWord word) {
|
||||
super(position.getParagraphIndex(), position.getElementIndex(), position.getElementIndex());
|
||||
Word = word;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean accepts(ZLTextElementArea area) {
|
||||
return Word == area.Element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof ZLTextWordRegionSoul)) {
|
||||
return false;
|
||||
}
|
||||
return Word == ((ZLTextWordRegionSoul)other).Word;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue