From 4a98faa9b51d0c3abdf94e81024a1c2ce1e02ea9 Mon Sep 17 00:00:00 2001 From: Nikolay Pultsin Date: Sat, 11 Jun 2011 11:05:53 +0200 Subject: [PATCH] selection refactoring --- .../zlibrary/text/view/ZLTextElementArea.java | 21 ++- .../text/view/ZLTextElementAreaVector.java | 6 +- .../text/view/ZLTextHyperlinkRegionSoul.java | 17 +-- .../text/view/ZLTextImageRegionSoul.java | 16 +-- .../zlibrary/text/view/ZLTextPosition.java | 17 --- .../zlibrary/text/view/ZLTextRegion.java | 60 ++++++++- .../zlibrary/text/view/ZLTextSelection.java | 121 ++++++++++++++---- .../zlibrary/text/view/ZLTextView.java | 68 +++++----- .../text/view/ZLTextWordRegionSoul.java | 16 +-- 9 files changed, 213 insertions(+), 129 deletions(-) diff --git a/src/org/geometerplus/zlibrary/text/view/ZLTextElementArea.java b/src/org/geometerplus/zlibrary/text/view/ZLTextElementArea.java index 10e8ae7a8..01b345ef3 100644 --- a/src/org/geometerplus/zlibrary/text/view/ZLTextElementArea.java +++ b/src/org/geometerplus/zlibrary/text/view/ZLTextElementArea.java @@ -19,12 +19,12 @@ package org.geometerplus.zlibrary.text.view; -final class ZLTextElementArea extends ZLTextFixedPosition { - final int XStart; - final int XEnd; - final int YStart; - final int YEnd; - +final class ZLTextElementArea extends ZLTextFixedPosition { + final int XStart; + final int XEnd; + final int YStart; + final int YEnd; + final int Length; final boolean AddHyphenationSign; final boolean ChangeStyle; @@ -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; + } } diff --git a/src/org/geometerplus/zlibrary/text/view/ZLTextElementAreaVector.java b/src/org/geometerplus/zlibrary/text/view/ZLTextElementAreaVector.java index f5dbc18b9..d0db8b487 100644 --- a/src/org/geometerplus/zlibrary/text/view/ZLTextElementAreaVector.java +++ b/src/org/geometerplus/zlibrary/text/view/ZLTextElementAreaVector.java @@ -43,11 +43,11 @@ final class ZLTextElementAreaVector extends ArrayList { 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()); diff --git a/src/org/geometerplus/zlibrary/text/view/ZLTextHyperlinkRegionSoul.java b/src/org/geometerplus/zlibrary/text/view/ZLTextHyperlinkRegionSoul.java index f24c7b4ac..d00ea0000 100644 --- a/src/org/geometerplus/zlibrary/text/view/ZLTextHyperlinkRegionSoul.java +++ b/src/org/geometerplus/zlibrary/text/view/ZLTextHyperlinkRegionSoul.java @@ -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; - } } diff --git a/src/org/geometerplus/zlibrary/text/view/ZLTextImageRegionSoul.java b/src/org/geometerplus/zlibrary/text/view/ZLTextImageRegionSoul.java index 37cbb949f..2680cb475 100644 --- a/src/org/geometerplus/zlibrary/text/view/ZLTextImageRegionSoul.java +++ b/src/org/geometerplus/zlibrary/text/view/ZLTextImageRegionSoul.java @@ -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; - } } diff --git a/src/org/geometerplus/zlibrary/text/view/ZLTextPosition.java b/src/org/geometerplus/zlibrary/text/view/ZLTextPosition.java index 43b7b8787..302b9374b 100644 --- a/src/org/geometerplus/zlibrary/text/view/ZLTextPosition.java +++ b/src/org/geometerplus/zlibrary/text/view/ZLTextPosition.java @@ -53,23 +53,6 @@ public abstract class ZLTextPosition implements Comparable { 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(); diff --git a/src/org/geometerplus/zlibrary/text/view/ZLTextRegion.java b/src/org/geometerplus/zlibrary/text/view/ZLTextRegion.java index 887fae339..824d87638 100644 --- a/src/org/geometerplus/zlibrary/text/view/ZLTextRegion.java +++ b/src/org/geometerplus/zlibrary/text/view/ZLTextRegion.java @@ -23,12 +23,62 @@ import java.util.*; import org.geometerplus.zlibrary.core.view.ZLPaintContext; -public final class ZLTextRegion implements Comparable { - public static abstract class Soul { - abstract boolean accepts(ZLTextElementArea area); +public final class ZLTextRegion /*implements Comparable*/ { + public static abstract class Soul implements Comparable { + 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 { 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 { } return 0; } + */ } diff --git a/src/org/geometerplus/zlibrary/text/view/ZLTextSelection.java b/src/org/geometerplus/zlibrary/text/view/ZLTextSelection.java index f25ca730b..0f720d330 100644 --- a/src/org/geometerplus/zlibrary/text/view/ZLTextSelection.java +++ b/src/org/geometerplus/zlibrary/text/view/ZLTextSelection.java @@ -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 { diff --git a/src/org/geometerplus/zlibrary/text/view/ZLTextView.java b/src/org/geometerplus/zlibrary/text/view/ZLTextView.java index fafd84e3e..d67375769 100644 --- a/src/org/geometerplus/zlibrary/text/view/ZLTextView.java +++ b/src/org/geometerplus/zlibrary/text/view/ZLTextView.java @@ -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() { diff --git a/src/org/geometerplus/zlibrary/text/view/ZLTextWordRegionSoul.java b/src/org/geometerplus/zlibrary/text/view/ZLTextWordRegionSoul.java index b299d6054..14f7ed01c 100644 --- a/src/org/geometerplus/zlibrary/text/view/ZLTextWordRegionSoul.java +++ b/src/org/geometerplus/zlibrary/text/view/ZLTextWordRegionSoul.java @@ -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; - } }