1
0
Fork 0
mirror of https://github.com/geometer/FBReaderJ.git synced 2025-10-03 17:59:33 +02:00

footnotes toasts (in progress)

This commit is contained in:
Nikolay Pultsin 2015-04-21 18:21:34 +01:00
parent b9422621fd
commit a0fc30a1b4
14 changed files with 312 additions and 130 deletions

View file

@ -1,7 +1,6 @@
===== 2.4 (Apr ??, 2015) =====
* (planned) Tablet-oriented preferences dialog
* (planned) Sync: single book upload
* (planned) Editable notes (by Tamotsu Takahashi)
* (planned) Fixed authors list/tags list editing
* (planned) CSS selectors priority

View file

@ -22,6 +22,12 @@ package org.geometerplus.android.fbreader;
import android.content.Intent;
import android.content.ActivityNotFoundException;
import android.net.Uri;
import android.os.Parcelable;
import android.view.View;
import com.github.johnpersano.supertoasts.SuperActivityToast;
import com.github.johnpersano.supertoasts.SuperToast;
import com.github.johnpersano.supertoasts.util.OnClickWrapper;
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
import org.geometerplus.zlibrary.text.view.*;
@ -63,7 +69,27 @@ class ProcessHyperlinkAction extends FBAndroidAction {
break;
case FBHyperlinkType.INTERNAL:
Reader.Collection.markHyperlinkAsVisited(Reader.getCurrentBook(), hyperlink.Id);
Reader.tryOpenFootnote(hyperlink.Id);
{
final FBReaderApp.FootnoteData data = Reader.getFootnoteData(hyperlink.Id);
if (data != null) {
final SuperActivityToast toast =
new SuperActivityToast(BaseActivity, SuperToast.Type.BUTTON);
toast.setText(data.Text);
toast.setDuration(SuperToast.Duration.LONG);
toast.setButtonIcon(
android.R.drawable.ic_menu_more,
"More"
//ZLResource.resource("dialog").getResource("button").getResource("edit").getValue()
);
toast.setOnClickWrapper(new OnClickWrapper("ftnt", new SuperToast.OnClickListener() {
@Override
public void onClick(View view, Parcelable token) {
Reader.tryOpenFootnote(hyperlink.Id);
}
}));
toast.show();
}
}
break;
}
} else if (soul instanceof ZLTextImageRegionSoul) {

View file

@ -35,7 +35,7 @@ public class SelectionCopyAction extends FBAndroidAction {
@Override
protected void run(Object ... params) {
final String text = Reader.getTextView().getSelectedText();
final String text = Reader.getTextView().getSelectedSnippet().getText();
Reader.getTextView().clearSelection();
final ClipboardManager clipboard =

View file

@ -32,7 +32,7 @@ public class SelectionShareAction extends FBAndroidAction {
@Override
protected void run(Object ... params) {
final String text = Reader.getTextView().getSelectedText();
final String text = Reader.getTextView().getSelectedSnippet().getText();
final String title = Reader.getCurrentBook().getTitle();
Reader.getTextView().clearSelection();

View file

@ -32,7 +32,7 @@ public class SelectionTranslateAction extends FBAndroidAction {
final FBView fbview = Reader.getTextView();
DictionaryUtil.openTextInDictionary(
BaseActivity,
fbview.getSelectedText(),
fbview.getSelectedSnippet().getText(),
fbview.getCountOfSelectedWords() == 1,
fbview.getSelectionStartY(),
fbview.getSelectionEndY()

View file

@ -23,6 +23,8 @@ import java.util.*;
import org.geometerplus.zlibrary.core.util.MiscUtil;
import org.geometerplus.zlibrary.text.view.*;
import org.geometerplus.zlibrary.text.util.TextSnippet;
import org.geometerplus.zlibrary.text.util.TextSnippetImpl;
public final class Bookmark extends ZLTextFixedPosition {
public enum DateType {
@ -33,86 +35,7 @@ public final class Bookmark extends ZLTextFixedPosition {
}
public static Bookmark createBookmark(Book book, String modelId, ZLTextWordCursor startCursor, int maxWords, boolean isVisible) {
final ZLTextWordCursor cursor = new ZLTextWordCursor(startCursor);
final Buffer buffer = new Buffer(cursor);
final Buffer sentenceBuffer = new Buffer(cursor);
final Buffer phraseBuffer = new Buffer(cursor);
int wordCounter = 0;
int sentenceCounter = 0;
int storedWordCounter = 0;
boolean lineIsNonEmpty = false;
boolean appendLineBreak = false;
mainLoop:
while (wordCounter < maxWords && sentenceCounter < 3) {
while (cursor.isEndOfParagraph()) {
if (!cursor.nextParagraph()) {
break mainLoop;
}
if (!buffer.isEmpty() && cursor.getParagraphCursor().isEndOfSection()) {
break mainLoop;
}
if (!phraseBuffer.isEmpty()) {
sentenceBuffer.append(phraseBuffer);
}
if (!sentenceBuffer.isEmpty()) {
if (appendLineBreak) {
buffer.append("\n");
}
buffer.append(sentenceBuffer);
++sentenceCounter;
storedWordCounter = wordCounter;
}
lineIsNonEmpty = false;
if (!buffer.isEmpty()) {
appendLineBreak = true;
}
}
final ZLTextElement element = cursor.getElement();
if (element instanceof ZLTextWord) {
final ZLTextWord word = (ZLTextWord)element;
if (lineIsNonEmpty) {
phraseBuffer.append(" ");
}
phraseBuffer.Builder.append(word.Data, word.Offset, word.Length);
phraseBuffer.Cursor.setCursor(cursor);
phraseBuffer.Cursor.setCharIndex(word.Length);
++wordCounter;
lineIsNonEmpty = true;
switch (word.Data[word.Offset + word.Length - 1]) {
case ',':
case ':':
case ';':
case ')':
sentenceBuffer.append(phraseBuffer);
break;
case '.':
case '!':
case '?':
++sentenceCounter;
if (appendLineBreak) {
buffer.append("\n");
appendLineBreak = false;
}
sentenceBuffer.append(phraseBuffer);
buffer.append(sentenceBuffer);
storedWordCounter = wordCounter;
break;
}
}
cursor.nextWord();
}
if (storedWordCounter < 4) {
if (sentenceBuffer.isEmpty()) {
sentenceBuffer.append(phraseBuffer);
}
if (appendLineBreak) {
buffer.append("\n");
}
buffer.append(sentenceBuffer);
}
return new Bookmark(book, modelId, startCursor, buffer.Cursor, buffer.Builder.toString(), isVisible);
return new Bookmark(book, modelId, new TextSnippetImpl(startCursor, maxWords), isVisible);
}
private long myId;
@ -187,18 +110,18 @@ mainLoop:
}
// creates new bookmark
public Bookmark(Book book, String modelId, ZLTextPosition start, ZLTextPosition end, String text, boolean isVisible) {
super(start);
public Bookmark(Book book, String modelId, TextSnippet snippet, boolean isVisible) {
super(snippet.getStart());
myId = -1;
Uid = newUUID();
BookId = book.getId();
BookTitle = book.getTitle();
myText = text;
myText = snippet.getText();
CreationDate = new Date();
ModelId = modelId;
IsVisible = isVisible;
myEnd = new ZLTextFixedPosition(end);
myEnd = new ZLTextFixedPosition(snippet.getEnd());
myStyleId = 1;
}
@ -347,29 +270,6 @@ mainLoop:
MiscUtil.equals(myText, other.myText);
}
private static class Buffer {
final StringBuilder Builder = new StringBuilder();
final ZLTextWordCursor Cursor;
Buffer(ZLTextWordCursor cursor) {
Cursor = new ZLTextWordCursor(cursor);
}
boolean isEmpty() {
return Builder.length() == 0;
}
void append(Buffer buffer) {
Builder.append(buffer.Builder);
Cursor.setCursor(buffer.Cursor);
buffer.Builder.delete(0, buffer.Builder.length());
}
void append(CharSequence data) {
Builder.append(data);
}
}
private static String newUUID() {
return UUID.randomUUID().toString();
}

View file

@ -28,6 +28,7 @@ import org.geometerplus.zlibrary.core.util.*;
import org.geometerplus.zlibrary.text.hyphenation.ZLTextHyphenator;
import org.geometerplus.zlibrary.text.model.ZLTextModel;
import org.geometerplus.zlibrary.text.util.EmptyTextSnippet;
import org.geometerplus.zlibrary.text.view.*;
import org.geometerplus.fbreader.book.*;
@ -210,11 +211,32 @@ public final class FBReaderApp extends ZLApplication {
return (FBView)getCurrentView();
}
public static class FootnoteData {
public final String Text;
public final boolean IsFull;
private FootnoteData(String text, boolean isFull) {
Text = text;
IsFull = isFull;
}
}
public FootnoteData getFootnoteData(String id) {
if (Model == null) {
return null;
}
final BookModel.Label label = Model.getLabel(id);
if (label == null) {
return null;
}
return new FootnoteData("Here will be the footnote text", false);
}
public void tryOpenFootnote(String id) {
if (Model != null) {
myJumpEndPosition = null;
myJumpTimeStamp = null;
BookModel.Label label = Model.getLabel(id);
final BookModel.Label label = Model.getLabel(id);
if (label != null) {
if (label.ModelId == null) {
if (getTextView() == BookTextView) {
@ -242,14 +264,11 @@ public final class FBReaderApp extends ZLApplication {
public Bookmark addSelectionBookmark() {
final FBView fbView = getTextView();
final String text = fbView.getSelectedText();
final Bookmark bookmark = new Bookmark(
Model.Book,
fbView.getModel().getId(),
fbView.getSelectionStartPosition(),
fbView.getSelectionEndPosition(),
text,
fbView.getSelectedSnippet(),
true
);
Collection.saveBookmark(bookmark);
@ -320,7 +339,7 @@ public final class FBReaderApp extends ZLApplication {
if (pos == null) {
pos = new ZLTextFixedPosition(0, 0, 0);
}
bm = new Bookmark(book, "", pos, pos, "", false);
bm = new Bookmark(book, "", new EmptyTextSnippet(pos), false);
}
myExternalFileOpener.openFile((ExternalFormatPlugin)plugin, book, bm);
return;

View file

@ -29,6 +29,8 @@ import org.geometerplus.zlibrary.core.util.ZLColor;
import org.geometerplus.zlibrary.core.view.ZLPaintContext;
import org.geometerplus.zlibrary.text.model.ZLTextModel;
import org.geometerplus.zlibrary.text.util.TextSnippet;
import org.geometerplus.zlibrary.text.util.TextSnippetImpl;
import org.geometerplus.zlibrary.text.view.*;
import org.geometerplus.zlibrary.text.view.style.ZLTextStyleCollection;
@ -79,7 +81,7 @@ public final class FBView extends ZLTextView {
return true;
}
final ZLTextRegion hyperlinkRegion = findRegion(x, y, MAX_SELECTION_DISTANCE, ZLTextRegion.HyperlinkFilter);
final ZLTextRegion hyperlinkRegion = findRegion(x, y, maxSelectionDistance(), ZLTextRegion.HyperlinkFilter);
if (hyperlinkRegion != null) {
selectRegion(hyperlinkRegion);
myReader.getViewWidget().reset();
@ -103,7 +105,7 @@ public final class FBView extends ZLTextView {
return true;
}
final ZLTextHighlighting highlighting = findHighlighting(x, y, MAX_SELECTION_DISTANCE);
final ZLTextHighlighting highlighting = findHighlighting(x, y, maxSelectionDistance());
if (highlighting instanceof BookmarkHighlighting) {
myReader.runAction(
ActionCode.SELECTION_BOOKMARK,
@ -141,7 +143,7 @@ public final class FBView extends ZLTextView {
return true;
}
final ZLTextSelectionCursor cursor = findSelectionCursor(x, y, MAX_SELECTION_DISTANCE);
final ZLTextSelectionCursor cursor = findSelectionCursor(x, y, maxSelectionDistance());
if (cursor != ZLTextSelectionCursor.None) {
myReader.runAction(ActionCode.SELECTION_HIDE_PANEL);
moveSelectionCursorTo(cursor, x, y);
@ -238,7 +240,7 @@ public final class FBView extends ZLTextView {
return true;
}
final ZLTextRegion region = findRegion(x, y, MAX_SELECTION_DISTANCE, ZLTextRegion.AnyRegionFilter);
final ZLTextRegion region = findRegion(x, y, maxSelectionDistance(), ZLTextRegion.AnyRegionFilter);
if (region != null) {
final ZLTextRegion.Soul soul = region.getSoul();
boolean doSelectRegion = false;
@ -294,7 +296,7 @@ public final class FBView extends ZLTextView {
soul instanceof ZLTextWordRegionSoul) {
if (myReader.MiscOptions.WordTappingAction.getValue() !=
MiscOptions.WordTappingActionEnum.doNothing) {
region = findRegion(x, y, MAX_SELECTION_DISTANCE, ZLTextRegion.AnyRegionFilter);
region = findRegion(x, y, maxSelectionDistance(), ZLTextRegion.AnyRegionFilter);
if (region != null) {
soul = region.getSoul();
if (soul instanceof ZLTextHyperlinkRegionSoul
@ -738,12 +740,14 @@ public final class FBView extends ZLTextView {
}
}
public String getSelectedText() {
public TextSnippet getSelectedSnippet() {
final ZLTextPosition start = getSelectionStartPosition();
final ZLTextPosition end = getSelectionEndPosition();
final TextBuildTraverser traverser = new TextBuildTraverser(this);
if (!isSelectionEmpty()) {
traverser.traverse(getSelectionStartPosition(), getSelectionEndPosition());
traverser.traverse(start, end);
}
return traverser.getText();
return new TextSnippetImpl(start, end, traverser.getText());
}
public int getCountOfSelectedWords() {

View file

@ -0,0 +1,43 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* 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.util;
import org.geometerplus.zlibrary.text.view.ZLTextFixedPosition;
import org.geometerplus.zlibrary.text.view.ZLTextPosition;
public class EmptyTextSnippet implements TextSnippet {
private final ZLTextPosition myPosition;
public EmptyTextSnippet(ZLTextPosition position) {
myPosition = new ZLTextFixedPosition(position);
}
public ZLTextPosition getStart() {
return myPosition;
}
public ZLTextPosition getEnd() {
return myPosition;
}
public String getText() {
return "";
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* 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.util;
import org.geometerplus.zlibrary.text.view.ZLTextPosition;
public interface TextSnippet {
ZLTextPosition getStart();
ZLTextPosition getEnd();
String getText();
}

View file

@ -0,0 +1,157 @@
/*
* Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org>
*
* 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.util;
import java.util.*;
import org.geometerplus.zlibrary.text.view.*;
public class TextSnippetImpl implements TextSnippet {
private final ZLTextPosition myStart;
private final ZLTextPosition myEnd;
private final String myText;
public TextSnippetImpl(ZLTextPosition start, ZLTextPosition end, String text) {
myStart = start;
myEnd = end;
myText = text;
}
public TextSnippetImpl(ZLTextWordCursor start, int maxWords) {
final ZLTextWordCursor cursor = new ZLTextWordCursor(start);
final Buffer buffer = new Buffer(cursor);
final Buffer sentenceBuffer = new Buffer(cursor);
final Buffer phraseBuffer = new Buffer(cursor);
int wordCounter = 0;
int sentenceCounter = 0;
int storedWordCounter = 0;
boolean lineIsNonEmpty = false;
boolean appendLineBreak = false;
mainLoop:
while (wordCounter < maxWords && sentenceCounter < 3) {
while (cursor.isEndOfParagraph()) {
if (!cursor.nextParagraph()) {
break mainLoop;
}
if (!buffer.isEmpty() && cursor.getParagraphCursor().isEndOfSection()) {
break mainLoop;
}
if (!phraseBuffer.isEmpty()) {
sentenceBuffer.append(phraseBuffer);
}
if (!sentenceBuffer.isEmpty()) {
if (appendLineBreak) {
buffer.append("\n");
}
buffer.append(sentenceBuffer);
++sentenceCounter;
storedWordCounter = wordCounter;
}
lineIsNonEmpty = false;
if (!buffer.isEmpty()) {
appendLineBreak = true;
}
}
final ZLTextElement element = cursor.getElement();
if (element instanceof ZLTextWord) {
final ZLTextWord word = (ZLTextWord)element;
if (lineIsNonEmpty) {
phraseBuffer.append(" ");
}
phraseBuffer.Builder.append(word.Data, word.Offset, word.Length);
phraseBuffer.Cursor.setCursor(cursor);
phraseBuffer.Cursor.setCharIndex(word.Length);
++wordCounter;
lineIsNonEmpty = true;
switch (word.Data[word.Offset + word.Length - 1]) {
case ',':
case ':':
case ';':
case ')':
sentenceBuffer.append(phraseBuffer);
break;
case '.':
case '!':
case '?':
++sentenceCounter;
if (appendLineBreak) {
buffer.append("\n");
appendLineBreak = false;
}
sentenceBuffer.append(phraseBuffer);
buffer.append(sentenceBuffer);
storedWordCounter = wordCounter;
break;
}
}
cursor.nextWord();
}
if (storedWordCounter < 4) {
if (sentenceBuffer.isEmpty()) {
sentenceBuffer.append(phraseBuffer);
}
if (appendLineBreak) {
buffer.append("\n");
}
buffer.append(sentenceBuffer);
}
myStart = new ZLTextFixedPosition(start);
myEnd = buffer.Cursor;
myText = buffer.Builder.toString();
}
private static class Buffer {
final StringBuilder Builder = new StringBuilder();
final ZLTextWordCursor Cursor;
Buffer(ZLTextWordCursor cursor) {
Cursor = new ZLTextWordCursor(cursor);
}
boolean isEmpty() {
return Builder.length() == 0;
}
void append(Buffer buffer) {
Builder.append(buffer.Builder);
Cursor.setCursor(buffer.Cursor);
buffer.Builder.delete(0, buffer.Builder.length());
}
void append(CharSequence data) {
Builder.append(data);
}
}
public ZLTextPosition getStart() {
return myStart;
}
public ZLTextPosition getEnd() {
return myEnd;
}
public String getText() {
return myText;
}
}

View file

@ -82,7 +82,7 @@ class ZLTextSelection extends ZLTextHighlighting {
clear();
final ZLTextRegion region = myView.findRegion(
x, y, ZLTextView.MAX_SELECTION_DISTANCE, ZLTextRegion.AnyRegionFilter
x, y, myView.maxSelectionDistance(), ZLTextRegion.AnyRegionFilter
);
if (region == null) {
return false;
@ -137,7 +137,7 @@ class ZLTextSelection extends ZLTextHighlighting {
myScroller.setXY(x, y);
}
ZLTextRegion region = myView.findRegion(x, y, ZLTextView.MAX_SELECTION_DISTANCE, ZLTextRegion.AnyRegionFilter);
ZLTextRegion region = myView.findRegion(x, y, myView.maxSelectionDistance(), ZLTextRegion.AnyRegionFilter);
if (region == null && myScroller != null) {
region = myView.findRegion(x, y, ZLTextRegion.AnyRegionFilter);
}

View file

@ -34,8 +34,6 @@ import org.geometerplus.zlibrary.text.hyphenation.*;
import org.geometerplus.zlibrary.text.view.style.ZLTextStyleCollection;
public abstract class ZLTextView extends ZLTextViewBase {
public static final int MAX_SELECTION_DISTANCE = 10;
public interface ScrollingMode {
int NO_OVERLAPPING = 0;
int KEEP_LINES = 1;

View file

@ -43,6 +43,14 @@ abstract class ZLTextViewBase extends ZLView {
super(application);
}
private int myMaxSelectionDistance = 0;
protected final int maxSelectionDistance() {
if (myMaxSelectionDistance == 0) {
myMaxSelectionDistance = ZLibrary.Instance().getDisplayDPI() / 20;
}
return myMaxSelectionDistance;
}
protected void resetMetrics() {
myMetrics = null;
}