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:
parent
5c3dfbec58
commit
78da337690
20 changed files with 407 additions and 94 deletions
|
@ -1 +1 @@
|
|||
<!ENTITY FBReaderVersion "0.5.3">
|
||||
<!ENTITY FBReaderVersion "0.5.4">
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -3,4 +3,6 @@
|
|||
<binding key="<VolumeDown>" action="volumeKeyScrollForward"/>
|
||||
<binding key="<VolumeUp>" action="volumeKeyScrollBackward"/>
|
||||
<binding key="<Back>" action="cancel"/>
|
||||
<binding key="<Enter>" action="followHyperlink"/>
|
||||
<binding key="<PadCenter>" action="followHyperlink"/>
|
||||
</keymap>
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -39,7 +39,6 @@ class ZLBooleanPreference extends CheckBoxPreference implements ZLPreference {
|
|||
setChecked(option.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccept() {
|
||||
myOption.setValue(isChecked());
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue