mirror of
https://github.com/geometer/FBReaderJ.git
synced 2025-10-05 02:39:23 +02:00
text highlighting, misc fixes
This commit is contained in:
parent
9296cef55e
commit
083629c6bf
7 changed files with 252 additions and 87 deletions
|
@ -126,6 +126,8 @@ final class NavigationPopup extends PopupPanel {
|
|||
}
|
||||
StartPosition = null;
|
||||
Application.hideActivePopup();
|
||||
getReader().getViewWidget().reset();
|
||||
getReader().getViewWidget().repaint();
|
||||
}
|
||||
};
|
||||
btnOk.setOnClickListener(listener);
|
||||
|
|
|
@ -66,8 +66,15 @@ public class ApiServerImplementation extends ApiInterface.Stub implements ApiMet
|
|||
setPageStart((TextPosition)parameters[0]);
|
||||
return ApiObject.Void.Instance;
|
||||
case HIGHLIGHT_AREA:
|
||||
{
|
||||
myReader.getTextView().highlight(
|
||||
getZLTextPosition((TextPosition)parameters[0]),
|
||||
getZLTextPosition((TextPosition)parameters[1])
|
||||
);
|
||||
return ApiObject.Void.Instance;
|
||||
}
|
||||
case CLEAR_HIGHLIGHTING:
|
||||
myReader.getTextView().clearHighlighting();
|
||||
return ApiObject.Void.Instance;
|
||||
default:
|
||||
return unsupportedMethodError(method);
|
||||
|
@ -96,6 +103,14 @@ public class ApiServerImplementation extends ApiInterface.Stub implements ApiMet
|
|||
);
|
||||
}
|
||||
|
||||
private ZLTextFixedPosition getZLTextPosition(TextPosition position) {
|
||||
return new ZLTextFixedPosition(
|
||||
position.ParagraphIndex,
|
||||
position.ElementIndex,
|
||||
position.CharIndex
|
||||
);
|
||||
}
|
||||
|
||||
private boolean isPageEndOfSection() {
|
||||
final ZLTextWordCursor cursor = myReader.getTextView().getEndCursor();
|
||||
return cursor.isEndOfParagraph() && cursor.getParagraphCursor().isEndOfSection();
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (C) 2007-2011 Geometer Plus <contact@geometerplus.com>
|
||||
*
|
||||
* 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;
|
||||
|
||||
interface ZLTextAbstractHighlighting {
|
||||
boolean clear();
|
||||
|
||||
boolean isEmpty();
|
||||
ZLTextPosition getStartPosition();
|
||||
ZLTextPosition getEndPosition();
|
||||
ZLTextElementArea getStartArea(ZLTextPage page);
|
||||
ZLTextElementArea getEndArea(ZLTextPage page);
|
||||
}
|
|
@ -28,25 +28,35 @@ final class ZLTextElementAreaVector {
|
|||
Collections.synchronizedList(new ArrayList<ZLTextRegion>());
|
||||
private ZLTextRegion myCurrentElementRegion;
|
||||
|
||||
public void clear() {
|
||||
void clear() {
|
||||
myElementRegions.clear();
|
||||
myCurrentElementRegion = null;
|
||||
myAreas.clear();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return myAreas.isEmpty();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return myAreas.size();
|
||||
}
|
||||
|
||||
// TODO: remove this unsafe method
|
||||
public ZLTextElementArea get(int index) {
|
||||
return myAreas.get(index);
|
||||
}
|
||||
|
||||
public ZLTextElementArea getFirstArea() {
|
||||
synchronized (myAreas) {
|
||||
return myAreas.isEmpty() ? null : myAreas.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
public ZLTextElementArea getLastArea() {
|
||||
synchronized (myAreas) {
|
||||
return myAreas.isEmpty() ? null : myAreas.get(myAreas.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean add(ZLTextElementArea area) {
|
||||
synchronized (myAreas) {
|
||||
if (myCurrentElementRegion != null
|
||||
&& myCurrentElementRegion.getSoul().accepts(area)) {
|
||||
myCurrentElementRegion.extend();
|
||||
|
@ -61,7 +71,7 @@ final class ZLTextElementAreaVector {
|
|||
soul = new ZLTextWordRegionSoul(area, (ZLTextWord)area.Element);
|
||||
}
|
||||
if (soul != null) {
|
||||
myCurrentElementRegion = new ZLTextRegion(soul, myAreas, size());
|
||||
myCurrentElementRegion = new ZLTextRegion(soul, myAreas, myAreas.size());
|
||||
myElementRegions.add(myCurrentElementRegion);
|
||||
} else {
|
||||
myCurrentElementRegion = null;
|
||||
|
@ -69,13 +79,38 @@ final class ZLTextElementAreaVector {
|
|||
}
|
||||
return myAreas.add(area);
|
||||
}
|
||||
}
|
||||
|
||||
ZLTextElementArea getFirstAfter(ZLTextPosition position) {
|
||||
synchronized (myAreas) {
|
||||
for (ZLTextElementArea area : myAreas) {
|
||||
if (position.compareTo(area) <= 0) {
|
||||
return area;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
ZLTextElementArea getLastBefore(ZLTextPosition position) {
|
||||
synchronized (myAreas) {
|
||||
for (int i = myAreas.size() - 1; i >= 0; --i) {
|
||||
final ZLTextElementArea area = myAreas.get(i);
|
||||
if (position.compareTo(area) > 0) {
|
||||
return area;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
ZLTextElementArea binarySearch(int x, int y) {
|
||||
synchronized (myAreas) {
|
||||
int left = 0;
|
||||
int right = size();
|
||||
int right = myAreas.size();
|
||||
while (left < right) {
|
||||
final int middle = (left + right) / 2;
|
||||
final ZLTextElementArea candidate = get(middle);
|
||||
final ZLTextElementArea candidate = myAreas.get(middle);
|
||||
if (candidate.YStart > y) {
|
||||
right = middle;
|
||||
} else if (candidate.YEnd < y) {
|
||||
|
@ -90,6 +125,7 @@ final class ZLTextElementAreaVector {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
ZLTextRegion getRegion(ZLTextRegion.Soul soul) {
|
||||
if (soul == null) {
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (C) 2007-2011 Geometer Plus <contact@geometerplus.com>
|
||||
*
|
||||
* 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;
|
||||
|
||||
class ZLTextHighlighting implements ZLTextAbstractHighlighting {
|
||||
private ZLTextPosition myStartPosition;
|
||||
private ZLTextPosition myEndPosition;
|
||||
|
||||
void setup(ZLTextPosition start, ZLTextPosition end) {
|
||||
myStartPosition = new ZLTextFixedPosition(start);
|
||||
myEndPosition = new ZLTextFixedPosition(end);
|
||||
}
|
||||
|
||||
public boolean clear() {
|
||||
if (isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
myStartPosition = null;
|
||||
myEndPosition = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return myStartPosition == null;
|
||||
}
|
||||
|
||||
public ZLTextPosition getStartPosition() {
|
||||
return myStartPosition;
|
||||
}
|
||||
|
||||
public ZLTextPosition getEndPosition() {
|
||||
return myEndPosition;
|
||||
}
|
||||
|
||||
public ZLTextElementArea getStartArea(ZLTextPage page) {
|
||||
return page.TextElementMap.getFirstAfter(myStartPosition);
|
||||
}
|
||||
|
||||
public ZLTextElementArea getEndArea(ZLTextPage page) {
|
||||
return page.TextElementMap.getLastBefore(myEndPosition);
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
package org.geometerplus.zlibrary.text.view;
|
||||
|
||||
public class ZLTextSelection {
|
||||
class ZLTextSelection implements ZLTextAbstractHighlighting {
|
||||
static class Point {
|
||||
int X;
|
||||
int Y;
|
||||
|
@ -45,11 +45,11 @@ public class ZLTextSelection {
|
|||
myView = view;
|
||||
}
|
||||
|
||||
boolean isEmpty() {
|
||||
public boolean isEmpty() {
|
||||
return myLeftMostRegionSoul == null;
|
||||
}
|
||||
|
||||
boolean clear() {
|
||||
public boolean clear() {
|
||||
if (isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -103,7 +103,9 @@ public class ZLTextSelection {
|
|||
}
|
||||
|
||||
final ZLTextElementAreaVector vector = myView.myCurrentPage.TextElementMap;
|
||||
if (!vector.isEmpty() && y < vector.get(0).YStart) {
|
||||
final ZLTextElementArea firstArea = vector.getFirstArea();
|
||||
final ZLTextElementArea lastArea = vector.getLastArea();
|
||||
if (firstArea != null && y < firstArea.YStart) {
|
||||
if (myScroller != null && myScroller.scrollsForward()) {
|
||||
myScroller.stop();
|
||||
myScroller = null;
|
||||
|
@ -112,7 +114,7 @@ public class ZLTextSelection {
|
|||
myScroller = new Scroller(false, x, y);
|
||||
return;
|
||||
}
|
||||
} else if (!vector.isEmpty() && y + ZLTextSelectionCursor.getHeight() / 2 + ZLTextSelectionCursor.getAccent() / 2 > vector.get(vector.size() - 1).YEnd) {
|
||||
} else if (lastArea != null && y + ZLTextSelectionCursor.getHeight() / 2 + ZLTextSelectionCursor.getAccent() / 2 > lastArea.YEnd) {
|
||||
if (myScroller != null && !myScroller.scrollsForward()) {
|
||||
myScroller.stop();
|
||||
myScroller = null;
|
||||
|
@ -181,7 +183,7 @@ public class ZLTextSelection {
|
|||
&& myRightMostRegionSoul.compareTo(area) >= 0;
|
||||
}
|
||||
|
||||
ZLTextPosition getStartPosition() {
|
||||
public ZLTextPosition getStartPosition() {
|
||||
if (isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
@ -192,7 +194,7 @@ public class ZLTextSelection {
|
|||
);
|
||||
}
|
||||
|
||||
ZLTextPosition getEndPosition() {
|
||||
public ZLTextPosition getEndPosition() {
|
||||
if (isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
@ -203,38 +205,34 @@ public class ZLTextSelection {
|
|||
);
|
||||
}
|
||||
|
||||
ZLTextElementArea getStartArea(ZLTextPage page) {
|
||||
public 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 (myLeftMostRegionSoul.compareTo(vector.get(0)) <= 0) {
|
||||
return vector.get(0);
|
||||
final ZLTextElementArea firstArea = vector.getFirstArea();
|
||||
if (firstArea != null && myLeftMostRegionSoul.compareTo(firstArea) <= 0) {
|
||||
return firstArea;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
ZLTextElementArea getEndArea(ZLTextPage page) {
|
||||
public 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);
|
||||
final ZLTextElementArea lastArea = vector.getLastArea();
|
||||
if (lastArea != null && myRightMostRegionSoul.compareTo(lastArea) >= 0) {
|
||||
return lastArea;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -244,10 +242,10 @@ public class ZLTextSelection {
|
|||
return false;
|
||||
}
|
||||
final ZLTextElementAreaVector vector = page.TextElementMap;
|
||||
if (vector.isEmpty()) {
|
||||
final ZLTextElementArea firstPageArea = page.TextElementMap.getFirstArea();
|
||||
if (firstPageArea == null) {
|
||||
return false;
|
||||
}
|
||||
final ZLTextElementArea firstPageArea = vector.get(0);
|
||||
final int cmp = myLeftMostRegionSoul.compareTo(firstPageArea);
|
||||
return cmp < 0 || (cmp == 0 && !firstPageArea.isFirstInElement());
|
||||
}
|
||||
|
@ -256,11 +254,10 @@ public class ZLTextSelection {
|
|||
if (isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
final ZLTextElementAreaVector vector = page.TextElementMap;
|
||||
if (vector.isEmpty()) {
|
||||
final ZLTextElementArea lastPageArea = page.TextElementMap.getLastArea();
|
||||
if (lastPageArea == null) {
|
||||
return false;
|
||||
}
|
||||
final ZLTextElementArea lastPageArea = vector.get(vector.size() - 1);
|
||||
final int cmp = myRightMostRegionSoul.compareTo(lastPageArea);
|
||||
return cmp > 0 || (cmp == 0 && !lastPageArea.isLastInElement());
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.geometerplus.zlibrary.core.application.ZLApplication;
|
|||
import org.geometerplus.zlibrary.core.view.ZLPaintContext;
|
||||
import org.geometerplus.zlibrary.core.filesystem.ZLFile;
|
||||
import org.geometerplus.zlibrary.core.filesystem.ZLResourceFile;
|
||||
import org.geometerplus.zlibrary.core.util.ZLColor;
|
||||
|
||||
import org.geometerplus.zlibrary.text.model.*;
|
||||
import org.geometerplus.zlibrary.text.hyphenation.*;
|
||||
|
@ -56,9 +57,16 @@ public abstract class ZLTextView extends ZLTextViewBase {
|
|||
|
||||
private final HashMap<ZLTextLineInfo,ZLTextLineInfo> myLineInfoCache = new HashMap<ZLTextLineInfo,ZLTextLineInfo>();
|
||||
|
||||
private ZLTextRegion.Soul mySelectedRegionSoul;
|
||||
private boolean myHighlightSelectedRegion = true;
|
||||
|
||||
private ZLTextSelection mySelection;
|
||||
private ZLTextHighlighting myHighlighting;
|
||||
|
||||
public ZLTextView(ZLApplication application) {
|
||||
super(application);
|
||||
mySelection = new ZLTextSelection(this);
|
||||
myHighlighting = new ZLTextHighlighting();
|
||||
}
|
||||
|
||||
public synchronized void setModel(ZLTextModel model) {
|
||||
|
@ -239,6 +247,19 @@ public abstract class ZLTextView extends ZLTextViewBase {
|
|||
}
|
||||
}
|
||||
|
||||
public void highlight(ZLTextPosition start, ZLTextPosition end) {
|
||||
myHighlighting.setup(start, end);
|
||||
Application.getViewWidget().reset();
|
||||
Application.getViewWidget().repaint();
|
||||
}
|
||||
|
||||
public void clearHighlighting() {
|
||||
if (myHighlighting.clear()) {
|
||||
Application.getViewWidget().reset();
|
||||
Application.getViewWidget().repaint();
|
||||
}
|
||||
}
|
||||
|
||||
protected void moveSelectionCursorTo(ZLTextSelectionCursor cursor, int x, int y) {
|
||||
y -= ZLTextSelectionCursor.getHeight() / 2 + ZLTextSelectionCursor.getAccent() / 2;
|
||||
mySelection.setCursorInMovement(cursor, x, y);
|
||||
|
@ -647,16 +668,15 @@ public abstract class ZLTextView extends ZLTextViewBase {
|
|||
preparePaintInfo();
|
||||
}
|
||||
|
||||
private static final char[] SPACE = new char[] { ' ' };
|
||||
private void drawTextLine(ZLTextPage page, ZLTextLineInfo info, int from, int to, int y) {
|
||||
final ZLTextParagraphCursor paragraph = info.ParagraphCursor;
|
||||
final ZLPaintContext context = myContext;
|
||||
|
||||
if (!mySelection.isEmpty() && from != to) {
|
||||
private void drawBackgroung(
|
||||
ZLTextAbstractHighlighting highligting, ZLColor color,
|
||||
ZLTextPage page, ZLTextLineInfo info, int from, int to, int y
|
||||
) {
|
||||
if (!highligting.isEmpty() && from != to) {
|
||||
final ZLTextElementArea fromArea = page.TextElementMap.get(from);
|
||||
final ZLTextElementArea toArea = page.TextElementMap.get(to - 1);
|
||||
final ZLTextElementArea selectionStartArea = mySelection.getStartArea(page);
|
||||
final ZLTextElementArea selectionEndArea = mySelection.getEndArea(page);
|
||||
final ZLTextElementArea selectionStartArea = highligting.getStartArea(page);
|
||||
final ZLTextElementArea selectionEndArea = highligting.getEndArea(page);
|
||||
if (selectionStartArea != null
|
||||
&& selectionEndArea != null
|
||||
&& selectionStartArea.compareTo(toArea) <= 0
|
||||
|
@ -674,11 +694,19 @@ public abstract class ZLTextView extends ZLTextViewBase {
|
|||
} else {
|
||||
right = selectionEndArea.XEnd;
|
||||
}
|
||||
context.setFillColor(getSelectedBackgroundColor());
|
||||
context.fillRectangle(left, top, right, bottom);
|
||||
myContext.setFillColor(color);
|
||||
myContext.fillRectangle(left, top, right, bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final char[] SPACE = new char[] { ' ' };
|
||||
private void drawTextLine(ZLTextPage page, ZLTextLineInfo info, int from, int to, int y) {
|
||||
drawBackgroung(mySelection, getSelectedBackgroundColor(), page, info, from, to, y);
|
||||
drawBackgroung(myHighlighting, getHighlightingColor(), page, info, from, to, y);
|
||||
|
||||
final ZLPaintContext context = myContext;
|
||||
final ZLTextParagraphCursor paragraph = info.ParagraphCursor;
|
||||
int index = from;
|
||||
final int endElementIndex = info.EndElementIndex;
|
||||
int charIndex = info.RealStartCharIndex;
|
||||
|
@ -1348,10 +1376,6 @@ public abstract class ZLTextView extends ZLTextViewBase {
|
|||
return false;
|
||||
}
|
||||
|
||||
private ZLTextRegion.Soul mySelectedRegionSoul;
|
||||
private ZLTextSelection mySelection;
|
||||
private boolean myHighlightSelectedRegion = true;
|
||||
|
||||
public void hideSelectedRegionBorder() {
|
||||
myHighlightSelectedRegion = false;
|
||||
Application.getViewWidget().reset();
|
||||
|
@ -1399,8 +1423,7 @@ public abstract class ZLTextView extends ZLTextViewBase {
|
|||
}
|
||||
|
||||
public int getSelectionStartY() {
|
||||
final ZLTextElementAreaVector vector = myCurrentPage.TextElementMap;
|
||||
if (mySelection.isEmpty() || vector.isEmpty()) {
|
||||
if (mySelection.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
final ZLTextElementArea selectionStartArea = mySelection.getStartArea(myCurrentPage);
|
||||
|
@ -1408,15 +1431,16 @@ public abstract class ZLTextView extends ZLTextViewBase {
|
|||
return selectionStartArea.YStart;
|
||||
}
|
||||
if (mySelection.hasAPartBeforePage(myCurrentPage)) {
|
||||
return vector.get(0).YStart;
|
||||
final ZLTextElementArea firstArea = myCurrentPage.TextElementMap.getFirstArea();
|
||||
return firstArea != null ? firstArea.YStart : 0;
|
||||
} else {
|
||||
return vector.get(vector.size() - 1).YEnd;
|
||||
final ZLTextElementArea lastArea = myCurrentPage.TextElementMap.getLastArea();
|
||||
return lastArea != null ? lastArea.YEnd : 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int getSelectionEndY() {
|
||||
final ZLTextElementAreaVector vector = myCurrentPage.TextElementMap;
|
||||
if (mySelection.isEmpty() || vector.isEmpty()) {
|
||||
if (mySelection.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
final ZLTextElementArea selectionEndArea = mySelection.getEndArea(myCurrentPage);
|
||||
|
@ -1424,9 +1448,11 @@ public abstract class ZLTextView extends ZLTextViewBase {
|
|||
return selectionEndArea.YEnd;
|
||||
}
|
||||
if (mySelection.hasAPartAfterPage(myCurrentPage)) {
|
||||
return vector.get(vector.size() - 1).YEnd;
|
||||
final ZLTextElementArea lastArea = myCurrentPage.TextElementMap.getLastArea();
|
||||
return lastArea != null ? lastArea.YEnd : 0;
|
||||
} else {
|
||||
return vector.get(0).YStart;
|
||||
final ZLTextElementArea firstArea = myCurrentPage.TextElementMap.getFirstArea();
|
||||
return firstArea != null ? firstArea.YStart : 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue