1
0
Fork 0
mirror of https://github.com/geometer/FBReaderJ.git synced 2025-10-05 19:42:17 +02:00

better hyperlink support

git-svn-id: https://only.mawhrin.net/repos/FBReaderJ/trunk@984 6a642e6f-84f6-412e-ac94-c4a38d5a04b0
This commit is contained in:
Nikolay Pultsin 2009-06-06 13:42:47 +00:00
parent 5c3dfbec58
commit 78da337690
20 changed files with 407 additions and 94 deletions

View file

@ -1 +1 @@
<!ENTITY FBReaderVersion "0.5.3">
<!ENTITY FBReaderVersion "0.5.4">

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.geometerplus.zlibrary.ui.android" android:versionCode="503" android:versionName="0.5.3">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.geometerplus.zlibrary.ui.android" android:versionCode="504" android:versionName="0.5.4">
<uses-permission android:name="android.permission.INTERNET" />
<application android:name="org.geometerplus.zlibrary.ui.android.library.ZLAndroidApplication" android:icon="@drawable/fbreader" android:label="FBReader">
<activity android:name="org.geometerplus.android.fbreader.FBReader" android:icon="@drawable/fbreader" android:label="FBReader" android:configChanges="orientation|keyboardHidden">

View file

@ -3,4 +3,6 @@
<binding key="&lt;VolumeDown&gt;" action="volumeKeyScrollForward"/>
<binding key="&lt;VolumeUp&gt;" action="volumeKeyScrollBackward"/>
<binding key="&lt;Back&gt;" action="cancel"/>
<binding key="&lt;Enter&gt;" action="followHyperlink"/>
<binding key="&lt;PadCenter&gt;" action="followHyperlink"/>
</keymap>

View file

