1
0
Fork 0
mirror of https://github.com/geometer/FBReaderJ.git synced 2025-10-03 17:59:33 +02:00

selection refactoring

This commit is contained in:
Nikolay Pultsin 2011-06-11 11:05:53 +02:00
parent 95d78d3c6d
commit 4a98faa9b5
9 changed files with 213 additions and 129 deletions

View file

@ -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;
}
}

View file

@ -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());

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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();

View file

@ -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;
}
*/
}

View file

@ -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 {

View file

@ -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() {

View file

@ -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;
}
}