1
0
Fork 0
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:
Nikolay Pultsin 2011-06-19 16:18:48 +01:00
parent 9296cef55e
commit 083629c6bf
7 changed files with 252 additions and 87 deletions

View file

@ -126,6 +126,8 @@ final class NavigationPopup extends PopupPanel {
}
StartPosition = null;
Application.hideActivePopup();
getReader().getViewWidget().reset();
getReader().getViewWidget().repaint();
}
};
btnOk.setOnClickListener(listener);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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