@ -35,7 +35,6 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import org.geometerplus.zlibrary.core.dialogs.ZLDialogManager;
//import org.geometerplus.zlibrary.ui.android.dialogs.ZLAndroidDialogManager;
import org.geometerplus.zlibrary.ui.android.library.ZLAndroidApplication;
import org.geometerplus.zlibrary.ui.android.R;
@ -46,6 +45,20 @@ public class BookDownloader extends Activity {
private static int ourDownloadedPart = 0;
private static String ourFileName = "";
public static boolean acceptsUri(Uri uri) {
final List<String> path = uri.getPathSegments();
if ((path == null) || path.isEmpty()) {
return false;
}
final String fileName = path.get(path.size() - 1);
return
fileName.endsWith(".fb2.zip") ||
fileName.endsWith(".fb2") ||
fileName.endsWith(".epub") ||
fileName.endsWith(".mobi");
}
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
@ -64,12 +77,7 @@ public class BookDownloader extends Activity {
if (uri != null) {
intent.setData(null);
final List<String> path = uri.getPathSegments();
ourFileName = path.get(path.size() - 1);
if (!ourFileName.endsWith(".fb2.zip") &&
!ourFileName.endsWith(".fb2") &&
!ourFileName.endsWith(".epub") &&
!ourFileName.endsWith(".mobi")) {
if (!acceptsUri(uri)) {
startNextMatchingActivity(intent);
finish();
return;
@ -80,6 +88,7 @@ public class BookDownloader extends Activity {
host = "feedbooks.com";
}
String dir = "/sdcard/Books/" + host;
final List<String> path = uri.getPathSegments();
for (int i = 0; i < path.size() - 1; ++i) {
dir += '/' + path.get(i);
}
@ -91,6 +100,7 @@ public class BookDownloader extends Activity {
return;
}
ourFileName = path.get(path.size() - 1);
final File fileFile = new File(dirFile, ourFileName);
if (fileFile.exists()) {
if (!fileFile.isFile()) {

View file

@ -41,7 +41,6 @@ class BookTitlePreference extends ZLStringPreference {
setValue(book.getTitle());
}
@Override
public void onAccept() {
myBook.setTitle(getValue());
}
@ -78,7 +77,6 @@ class LanguagePreference extends ZLStringListPreference {
}
}
@Override
public void onAccept() {
final String value = getValue();
myBook.setLanguage((value.length() != 0) ? value : null);

View file

@ -39,7 +39,6 @@ class ZLBooleanPreference extends CheckBoxPreference implements ZLPreference {
setChecked(option.getValue());
}
@Override
public void onAccept() {
myOption.setValue(isChecked());
}

View file

@ -30,10 +30,8 @@ abstract class ZLSimplePreference extends Preference implements ZLPreference, Pr
setOnPreferenceClickListener(this);
}
@Override
public abstract void onAccept();
@Override
public boolean onPreferenceClick(Preference preference) {
onClick();
return true;

View file

@ -36,6 +36,8 @@ import org.geometerplus.zlibrary.ui.android.view.ZLAndroidPaintContext;
import org.geometerplus.zlibrary.ui.android.view.ZLAndroidWidget;
import org.geometerplus.zlibrary.ui.android.dialogs.ZLAndroidDialogManager;
import org.geometerplus.android.fbreader.BookDownloader;
public final class ZLAndroidLibrary extends ZLibrary {
private ZLAndroidActivity myActivity;
private final Application myApplication;
@ -69,8 +71,12 @@ public final class ZLAndroidLibrary extends ZLibrary {
}
public void openInBrowser(String reference) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(reference));
final Intent intent = new Intent(Intent.ACTION_VIEW);
final Uri uri = Uri.parse(reference);
if (BookDownloader.acceptsUri(uri)) {
intent.setClass(myActivity, BookDownloader.class);
}
intent.setData(uri);
myActivity.startActivity(intent);
}

View file

@ -33,9 +33,10 @@ import org.geometerplus.zlibrary.ui.android.image.ZLAndroidImageData;
public final class ZLAndroidPaintContext extends ZLPaintContext {
private Canvas myCanvas;
final Paint Paint;
private ZLColor myColor = new ZLColor(0, 0, 0);
private ZLColor myFillColor = new ZLColor(0, 0, 0);
private final Paint myTextPaint = new Paint();
private final Paint myLinePaint = new Paint();
private final Paint myFillPaint = new Paint();
private final Paint myOutlinePaint = new Paint();
private int myWidth;
private int myHeight;
@ -56,10 +57,17 @@ public final class ZLAndroidPaintContext extends ZLPaintContext {
private HashMap<String,Typeface[]> myTypefaces = new HashMap<String,Typeface[]>();
private ZLAndroidPaintContext() {
Paint = new Paint();
Paint.setLinearText(false);
Paint.setAntiAlias(true);
Paint.setSubpixelText(false);
myTextPaint.setLinearText(false);
myTextPaint.setAntiAlias(true);
myTextPaint.setSubpixelText(false);
myOutlinePaint.setColor(Color.rgb(255, 127, 0));
myOutlinePaint.setAntiAlias(true);
myOutlinePaint.setDither(true);
myOutlinePaint.setStrokeWidth(4);
myOutlinePaint.setStyle(Paint.Style.STROKE);
myOutlinePaint.setPathEffect(new CornerPathEffect(5));
myOutlinePaint.setMaskFilter(new EmbossMaskFilter(new float[] {1, 1, 1}, .4f, 6f, 3.5f));
}
void setSize(int width, int height, int scrollbarWidth) {
@ -99,15 +107,45 @@ public final class ZLAndroidPaintContext extends ZLPaintContext {
}
public void clear(ZLColor color) {
// TODO: implement
myColor = color;
Paint.setColor(Color.rgb(color.Red, color.Green, color.Blue));
myCanvas.drawRect(0, 0, myWidth + myScrollbarWidth, myHeight, Paint);
myFillPaint.setColor(Color.rgb(color.Red, color.Green, color.Blue));
myCanvas.drawRect(0, 0, myWidth + myScrollbarWidth, myHeight, myFillPaint);
}
public void drawOutline(int[] xs, int ys[]) {
final int last = xs.length - 1;
int xStart = (xs[0] + xs[last]) / 2;
int yStart = (ys[0] + ys[last]) / 2;
int xEnd = xStart;
int yEnd = yStart;
if (xs[0] != xs[last]) {
if (xs[0] > xs[last]) {
xStart -= 5;
xEnd += 5;
} else {
xStart += 5;
xEnd -= 5;
}
} else {
if (ys[0] > ys[last]) {
yStart -= 5;
yEnd += 5;
} else {
yStart += 5;
yEnd -= 5;
}
}
final Path path = new Path();
path.moveTo(xStart, yStart);
for (int i = 0; i <= last; ++i) {
path.lineTo(xs[i], ys[i]);
}
path.lineTo(xEnd, yEnd);
myCanvas.drawPath(path, myOutlinePaint);
}
protected void setFontInternal(String family, int size, boolean bold, boolean italic, boolean underline) {
final int style = (bold ? Typeface.BOLD : 0) | (italic ? Typeface.ITALIC : 0);
final Paint paint = Paint;
Typeface[] typefaces = myTypefaces.get(family);
if (typefaces == null) {
typefaces = new Typeface[4];
@ -118,22 +156,23 @@ public final class ZLAndroidPaintContext extends ZLPaintContext {
typeface = Typeface.create(family, style);
typefaces[style] = typeface;
}
paint.setTypeface(typeface);
paint.setTextSize(size);
paint.setUnderlineText(underline);
myTextPaint.setTypeface(typeface);
myTextPaint.setTextSize(size);
myTextPaint.setUnderlineText(underline);
}
public void setColor(ZLColor color, int style) {
public void setTextColor(ZLColor color) {
myTextPaint.setColor(Color.rgb(color.Red, color.Green, color.Blue));
}
public void setLineColor(ZLColor color, int style) {
// TODO: use style
if (!myColor.equals(color)) {
myColor = color;
Paint.setColor(Color.rgb(color.Red, color.Green, color.Blue));
}
myLinePaint.setColor(Color.rgb(color.Red, color.Green, color.Blue));
}
public void setFillColor(ZLColor color, int style) {
// TODO: use style
myFillColor = color;
myFillPaint.setColor(Color.rgb(color.Red, color.Green, color.Blue));
}
public int getWidth() {
@ -144,19 +183,19 @@ public final class ZLAndroidPaintContext extends ZLPaintContext {
}
public int getStringWidth(char[] string, int offset, int length) {
return (int)(Paint.measureText(string, offset, length) + 0.5f);
return (int)(myTextPaint.measureText(string, offset, length) + 0.5f);
}
protected int getSpaceWidthInternal() {
return (int)(Paint.measureText(" ", 0, 1) + 0.5f);
return (int)(myTextPaint.measureText(" ", 0, 1) + 0.5f);
}
protected int getStringHeightInternal() {
return (int)(Paint.getTextSize() + 0.5f);
return (int)(myTextPaint.getTextSize() + 0.5f);
}
protected int getDescentInternal() {
return (int)(Paint.descent() + 0.5f);
return (int)(myTextPaint.descent() + 0.5f);
}
public void drawString(int x, int y, char[] string, int offset, int length) {
myCanvas.drawText(string, offset, length, x, y, Paint);
myCanvas.drawText(string, offset, length, x, y, myTextPaint);
}
public int imageWidth(ZLImageData imageData) {
@ -172,7 +211,7 @@ public final class ZLAndroidPaintContext extends ZLPaintContext {
public void drawImage(int x, int y, ZLImageData imageData) {
Bitmap bitmap = ((ZLAndroidImageData)imageData).getBitmap(myWidth, myHeight);
if (bitmap != null) {
myCanvas.drawBitmap(bitmap, x, y - bitmap.getHeight(), Paint);
myCanvas.drawBitmap(bitmap, x, y - bitmap.getHeight(), myFillPaint);
}
}
@ -185,8 +224,8 @@ public final class ZLAndroidPaintContext extends ZLPaintContext {
++y0;
++y1;
}
final Paint paint = Paint;
final Canvas canvas = myCanvas;
final Paint paint = myLinePaint;
paint.setAntiAlias(false);
canvas.drawLine(x0, y0, x1, y1, paint);
canvas.drawPoint(x0, y0, paint);
@ -195,8 +234,6 @@ public final class ZLAndroidPaintContext extends ZLPaintContext {
}
public void fillRectangle(int x0, int y0, int x1, int y1) {
ZLColor color = myFillColor;
Paint.setColor(Color.rgb(color.Red, color.Green, color.Blue));
if (x1 < x0) {
int swap = x1;
x1 = x0;
@ -207,9 +244,7 @@ public final class ZLAndroidPaintContext extends ZLPaintContext {
y1 = y0;
y0 = swap;
}
myCanvas.drawRect(x0, y0, x1 + 1, y1 + 1, Paint);
color = myColor;
Paint.setColor(Color.rgb(color.Red, color.Green, color.Blue));
myCanvas.drawRect(x0, y0, x1 + 1, y1 + 1, myFillPaint);
}
public void drawFilledCircle(int x, int y, int r) {
// TODO: implement

View file

@ -22,8 +22,7 @@ package org.geometerplus.zlibrary.ui.android.view;
import java.util.Map;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Bitmap;
import android.graphics.*;
import android.view.*;
import android.util.AttributeSet;
@ -35,6 +34,7 @@ import org.geometerplus.zlibrary.ui.android.util.ZLAndroidKeyUtil;
public class ZLAndroidWidget extends View {
private ZLAndroidViewWidget myViewWidget;
private final Paint myPaint = new Paint();
private Bitmap myMainBitmap;
private Bitmap mySecondaryBitmap;
private boolean mySecondaryBitmapIsUpToDate;
@ -135,7 +135,7 @@ public class ZLAndroidWidget extends View {
myMainBitmap,
horizontal ? myScrollingShift : 0,
horizontal ? 0 : myScrollingShift,
context.Paint
myPaint
);
final int size = horizontal ? w : h;
int shift = (myScrollingShift < 0) ? (myScrollingShift + size) : (myScrollingShift - size);
@ -143,7 +143,7 @@ public class ZLAndroidWidget extends View {
mySecondaryBitmap,
horizontal ? shift : 0,
horizontal ? 0 : shift,
context.Paint
myPaint
);
if (stopScrolling) {
final ZLView view = ZLApplication.Instance().getCurrentView();
@ -165,10 +165,11 @@ public class ZLAndroidWidget extends View {
shift += size;
}
// TODO: set color
myPaint.setColor(Color.rgb(127, 127, 127));
if (horizontal) {
canvas.drawLine(shift, 0, shift, h + 1, context.Paint);
canvas.drawLine(shift, 0, shift, h + 1, myPaint);
} else {
canvas.drawLine(0, shift, w + 1, shift, context.Paint);
canvas.drawLine(0, shift, w + 1, shift, myPaint);
}
if (myScrollingInProgress) {
postInvalidate();
@ -297,12 +298,16 @@ public class ZLAndroidWidget extends View {
private void onDrawStatic(Canvas canvas) {
drawOnBitmap(myMainBitmap);
canvas.drawBitmap(myMainBitmap, 0, 0, ZLAndroidPaintContext.Instance().Paint);
canvas.drawBitmap(myMainBitmap, 0, 0, myPaint);
}
@Override
public boolean onTrackballEvent(MotionEvent event) {
ZLApplication.Instance().getCurrentView().onTrackballRotated((int)(10 * event.getX()), (int)(10 * event.getY()));
if (event.getAction() == MotionEvent.ACTION_DOWN) {
onKeyDown(KeyEvent.KEYCODE_DPAD_CENTER, null);
} else {
ZLApplication.Instance().getCurrentView().onTrackballRotated((int)(10 * event.getX()), (int)(10 * event.getY()));
}
return true;
}
@ -359,6 +364,8 @@ public class ZLAndroidWidget extends View {
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_BACK:
case KeyEvent.KEYCODE_ENTER:
case KeyEvent.KEYCODE_DPAD_CENTER:
return ZLApplication.Instance().doActionByKey(ZLAndroidKeyUtil.getKeyNameByCode(keyCode));
case KeyEvent.KEYCODE_DPAD_DOWN:
ZLApplication.Instance().getCurrentView().onTrackballRotated(0, 1);
@ -376,6 +383,8 @@ public class ZLAndroidWidget extends View {
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_BACK:
case KeyEvent.KEYCODE_ENTER:
case KeyEvent.KEYCODE_DPAD_CENTER:
return true;
default:
return false;

View file

@ -49,4 +49,5 @@ public interface ActionCode {
String COPY_SELECTED_TEXT_TO_CLIPBOARD = "copyToClipboard";
String CLEAR_SELECTION = "clearSelection";
String OPEN_SELECTED_TEXT_IN_DICTIONARY = "openInDictionary";
String FOLLOW_HYPERLINK = "followHyperlink";
};

View file

@ -120,6 +120,7 @@ public final class FBReader extends ZLApplication {
//addAction(ActionCode.COPY_SELECTED_TEXT_TO_CLIPBOARD, new DummyAction(this));
//addAction(ActionCode.OPEN_SELECTED_TEXT_IN_DICTIONARY, new DummyAction(this));
//addAction(ActionCode.CLEAR_SELECTION, new DummyAction(this));
addAction(ActionCode.FOLLOW_HYPERLINK, new FollowHyperlinkAction(this));
BookTextView = new FBView(this);
FootnoteView = new FBView(this);

View file

@ -70,6 +70,17 @@ public final class FBView extends ZLTextView {
}
}
void followHyperlink(ZLTextHyperlink hyperlink) {
switch (hyperlink.Type) {
case FBHyperlinkType.EXTERNAL:
ZLibrary.Instance().openInBrowser(hyperlink.Id);
break;
case FBHyperlinkType.INTERNAL:
((FBReader)ZLApplication.Instance()).tryOpenFootnote(hyperlink.Id);
break;
}
}
private int myStartX;
private int myStartY;
private boolean myIsManualScrollingActive;
@ -83,17 +94,12 @@ public final class FBView extends ZLTextView {
return false;
}
ZLTextElementArea area = getElementByCoordinates(x, y);
if (area != null) {
final ZLTextHyperlink hyperlink = area.Style.Hyperlink;
switch (hyperlink.Type) {
case FBHyperlinkType.EXTERNAL:
ZLibrary.Instance().openInBrowser(hyperlink.Id);
return true;
case FBHyperlinkType.INTERNAL:
((FBReader)ZLApplication.Instance()).tryOpenFootnote(hyperlink.Id);
return true;
}
final ZLTextHyperlink hyperlink = findHyperlink(x, y, 10);
if (hyperlink != null) {
selectHyperlink(hyperlink);
ZLApplication.Instance().repaintView();
followHyperlink(hyperlink);
return true;
}
final ScrollingPreferences preferences = ScrollingPreferences.Instance();

View file

@ -19,15 +19,13 @@
package org.geometerplus.fbreader.fbreader;
import org.geometerplus.zlibrary.text.view.ZLTextView;
class FindNextAction extends FBAction {
FindNextAction(FBReader fbreader) {
super(fbreader);
}
public boolean isEnabled() {
ZLTextView view = Reader.getTextView();
FBView view = Reader.getTextView();
return (view != null) && view.canFindNext();
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (C) 2009 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.fbreader.fbreader;
public class FollowHyperlinkAction extends FBAction {
FollowHyperlinkAction(FBReader fbreader) {
super(fbreader);
}
public boolean isEnabled() {
FBView view = Reader.getTextView();
return (view != null) && (view.getCurrentHyperlink() != null);
}
public void run() {
FBView view = Reader.getTextView();
view.followHyperlink(view.getCurrentHyperlink());
}
}

View file

@ -85,10 +85,11 @@ abstract public class ZLPaintContext {
abstract protected void setFontInternal(String family, int size, boolean bold, boolean italic, boolean underline);
final public void setColor(ZLColor color) {
setColor(color, LineStyle.SOLID_LINE);
abstract public void setTextColor(ZLColor color);
final public void setLineColor(ZLColor color) {
setLineColor(color, LineStyle.SOLID_LINE);
}
abstract public void setColor(ZLColor color, int style);
abstract public void setLineColor(ZLColor color, int style);
final public void setFillColor(ZLColor color) {
setFillColor(color, FillStyle.SOLID_FILL);
@ -142,6 +143,7 @@ abstract public class ZLPaintContext {
abstract public void drawLine(int x0, int y0, int x1, int y1);
abstract public void fillRectangle(int x0, int y0, int x1, int y1);
abstract public void drawFilledCircle(int x, int y, int r);
abstract public void drawOutline(int[] xs, int ys[]);
public ArrayList fontFamilies() {
if (myFamilies.isEmpty()) {

View file

@ -19,7 +19,7 @@
package org.geometerplus.zlibrary.text.view;
public final class ZLTextElementArea extends ZLTextFixedPosition {
final class ZLTextElementArea extends ZLTextFixedPosition {
final int XStart;
final int XEnd;
final int YStart;
@ -28,8 +28,8 @@ public final class ZLTextElementArea extends ZLTextFixedPosition {
final int Length;
final boolean AddHyphenationSign;
final boolean ChangeStyle;
public final ZLTextStyle Style;
public final ZLTextElement Element;
final ZLTextStyle Style;
final ZLTextElement Element;
ZLTextElementArea(int paragraphIndex, int elementIndex, int charIndex, int length, boolean addHyphenationSign, boolean changeStyle, ZLTextStyle style, ZLTextElement element, int xStart, int xEnd, int yStart, int yEnd) {
super(paragraphIndex, elementIndex, charIndex);

View file

@ -19,13 +19,16 @@
package org.geometerplus.zlibrary.text.view;
import java.util.List;
import java.util.*;
import org.geometerplus.zlibrary.core.view.ZLPaintContext;
public class ZLTextHyperlinkArea {
final ZLTextHyperlink Hyperlink;
private final List<ZLTextElementArea> myList;
private final int myFromIndex;
private int myToIndex;
private HorizontalConvexHull myHull;
ZLTextHyperlinkArea(ZLTextHyperlink hyperlink, List<ZLTextElementArea> list, int fromIndex) {
Hyperlink = hyperlink;
@ -42,9 +45,18 @@ public class ZLTextHyperlinkArea {
return myList.subList(myFromIndex, myToIndex);
}
public void draw(ZLPaintContext context) {
if (myHull == null) {
myHull = new HorizontalConvexHull();
}
myHull.draw(context);
}
public int distanceTo(int x, int y) {
// TODO: implement
return 20;
if (myHull == null) {
myHull = new HorizontalConvexHull();
}
return myHull.distanceTo(x, y);
}
public boolean equals(Object other) {
@ -53,4 +65,185 @@ public class ZLTextHyperlinkArea {
}
return Hyperlink == ((ZLTextHyperlinkArea)other).Hyperlink;
}
private final class HorizontalConvexHull {
private final LinkedList<Rectangle> myRectangles = new LinkedList<Rectangle>();
HorizontalConvexHull() {
for (ZLTextElementArea area : textAreas()) {
addArea(area);
}
normalize();
}
private void addArea(ZLTextElementArea area) {
if (myRectangles.isEmpty()) {
myRectangles.add(new Rectangle(area.XStart, area.XEnd, area.YStart, area.YEnd));
return;
}
final int top = area.YStart;
final int bottom = area.YEnd;
for (ListIterator<Rectangle> iter = myRectangles.listIterator(); iter.hasNext(); ) {
Rectangle r = iter.next();
if (r.Bottom <= top) {
continue;
}
if (r.Top >= bottom) {
break;
}
if (r.Top < top) {
final Rectangle before = new Rectangle(r);
before.Bottom = top;
r.Top = top;
iter.previous();
iter.add(before);
iter.next();
}
if (r.Bottom > bottom) {
final Rectangle after = new Rectangle(r);
after.Top = bottom;
r.Bottom = bottom;
iter.add(after);
}
r.Left = Math.min(r.Left, area.XStart);
r.Right = Math.max(r.Right, area.XEnd);
}
final Rectangle first = myRectangles.getFirst();
if (top < first.Top) {
myRectangles.add(0, new Rectangle(area.XStart, area.XEnd, top, Math.min(bottom, first.Top)));
}
final Rectangle last = myRectangles.getLast();
if (bottom > last.Bottom) {
myRectangles.add(new Rectangle(area.XStart, area.XEnd, Math.max(top, last.Bottom), bottom));
}
}
private void normalize() {
Rectangle previous = null;
for (ListIterator<Rectangle> iter = myRectangles.listIterator(); iter.hasNext(); ) {
final Rectangle current = iter.next();
if (previous != null) {
if ((previous.Left == current.Left) && (previous.Right == current.Right)) {
previous.Bottom = current.Bottom;
iter.remove();
continue;
}
if ((previous.Bottom != current.Top) &&
(current.Left <= previous.Right) &&
(previous.Left <= current.Right)) {
iter.previous();
iter.add(new Rectangle(
Math.max(previous.Left, current.Left),
Math.min(previous.Right, current.Right),
previous.Bottom,
current.Top
));
iter.next();
}
}
previous = current;
}
}
int distanceTo(int x, int y) {
int distance = Integer.MAX_VALUE;
for (Rectangle r : myRectangles) {
final int xd = (r.Left > x) ? r.Left - x : ((r.Right < x) ? x - r.Right : 0);
final int yd = (r.Top > y) ? r.Top - y : ((r.Bottom < y) ? y - r.Bottom : 0);
distance = Math.min(distance, Math.max(xd, yd));
if (distance == 0) {
break;
}
}
return distance;
}
void draw(ZLPaintContext context) {
final LinkedList<Rectangle> rectangles = new LinkedList<Rectangle>(myRectangles);
while (!rectangles.isEmpty()) {
final LinkedList<Rectangle> connected = new LinkedList<Rectangle>();
Rectangle previous = null;
for (final Iterator<Rectangle> iter = rectangles.iterator(); iter.hasNext(); ) {
final Rectangle current = iter.next();
if ((previous != null) &&
((previous.Left > current.Right) || (current.Left > previous.Right))) {
break;
}
iter.remove();
connected.add(current);
previous = current;
}
final LinkedList<Integer> xList = new LinkedList<Integer>();
final LinkedList<Integer> yList = new LinkedList<Integer>();
int x = 0, xPrev = 0;
final ListIterator<Rectangle> iter = connected.listIterator();
Rectangle r = iter.next();
x = r.Right + 2;
xList.add(x); yList.add(r.Top);
while (iter.hasNext()) {
xPrev = x;
r = iter.next();
x = r.Right + 2;
if (x != xPrev) {
final int y = (x < xPrev) ? r.Top + 2 : r.Top;
xList.add(xPrev); yList.add(y);
xList.add(x); yList.add(y);
}
}
xList.add(x); yList.add(r.Bottom + 2);
r = iter.previous();
x = r.Left - 2;
xList.add(x); yList.add(r.Bottom + 2);
while (iter.hasPrevious()) {
xPrev = x;
r = iter.previous();
x = r.Left - 2;
if (x != xPrev) {
final int y = (x > xPrev) ? r.Bottom : r.Bottom + 2;
xList.add(xPrev); yList.add(y);
xList.add(x); yList.add(y);
}
}
xList.add(x); yList.add(r.Top);
final int xs[] = new int[xList.size()];
final int ys[] = new int[yList.size()];
int count = 0;
for (int xx : xList) {
xs[count++] = xx;
}
count = 0;
for (int yy : yList) {
ys[count++] = yy;
}
context.drawOutline(xs, ys);
}
}
}
private static final class Rectangle {
int Left;
int Right;
int Top;
int Bottom;
Rectangle(int left, int right, int top, int bottom) {
Left = left;
Right = right;
Top = top;
Bottom = bottom;
}
Rectangle(Rectangle orig) {
Left = orig.Left;
Right = orig.Right;
Top = orig.Top;
Bottom = orig.Bottom;
}
}
}

View file

@ -19,9 +19,7 @@
package org.geometerplus.zlibrary.text.view;
import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.*;
import org.geometerplus.zlibrary.core.util.ZLColor;
@ -322,13 +320,7 @@ public abstract class ZLTextView extends ZLTextViewBase {
final ZLTextHyperlinkArea hyperlinkArea = getCurrentHyperlinkArea(page);
if (hyperlinkArea != null) {
Context.setColor(new ZLColor(255, 0, 0));
for (ZLTextElementArea area : hyperlinkArea.textAreas()) {
Context.drawLine(area.XStart, area.YStart, area.XEnd, area.YStart);
Context.drawLine(area.XEnd, area.YStart, area.XEnd, area.YEnd);
Context.drawLine(area.XEnd, area.YEnd, area.XStart, area.YEnd);
Context.drawLine(area.XStart, area.YEnd, area.XStart, area.YStart);
}
hyperlinkArea.draw(Context);
}
}
@ -1131,6 +1123,33 @@ public abstract class ZLTextView extends ZLTextViewBase {
return hyperlinkAreas.get(index);
}
public ZLTextHyperlink getCurrentHyperlink() {
final ZLTextHyperlinkArea area = getCurrentHyperlinkArea(myCurrentPage);
return (area != null) ? area.Hyperlink : null;
}
protected ZLTextHyperlink findHyperlink(int x, int y, int maxDistance) {
ZLTextHyperlinkArea area = null;
int distance = Integer.MAX_VALUE;
for (ZLTextHyperlinkArea a : myCurrentPage.TextElementMap.HyperlinkAreas) {
final int d = a.distanceTo(x, y);
if ((d < distance) && (d <= maxDistance)) {
area = a;
distance = d;
}
}
return (area != null) ? area.Hyperlink : null;
}
protected void selectHyperlink(ZLTextHyperlink hyperlink) {
for (ZLTextHyperlinkArea area : myCurrentPage.TextElementMap.HyperlinkAreas) {
if (area.Hyperlink == hyperlink) {
myCurrentHyperlink = area;
break;
}
}
}
protected boolean moveHyperlinkPointer(boolean forward) {
final ArrayList<ZLTextHyperlinkArea> hyperlinkAreas = myCurrentPage.TextElementMap.HyperlinkAreas;
boolean hyperlinkIsChanged = false;

View file

@ -181,7 +181,7 @@ abstract class ZLTextViewBase extends ZLView {
final void drawWord(int x, int y, ZLTextWord word, int start, int length, boolean addHyphenationSign) {
final ZLPaintContext context = Context;
context.setColor(getTextColor(myTextStyle.Hyperlink.Type));
context.setTextColor(getTextColor(myTextStyle.Hyperlink.Type));
if ((start == 0) && (length == -1)) {
drawString(x, y, word.Data, word.Offset, word.Length, word.getMark(), 0);
} else {
@ -205,7 +205,7 @@ abstract class ZLTextViewBase extends ZLView {
private final void drawString(int x, int y, char[] str, int offset, int length, ZLTextWord.Mark mark, int shift) {
final ZLPaintContext context = Context;
context.setColor(getTextColor(myTextStyle.Hyperlink.Type));
context.setTextColor(getTextColor(myTextStyle.Hyperlink.Type));
if (mark == null) {
context.drawString(x, y, str, offset, length);
} else {
@ -230,11 +230,11 @@ abstract class ZLTextViewBase extends ZLView {
}
if (markStart < length) {
context.setColor(getHighlightedTextColor());
context.setTextColor(getHighlightedTextColor());
int endPos = Math.min(markStart + markLen, length);
context.drawString(x, y, str, offset + markStart, endPos - markStart);
x += context.getStringWidth(str, offset + markStart, endPos - markStart);
context.setColor(getTextColor(myTextStyle.Hyperlink.Type));
context.setTextColor(getTextColor(myTextStyle.Hyperlink.Type));
}
pos = markStart + markLen;
}