com.pdftron.pdf.tools.Tool.java Source code

Java tutorial

Introduction

Here is the source code for com.pdftron.pdf.tools.Tool.java

Source

//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2016 by PDFTron Systems Inc. All Rights Reserved.
// Consult legal.txt regarding legal and license information.
//---------------------------------------------------------------------------------------

package com.pdftron.pdf.tools;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.support.v4.widget.EdgeEffectCompat;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.util.Patterns;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.EditText;
import android.widget.PopupWindow;

import com.pdftron.common.PDFNetException;
import com.pdftron.pdf.Annot;
import com.pdftron.pdf.PDFViewCtrl;
import com.pdftron.pdf.Page;
import com.pdftron.pdf.annots.Markup;
import com.pdftron.pdf.utils.AnalyticsHandlerAdapter;
import com.pdftron.pdf.utils.Utils;

import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * The base class that implements the ToolManager.Tool interface and several
 * basic tool functionalities.
 */
public abstract class Tool implements ToolManager.Tool {

    public static final String QM_NOTE = "note";
    protected static final String QM_APPEARANCE = "appearance";
    protected static final String QM_COLOR = "color";
    protected static final String QM_THICKNESS = "thickness";
    protected static final String QM_OPACITY = "opacity";
    protected static final String QM_DELETE = "delete";
    public static final String QM_OPEN_ATTACHMENT = "open_attachment";
    protected static final String QM_EDIT = "edit";

    public static final String QM_REPLY = "reply";

    protected static final String QM_HIGHLIGHT = "highlight";
    protected static final String QM_STRIKEOUT = "strikeout";
    protected static final String QM_SQUIGGLY = "squiggly";
    protected static final String QM_UNDERLINE = "underline";
    public static final String QM_DEFINE = "define";
    public static final String QM_TRANSLATE = "translate";
    public static final String QM_TTS = "text_to_speech";
    public static final String QM_SEARCH = "search";
    public static final String QM_STAMPER = "stamper";
    public static final String QM_SHARE = "share";
    public static final String QM_COPY = "copy";
    protected static final String QM_TYPE = "textmarkup_type";
    protected static final String QM_OVERFLOW_MENU = "overflow_qm";
    protected static final String QM_DOTS = "d"; // must be 1 char to make the qm button the minimum size

    protected static final String QM_STICKY_NOTE = "sticky_note";
    protected static final String QM_FLOATING_SIG = "floating_sig";
    protected static final String QM_FREEHAND = "freehand";
    protected static final String QM_FREE_TEXT = "free_text";
    protected static final String QM_ARROW = "arrow";
    protected static final String QM_LINE = "line";
    protected static final String QM_RECTANGLE = "rectangle";
    protected static final String QM_OVAL = "oval";
    protected static final String QM_INK_ERASER = "ink_eraser";
    protected static final String QM_PASTE = "paste";

    public static final String PREFS_FILE_NAME = "com_pdftron_pdfnet_pdfviewctrl_prefs_file";
    public static final int PREFS_SHAPE_STROKE_COLOR_DEFAULT = 0xFFFF0000; // Red
    public static final int MODERATOR_COLOR = 0x00FF00; // Red
    public static final int PREFS_SHAPE_FILL_COLOR_DEFAULT = 0x00000000; // Transparent
    public static final float PREFS_SHAPE_STROKE_THICKNESS_DEFAULT = 1.0f;
    public static final float PREFS_SHAPE_OPACITY_DEFAULT = 1.0f;

    public static final String PREFS_NOTE_ICON_DEFAULT = "Comment";
    public static final int PREFS_NOTE_ICON_COLOR_DEFAULT = 0xFFFFFF00; // Yellow

    public static final int PREFS_FREEHAND_STROKE_COLOR_DEFAULT1 = R.color.red; // Red // Khanya
    public static final int PREFS_FREEHAND_STROKE_COLOR_DEFAULT2 = R.color.blue; // Blue
    public static final int PREFS_FREEHAND_STROKE_COLOR_DEFAULT3 = R.color.black; // Black
    public static final int PREFS_FREEHAND_STROKE_COLOR_DEFAULT4 = R.color.green; // Green
    public static final int PREFS_FREEHAND_STROKE_COLOR_DEFAULT5 = R.color.yellow; // Yellow

    public static final int PREFS_FREETEXT_COLOR_DEFAULT = 0xFFFF0000; // Red
    public static final int PREFS_FREETEXT_FILL_COLOR_DEFAULT = 0x00000000; // Transparent
    public static final int PREFS_FREETEXT_FONT_SIZE_DEFAULT = 16;
    public static final float PREFS_FREETEXT_OPACITY_DEFAULT = 1.0f;
    public static final String PREFS_FREETEXT_FONT_DEFAULT = ""; // PDFNet handles default

    //public static final int PREFS_TEXT_HIGHLIGHT_COLOR_DEFAULT = 0xFFFF00; // Yellow
    public static final int PREFS_TEXT_HIGHLIGHT_COLOR_DEFAULT = 0x00FF00; // green
    public static final int PREFS_TEXT_HIGHLIGHT_COLOR_RED = 0xFF0000; // Red
    public static final float PREFS_TEXT_HIGHLIGHT_OPACITY_DEFAULT = 1.0f;
    public static final int PREFS_TEXT_MARKUP_COLOR_DEFAULT = 0xFF0000; // Red
    public static final float PREFS_TEXT_MARKUP_OPACITY_DEFAULT = 1.0f;
    public static final float PREFS_TEXT_MARKUP_THICKNESS_DEFAULT = 1.0f;

    public static final int PREFS_ERASER_THICKNESS_DEFAULT = 10;
    public static final int PREFS_ERASER_THICKNESS_MIN = 5;
    public static final int PREFS_ERASER_THICKNESS_MAX = 30;

    public static final int PREFS_SIGNATURE_COLOR_DEFAULT = 0xFF000000; // Black
    public static final float PREFS_SIGNATURE_THICKNESS_DEFAULT = 8.0f;
    public static final int PREFS_SIGNATURE_THICKNESS_MAX = 24;
    public static final int PREFS_SIGNATURE_THICKNESS_MIN = 2;

    // user-default annotation properties
    public static final String PREF_ANNOTATION_CREATION_LINE = "annotation_creation"; // line
    public static final String PREF_ANNOTATION_CREATION_RECTANGLE = "annotation_creation_rectangle";
    public static final String PREF_ANNOTATION_CREATION_OVAL = "annotation_creation_oval";
    public static final String PREF_ANNOTATION_CREATION_HIGHLIGHT = "annotation_creation_highlight";
    public static final String PREF_ANNOTATION_CREATION_UNDERLINE = "annotation_creation_text_markup"; // underline
    public static final String PREF_ANNOTATION_CREATION_STRIKEOUT = "annotation_creation_strikeout";
    public static final String PREF_ANNOTATION_CREATION_SQUIGGLY = "annotation_creation_squiggly";
    public static final String PREF_ANNOTATION_CREATION_FREETEXT = "annotation_creation_freetext";
    public static final String PREF_ANNOTATION_CREATION_FREEHAND = "annotation_creation_freehand";
    public static final String PREF_ANNOTATION_CREATION_NOTE = "annotation_creation_note";
    public static final String PREF_ANNOTATION_CREATION_ERASER = "annotation_creation_eraser";
    public static final String PREF_ANNOTATION_CREATION_SIGNATURE = "annotation_creation_signature";

    public static final String PREF_ANNOTATION_CREATION_COLOR = "_color";
    public static final String PREF_ANNOTATION_CREATION_FILL_COLOR = "_fill_color";
    public static final String PREF_ANNOTATION_CREATION_OPACITY = "_opacity";
    public static final String PREF_ANNOTATION_CREATION_THICKNESS = "_thickness";
    public static final String PREF_ANNOTATION_CREATION_ICON = "_icon";
    public static final String PREF_ANNOTATION_CREATION_FONT = "_font";

    // custom annotation properties
    public static final String PREF_ANNOTATION_PROPERTY_LINE = "annotation_property_shape"; // line
    public static final String PREF_ANNOTATION_PROPERTY_ARROW = "annotation_property_arrow";
    public static final String PREF_ANNOTATION_PROPERTY_RECTANGLE = "annotation_property_rectangle";
    public static final String PREF_ANNOTATION_PROPERTY_OVAL = "annotation_property_oval";
    public static final String PREF_ANNOTATION_PROPERTY_HIGHLIGHT = "annotation_property_highlight";
    public static final String PREF_ANNOTATION_PROPERTY_UNDERLINE = "annotation_property_text_markup"; // underline
    public static final String PREF_ANNOTATION_PROPERTY_STRIKEOUT = "annotation_property_strikeout";
    public static final String PREF_ANNOTATION_PROPERTY_SQUIGGLY = "annotation_property_squiggly";
    public static final String PREF_ANNOTATION_PROPERTY_FREETEXT = "annotation_property_freetext";
    public static final String PREF_ANNOTATION_PROPERTY_FREEHAND = "annotation_property_freehand";
    public static final String PREF_ANNOTATION_PROPERTY_NOTE = "annotation_property_note";
    public static final String PREF_ANNOTATION_PROPERTY_ERASER = "annotation_property_eraser";
    public static final String PREF_ANNOTATION_PROPERTY_SIGNATURE = "annotation_property_signature";

    public static final String PREF_ANNOTATION_PROPERTY_COLORS = "_colors";
    public static final String PREF_ANNOTATION_PROPERTY_FILL_COLORS = "_fill_colors";
    public static final String PREF_ANNOTATION_PROPERTY_COLOR = "_color";
    public static final String PREF_ANNOTATION_PROPERTY_FILL_COLOR = "_fill_color";
    public static final String PREF_ANNOTATION_PROPERTY_OPACITY = "_opacity";
    public static final String PREF_ANNOTATION_PROPERTY_THICKNESS = "_thickness";
    public static final String PREF_ANNOTATION_PROPERTY_LAST_USED_VIEW = "_last_used_view";
    public static final String PREF_ANNOTATION_PROPERTY_ICON = "_icon";
    public static final String PREF_ANNOTATION_PROPERTY_FONT = "_font";

    public static final String PREF_ANNOTATION_PROPERTY_PRESETS = "_presets";
    public static final String PREF_ANNOTATION_PROPERTY_CUSTOM = "_custom";

    public static final String PREF_ANNOTATION_PROPERTY_TAB = "_tab";

    public static final String ANNOTATION_NOTE_ICON_FILE_PREFIX = "annotation_note_icon_";
    public static final String ANNOTATION_NOTE_ICON_FILE_POSTFIX_FILL = "_fill";
    public static final String ANNOTATION_NOTE_ICON_FILE_POSTFIX_OUTLINE = "_outline";

    public static final String ANNOTATION_TOOLBAR_SIGNATURE_STATE = "annotation_toolbar_signature_state";

    public static final String ANNOTATION_FREE_TEXT_FONTS = "annotation_property_free_text_fonts_list";
    public static final String ANNOTATION_FREE_TEXT_JSON_FONT_FILE_PATH = "filepath";
    public static final String ANNOTATION_FREE_TEXT_JSON_FONT_DISPLAY_NAME = "display name";
    public static final String ANNOTATION_FREE_TEXT_JSON_FONT_DISPLAY_IN_LIST = "display font";
    public static final String ANNOTATION_FREE_TEXT_JSON_FONT_PDFTRON_NAME = "pdftron name";
    public static final String ANNOTATION_FREE_TEXT_JSON_FONT_NAME = "font name";
    public static final String ANNOTATION_FREE_TEXT_JSON_FONT = "fonts";

    // list of white listed annotation free text fonts
    // the fonts on this list are from: https://www.microsoft.com/typography/fonts/popular.aspx,
    // the Base14 Fonts and other common fonts found on devices
    public static final String[] ANNOTATION_FREE_TEXT_WHITELIST_FONTS = { "Gill", "Calibri", "Arial", "SimSun",
            "Curlz", "Times", "Lucida", "Rockwell", "Old English", "Abadi", "Twentieth Century", "News Gothic",
            "Bodoni", "Candara", "PMingLiU", "Palace Script", "Helvetica", "Courier", "Roboto", "Comic", "Droid",
            "Georgia", "MotoyaLManu", "NanumGothic", "Kaiti", "Miaowu", "ShaoNV", "Rosemary" };

    // overflow quick tools menu properties
    public static final int QUICK_TOOLS_MENU_TEXT_SELECT = 1;
    public static final int QUICK_TOOLS_MENU_TEXT_MARKUP = 2;

    public static final String PREF_QUICK_TOOLS_MENU = "quick_tools_menu";
    public static final String PREF_QUICK_TOOLS_MENU_OVERFLOW = "_overflow";
    public static final String PREF_QUICK_TOOLS_MENU_MRU = "_mru";
    public static final String PREF_QUICK_TOOLS_MENU_TEXT_SELECT = "_text_select";
    public static final String PREF_QUICK_TOOLS_MENU_TEXT_MARKUP = "_text_markup";

    // quick tools menu with an overflow menu (three dots)
    public static final String PREF_QUICK_TOOLS_MENU_TEXT_SELECT_MRU_DEFAULT = QM_STRIKEOUT;
    public static final String PREF_QUICK_TOOLS_MENU_TEXT_SELECT_OVERFLOW_DEFAULT = QM_SQUIGGLY + " " + QM_SHARE
            + " " + QM_SEARCH + " " + QM_TTS;
    public static final String PREF_QUICK_TOOLS_MENU_TEXT_MARKUP_MRU_DEFAULT = QM_TYPE;
    public static final String PREF_QUICK_TOOLS_MENU_TEXT_MARKUP_OVERFLOW_DEFAULT = QM_SHARE + " " + QM_SEARCH + " "
            + QM_TTS;

    // quick tools menu with an overflow menu and extra options (define & translate)
    public static final String PREF_QUICK_TOOLS_MENU_EXTRA_TEXT_SELECT_MRU_DEFAULT = QM_STRIKEOUT + " " + QM_DEFINE;
    public static final String PREF_QUICK_TOOLS_MENU_EXTRA_TEXT_SELECT_OVERFLOW_DEFAULT = QM_SQUIGGLY + " "
            + QM_TRANSLATE + " " + QM_SHARE + " " + QM_SEARCH + " " + QM_TTS;
    public static final String PREF_QUICK_TOOLS_MENU_EXTRA_TEXT_MARKUP_MRU_DEFAULT = QM_TYPE;
    public static final String PREF_QUICK_TOOLS_MENU_EXTRA_TEXT_MARKUP_OVERFLOW_DEFAULT = QM_DEFINE + " "
            + QM_TRANSLATE + " " + QM_SHARE + " " + QM_SEARCH + " " + QM_TTS;

    // Translation languages properties
    public static final String PREF_TRANSLATION_SOURCE_LANGUAGE_CODE_KEY = "translation_source_language_code";
    public static final String PREF_TRANSLATION_TARGET_LANGUAGE_CODE_KEY = "translation_target_language_code";
    public static final String LAST_DEVICE_LOCALE_LANGUAGE = "last_device_locale_language";
    public static final String GOOGLE_TRANSLATE_SOURCE_TARGET_LANGUAGES = "google_translate_source_target_languages";
    public static final String GOOGLE_TRANSLATE_USED_CHARS = "google_translate_used_chars";

    public static final String PREF_TRANSLATION_SOURCE_LANGUAGE_CODE_DEFAULT = "en"; // english
    public static final String PREF_TRANSLATION_TARGET_LANGUAGE_CODE_DEFAULT = "fr"; // french

    // Stamper
    public static final int INTENT_PICK_IMAGE_CAM_REQUEST = 1234;

    protected static PDFViewCtrl mPDFView;
    protected int mNextToolMode;
    protected int mCurrentDefaultToolMode; // the default tool in continuous annotating mode, used for allowing editing
    protected Annot mAnnot;
    protected int mAnnotPageNum;
    protected int mSelectPageNum;
    protected RectF mAnnotBBox; // In page space
    protected QuickMenu mQuickMenu;
    protected LinkedList<MenuEntry> mMenuTitles;
    protected LinkedList<MenuEntry> mOverflowMenuTitles;
    protected String mMruMenuItems[];
    protected String mOverflowMenuItems[];
    protected Paint mPaint4PageNum;
    protected boolean mJustSwitchedFromAnotherTool;
    protected boolean mMenuShown;
    protected boolean mShowPageNum;
    protected boolean mAvoidLongPressAttempt;
    protected float mPageNumPosAdjust;
    protected RectF mTempPageDrawingRectF;
    protected final float mTextSize;
    protected final float mTextVOffset;
    private Matrix mTempMtx1;
    private Matrix mTempMtx2;

    // edge effect
    private EdgeEffectCompat mEdgeEffectLeft;
    private EdgeEffectCompat mEdgeEffectRight;

    protected boolean mForceSameNextToolMode;
    protected boolean mAnnotPushedBack;

    private Markup mMarkupToAuthor;

    private boolean mPageNumberIndicatorVisible;

    protected boolean mAllowTwoFingerScroll;
    protected boolean mAllowOneFingerScrollWithStylus;
    protected boolean mAllowZoom;

    protected boolean mHasPermission = true;

    /*
     * Used to remove the shown page number
     */

    private static class PageNumberRemovalHandler extends Handler {
        private final WeakReference<Tool> mTool;

        public PageNumberRemovalHandler(Tool tool) {
            mTool = new WeakReference<Tool>(tool);
        }

        @Override
        public void handleMessage(Message msg) {
            Tool tool = mTool.get();
            if (tool != null) {
                tool.mShowPageNum = false;
                tool.mPDFView.invalidate();
            }
        }
    }

    private PageNumberRemovalHandler mPageNumberRemovalHandler = new PageNumberRemovalHandler(this);

    /**
     * Constructor.
     */
    public Tool(PDFViewCtrl ctrl) {
        mPDFView = ctrl;
        mNextToolMode = ToolManager.e_pan;
        mCurrentDefaultToolMode = ToolManager.e_pan;
        mAnnot = null;
        mAnnotBBox = new RectF();
        mJustSwitchedFromAnotherTool = false;
        mForceSameNextToolMode = false;
        mAvoidLongPressAttempt = false;
        mAnnotPushedBack = false;
        mMenuShown = false;
        mPageNumPosAdjust = 0;
        mShowPageNum = false;
        mTempPageDrawingRectF = new RectF();

        mTextSize = convDp2Pix(15);
        mTextVOffset = convDp2Pix(50);
        mPaint4PageNum = new Paint();
        mPaint4PageNum.setAntiAlias(true);
        mPaint4PageNum.setTextSize(mTextSize);
        mPaint4PageNum.setStyle(Paint.Style.FILL);

        mTempMtx1 = new Matrix();
        mTempMtx2 = new Matrix();

        mPageNumberIndicatorVisible = true;

        mAllowTwoFingerScroll = false;
        mAllowOneFingerScrollWithStylus = false;
        mAllowZoom = true;

        // Disable page turning (in non-continuous page presentation mode);
        // it is only turned on in Pan tool.
        mPDFView.setBuiltInPageSlidingEnabled(false);

        // Sets up edge effects
        mEdgeEffectLeft = new EdgeEffectCompat(ctrl.getContext());
        mEdgeEffectRight = new EdgeEffectCompat(ctrl.getContext());
    }

    /**
     * Implements the PDFViewCtrl.Tool interfaces
     */
    public abstract int getMode();

    final public int getNextToolMode() {
        return mNextToolMode;
    }

    /**
     * Whether this tool is creating a new annotation
     */
    public boolean isCreatingAnnotation() {
        return false;
    }

    protected void addMenu(MenuEntry menuEntry, boolean hasPermission) {
        if (hasPermission) {
            mMenuTitles.add(menuEntry);
        }
    }

    protected boolean hasPermission(Annot annot) {
        boolean hasPermission = true;
        if (mPDFView.getToolManager() instanceof ToolManager) {
            try {
                mPDFView.docLockRead();
                String currentAuthor = ((ToolManager) mPDFView.getToolManager()).getAuthorId();
                Markup mp = new Markup(annot);
                if (mp.isValid()) {
                    String userId = mp.getTitle() != null ? mp.getTitle() : null;
                    if (null != userId && null != currentAuthor) {
                        hasPermission = currentAuthor.equals(userId);
                    }
                }
            } catch (Exception e) {
                hasPermission = true;
            } finally {
                mPDFView.docUnlockRead();
            }
        }
        return hasPermission;
    }

    public boolean onDown(MotionEvent e) {
        if (!mPDFView.isZoomingInAddingAnnotationEnabled() && isCreatingAnnotation()) {
            mAllowZoom = false;
        } else {
            mAllowZoom = true;
        }
        mPDFView.setZoomEnabled(mAllowZoom);

        return false;
    }

    public void onDocumentDownloadEvent(int type, int page_num, int page_downloaded, int page_count,
            String message) {
    }

    public boolean onUp(MotionEvent e, int prior_event_type) {
        mPageNumberRemovalHandler.sendEmptyMessageDelayed(1, 3000);
        return false;
    }

    public boolean onFlingStop() {
        return false;
    }

    public boolean onMove(MotionEvent e1, MotionEvent e2, float x_dist, float y_dist) {
        if (isCreatingAnnotation()) {
            if (e1.getPointerCount() == 2 || e2.getPointerCount() == 2) {
                mAllowTwoFingerScroll = true;
            }
        } else {
            mAllowTwoFingerScroll = false;
        }

        // Enable page turning (in non-continuous page presentation mode);
        // it is always enabled for pan mode and text-highlighter
        // it is enabled only if scrolled with two fingers in other modes
        if (getMode() == ToolManager.e_pan || getMode() == ToolManager.e_text_select
                || getMode() == ToolManager.e_text_highlighter) {
            mPDFView.setBuiltInPageSlidingEnabled(true);
        } else {
            if (mAllowTwoFingerScroll || mAllowOneFingerScrollWithStylus) {
                mPDFView.setBuiltInPageSlidingEnabled(true);
            } else {
                mPDFView.setBuiltInPageSlidingEnabled(false);
            }
        }

        // showTransientPageNumber();
        return false;
    }

    public void onScrollChanged(int l, int t, int oldl, int oldt) {
    }

    public void onPageTurning(int old_page, int cur_page) {
    }

    public boolean onSingleTapConfirmed(MotionEvent e) {
        return false;
    }

    public void onPostSingleTapConfirmed() {
    }

    public boolean onSingleTapUp(MotionEvent e) {
        //        if (ToolManager.isSingleMarkActive)
        //        {
        //            //mNextToolMode = ToolManager.e_text_select;
        //            mNextToolMode = ToolManager.e_text_highlight;
        //            ToolManager toolManager = (ToolManager) mPDFView.getToolManager();
        //            ToolManager.Tool tool = toolManager.createTool(mNextToolMode, toolManager.mTool);
        //            toolManager.setTool(tool);
        //        }
        return false;
    }

    public boolean onDoubleTap(MotionEvent e) {
        //showTransientPageNumber();

        // The following code shows how to override the double tap behavior of PDFViewCtrl.

        boolean customize = true;
        if (!customize) {
            // Let PDFViewCtrl handle how double tap zooms
            return false;

        } else {
            if (isCreatingAnnotation()) {
                // Disable double tap in annotation creation mode
                return true;
            }
            boolean animate = true;
            // I want to customize how double tap zooms
            int x = (int) (e.getX() + 0.5);
            int y = (int) (e.getY() + 0.5);

            final int ref_mode;
            if (mPDFView.isMaintainZoomEnabled()) {
                ref_mode = mPDFView.getPreferredViewMode();
            } else {
                ref_mode = mPDFView.getPageRefViewMode();
            }

            double zoom = mPDFView.getZoom();
            double refZoom = 0;
            try {
                refZoom = mPDFView.getZoomForViewMode(ref_mode);
            } catch (PDFNetException ex) {
                Log.v("Tool", ex.getMessage());
            }
            boolean smartZoom = false;
            if (refZoom > 0 && zoom > 0) {
                double zoomFactor = refZoom / zoom;
                if (zoomFactor > 0.95 && zoomFactor < 1.05) {
                    smartZoom = true;
                }
            }
            if (smartZoom) {
                // Let's try smart zoom first
                boolean result = mPDFView.smartZoom(x, y, animate);
                if (!result) {
                    // If not, just zoom in
                    boolean use_snapshot = true;
                    double temp = mPDFView.getZoom();
                    mPDFView.setZoom(x, y, temp * 2.5, use_snapshot, animate);
                }
            } else {
                mPDFView.setPageViewMode(ref_mode, x, y, animate);
            }
            //            //adriaan added
            //            if (ToolManager.isPenMarkActive)
            //            {
            //                ToolManager.dontAddMarkandToast = true;
            //                ToolManager.activateFingerHighlighter();
            //            }

            return true; // This tells PDFViewCtrl to skip its internal logic
        }

        // return false;
    }

    public void onDoubleTapEnd(MotionEvent e) {
    }

    @Override
    public boolean onDoubleTapEvent(MotionEvent e) {
        return false;
    }

    public void onLayout(boolean changed, int l, int t, int r, int b) {
    }

    public boolean onLongPress(MotionEvent e) {
        return false;
    }

    public boolean onScaleBegin(float x, float y) {
        //(x, y) is the scaling focal point in client space
        return false;
    }

    public boolean onScale(float x, float y) {
        //(x, y) is the scaling focal point in client space
        return false;
    }

    public boolean onScaleEnd(float x, float y) {
        //(x, y) is the scaling focal point in client space
        //showTransientPageNumber();
        return false;
    }

    public void onConfigurationChanged(Configuration newConfig) {
    }

    public boolean onShowPress(MotionEvent e) {
        return false;
    }

    public void onClose() {
        mPageNumberRemovalHandler.removeCallbacksAndMessages(null);
        closeMenu();
    }

    public void onCustomEvent(Object o) {
    }

    public void onSetDoc() {
    }

    /**
     * Called after the tool is created by ToolManager.
     */
    public void onCreate() {
        if (mShowPageNum) {
            //showTransientPageNumber();
        }
    }

    public void onDraw(Canvas canvas, android.graphics.Matrix tfm) {
        // Draw page number
        if (mShowPageNum && mPageNumberIndicatorVisible) {
            int width = mPDFView.getWidth();
            int height = mPDFView.getHeight();
            int page_num = mPDFView.getCurrentPage();
            boolean restore = false;
            float yOffset = 0;

            try {
                // During page sliding, PDFViewCtrl might apply extra transformation
                // matrix to Canvas for animation. However, page number should not
                // move; hence applying the inverse to offset it.
                if (!tfm.isIdentity()) {
                    canvas.save();
                    restore = true;
                    tfm.invert(mTempMtx1);
                    canvas.getMatrix(mTempMtx2);
                    mTempMtx2.postConcat(mTempMtx1);
                    canvas.setMatrix(mTempMtx2);

                    // Workaround for bug found in Android > ICS with hardware acceleration turned
                    // ON. See http://code.google.com/p/android/issues/detail?id=24517 for more info
                    if (Build.VERSION.SDK_INT >= 14
                            /*Build.VERSION_CODES.ICE_CREAM_SANDWICH*/ && mPDFView.isHardwareAccelerated()) {
                        Rect rectangle = new Rect();
                        ((android.app.Activity) mPDFView.getContext()).getWindow().getDecorView()
                                .getWindowVisibleDisplayFrame(rectangle);
                        yOffset = rectangle.top;
                    }
                }

                int page_count = mPDFView.getDoc().getPageCount();
                String str = String.format(getStringFromResId(R.string.tools_misc_pagerange), page_num, page_count);

                Rect r = new Rect();
                mPaint4PageNum.getTextBounds(str, 0, str.length(), r);
                float str_width = r.width();
                float str_height = r.height();

                float margin = str_height / 1.5f;
                float left = width - str_width * 1.5f - margin + mPDFView.getScrollX();

                float top = mPDFView.getScrollY() + height - mPageNumPosAdjust - str_height * 3.0f + yOffset;

                float right = left + str_width + margin * 2;
                float bottom = top + str_height + margin * 2;

                mTempPageDrawingRectF.set(left, top, right, bottom);
                mPaint4PageNum.setColor(
                        mPDFView.getContext().getResources().getColor(R.color.tools_pageindicator_background));
                canvas.drawRoundRect(mTempPageDrawingRectF, margin, margin, mPaint4PageNum);

                mPaint4PageNum
                        .setColor(mPDFView.getContext().getResources().getColor(R.color.tools_pageindicator_text));
                left += margin;
                top += str_height / 2 + margin + mPaint4PageNum.descent();

                canvas.drawText(str, left, top - 0.5f, mPaint4PageNum);

            } catch (Exception e) {

            } finally {
                if (restore) {
                    canvas.restore();
                }
            }
        }
    }

    public void setJustCreatedFromAnotherTool() {
        mJustSwitchedFromAnotherTool = true;
    }

    public Boolean createImageStamp(Uri uri, int imageRotation, String filePath) {
        if (getMode() == ToolManager.e_stamper) {
            try {
                return ((Stamper) this).createImageStamp(uri, imageRotation, filePath);
            } catch (Exception e) {
            }
        }
        return false;
    }

    public void clearTargetPoint() {
        if (getMode() == ToolManager.e_stamper) {
            try {
                ((Stamper) this).clearTargetPoint();
            } catch (Exception e) {
            }
        }
    }

    public void closeMenu() {
        if (mQuickMenu != null) {
            mMenuShown = false;
            mQuickMenu.dismiss();
        }
    }

    public void initOverflowMenu(int menuType, boolean hasPermission) {
        SharedPreferences settings = mPDFView.getContext().getSharedPreferences(Tool.PREFS_FILE_NAME, 0);

        String mru_menu_string = "";
        if (hasPermission) {
            // get mru menu items and sort alphabetically, so they don't swap locations unnecessarily
            mru_menu_string = settings.getString(getMRUMenuKey(menuType), getMRUMenuDefault(menuType));
            mMruMenuItems = mru_menu_string.split(" ");
            String[] tempMruMenuItems = new String[mMruMenuItems.length];
            System.arraycopy(mMruMenuItems, 0, tempMruMenuItems, 0, mMruMenuItems.length);
            Arrays.sort(tempMruMenuItems);

            // add mru menu items
            for (int i = 0; i < tempMruMenuItems.length; i++) {
                try {
                    String resString = "tools_qm_" + tempMruMenuItems[i];
                    int resID = mPDFView.getContext().getResources().getIdentifier(resString, "string",
                            mPDFView.getContext().getPackageName());
                    mMenuTitles.add(new MenuEntry(tempMruMenuItems[i], getStringFromResId(resID)));
                } catch (Exception e) {
                    AnalyticsHandlerAdapter.getInstance().sendException(e);
                }
            }
        }

        // get overflow menu items
        String overflow_menu_string = settings.getString(getOverflowMenuKey(menuType),
                getOverflowMenuDefault(menuType));
        if (!mru_menu_string.contains(QM_SEARCH) && !overflow_menu_string.contains(QM_SEARCH)) {
            // work around issue where
            // items that are added later on might not make to the list when user updates the app
            overflow_menu_string = overflow_menu_string + " " + QM_SEARCH;
        }
        mOverflowMenuItems = overflow_menu_string.split(" ");

        // Overflow menu items
        for (int i = 0; i < mOverflowMenuItems.length; i++) {
            try {
                String resString = "tools_qm_" + mOverflowMenuItems[i];
                int resID = mPDFView.getContext().getResources().getIdentifier(resString, "string",
                        mPDFView.getContext().getPackageName());
                mOverflowMenuTitles.add(new MenuEntry(mOverflowMenuItems[i], getStringFromResId(resID)));
            } catch (Exception e) {
                AnalyticsHandlerAdapter.getInstance().sendException(e);
            }
        }

        // add overflow menu
        // if LTR, must be last item on list
        // if RTL, must be first item on list
        if (Utils.isRtlLayout(mPDFView.getContext())) {
            mMenuTitles.addFirst(new MenuEntry(QM_OVERFLOW_MENU, QM_DOTS));
        } else {
            mMenuTitles.addLast(new MenuEntry(QM_OVERFLOW_MENU, QM_DOTS));
        }
    }

    //    public void updateOverflowMenu(int menuType, int selectedId, String selectedStr) {
    //        // MRU item clicked - update order of MRU string
    //        // No need to update if the dots or first element in the MRU string was clicked (already in MRU order then)
    //        // Check if selected ID falls within the range of MRU buttons
    //        if (!selectedStr.equals(QM_DOTS) && selectedId != 0 && selectedId >= (mMenuTitles.size() - mMruMenuItems.length) && selectedId < mMenuTitles.size()) {
    //            String mruMenuString = selectedStr + " ";
    //            for (int i = 0; i < mMruMenuItems.length; i++) {
    //                if (!selectedStr.equals(mMruMenuItems[i])) {
    //                    mruMenuString += mMruMenuItems[i] + " ";
    //                }
    //            }
    //
    //            SharedPreferences settings = mPDFView.getContext().getSharedPreferences(Tool.PREFS_FILE_NAME, 0);
    //            SharedPreferences.Editor editor = settings.edit();
    //            editor.putString(getMRUMenuKey(menuType), mruMenuString);
    //            editor.apply();
    //
    //            // Overflow menu item clicked
    //        } else if (selectedId >= mMenuTitles.size()) {
    //            String itemSelected = mQuickMenu.getSelectedType();
    //
    //            String mruItemToRemove = mMruMenuItems[mMruMenuItems.length - 1];
    //
    //            //update MRU menu string
    //            String mruMenuString = itemSelected + " ";
    //            for (int i = 0; i < mMruMenuItems.length; i++) {
    //                if (!mMruMenuItems[i].equals(mruItemToRemove)) {
    //                    mruMenuString += mMruMenuItems[i] + " ";
    //                }
    //            }
    //
    //            // update Overflow menu string
    //            String overflowMenuString = mruItemToRemove + " ";
    //            for (int i = 0; i < mOverflowMenuItems.length; i++) {
    //                if (!mOverflowMenuItems[i].equals(itemSelected)) {
    //                    overflowMenuString += mOverflowMenuItems[i] + " ";
    //                }
    //            }
    //
    //            SharedPreferences settings = mPDFView.getContext().getSharedPreferences(Tool.PREFS_FILE_NAME, 0);
    //            SharedPreferences.Editor editor = settings.edit();
    //            editor.putString(getOverflowMenuKey(menuType), overflowMenuString);
    //            editor.putString(getMRUMenuKey(menuType), mruMenuString);
    //            editor.apply();
    //        }
    //    }

    @Override
    public boolean onDrawEdgeEffects(Canvas canvas, int width, int verticalOffset) {
        boolean needsInvalidate = false;

        if (!mEdgeEffectLeft.isFinished()) {
            canvas.save();
            try {
                canvas.translate(0, canvas.getHeight() + verticalOffset);
                canvas.rotate(-90, 0, 0);
                mEdgeEffectLeft.setSize(canvas.getHeight(), canvas.getWidth());
                if (mEdgeEffectLeft.draw(canvas)) {
                    needsInvalidate = true;
                }
            } finally {
                canvas.restore();
            }
        }

        if (!mEdgeEffectRight.isFinished()) {
            canvas.save();
            try {
                canvas.translate(width, verticalOffset);
                canvas.rotate(90, 0, 0);
                mEdgeEffectRight.setSize(canvas.getHeight(), canvas.getWidth());
                if (mEdgeEffectRight.draw(canvas)) {
                    needsInvalidate = true;
                }
            } finally {
                canvas.restore();
            }
        }
        return needsInvalidate;
    }

    @Override
    public void onReleaseEdgeEffects() {
        mEdgeEffectLeft.onRelease();
        mEdgeEffectRight.onRelease();
    }

    @Override
    public void onPullEdgeEffects(int whichEdge, float deltaDistance) {
        if (whichEdge < 0) {
            // left
            mEdgeEffectLeft.onPull(deltaDistance);
        } else if (whichEdge > 0) {
            // right
            mEdgeEffectRight.onPull(deltaDistance);
        }
    }

    @Override
    public void onDoubleTapZoomAnimationBegin() {
    }

    @Override
    public void onDoubleTapZoomAnimationEnd() {
    }

    @Override
    public void onNightModeUpdated(boolean isNightMode) {
    }

    @Override
    public void onRenderingFinished() {

    }

    protected boolean onQuickMenuClicked(int menu_id, String menu_type) {
        try {
            if (((ToolManager) mPDFView.getToolManager()) != null) {
                return ((ToolManager) mPDFView.getToolManager()).onQuickMenuClicked(menu_id, menu_type);
            }
        } catch (Exception e) {
        }
        return false;
    }

    protected boolean isMenuShown() {
        return mMenuShown;
    }

    // change the note menu button text based on if the note has contents
    protected void updateQuickMenuNoteText(String note) {
        int size = mMenuTitles.size();
        for (int i = 0; i < size; i++) {
            // get note menu entry
            if (mMenuTitles.get(i).getType().equals(QM_NOTE)) {
                MenuEntry noteEntry = mMenuTitles.get(i);

                // update note text
                if (note != null && !note.equals("")) {
                    noteEntry.setText(getStringFromResId(R.string.tools_qm_view_note));
                } else {
                    noteEntry.setText(getStringFromResId(R.string.tools_qm_add_note));
                }
                break;
            }
        }

    }

    public void setupAnnotProperty(int color, float opacity, float thickness, int fillColor, String icon,
            String pdfTronFontName) {
    }

    public void setForceSameNextToolMode(boolean mode) {
        mForceSameNextToolMode = mode;
    }

    public boolean isEditingAnnot() {
        try {
            if (getMode() == ToolManager.e_text_create) {
                return ((FreeTextCreate) this).isFreeTextEditing();
            } else if (getMode() == ToolManager.e_annot_edit) {
                return ((AnnotEdit) this).isFreeTextEditing();
            }
        } catch (Exception e) {
        }
        return false;
    }

    public void setPageNumberIndicatorVisible(boolean visible) {
        mPageNumberIndicatorVisible = visible;
    }

    protected void setNextToolModeHelper(int nextToolMode) {
        if (mForceSameNextToolMode) {
            mNextToolMode = getMode();
        } else {
            mNextToolMode = nextToolMode;
        }
    }

    protected void setCurrentDefaultToolModeHelper(int defaultToolMode) {
        if (mForceSameNextToolMode) {
            mCurrentDefaultToolMode = defaultToolMode;
        } else {
            mCurrentDefaultToolMode = ToolManager.e_pan;
        }
    }

    protected boolean skipOnUpPriorEvent(int priorEvent) {
        if (priorEvent == PDFViewCtrl.PRIOR_EVENT_FLING || priorEvent == PDFViewCtrl.PRIOR_EVENT_SCROLLING) {
            return true;
        } else {
            return false;
        }
    }

    //    protected void showTransientPageNumber()
    // {
    //        mPageNumberRemovalHandler.removeMessages(1);
    //        mShowPageNum = true;
    //        mPageNumberRemovalHandler.sendEmptyMessageDelayed(1, 3000);
    //        mPDFView.invalidate();
    //    }

    /**
     * Build the bounding box of the annotation.
     */
    protected void buildAnnotBBox() {
        if (mAnnot != null) {
            mAnnotBBox.set(0, 0, 0, 0);
            try {
                com.pdftron.pdf.Rect r = mAnnot.getVisibleContentBox();
                mAnnotBBox.set((float) r.getX1(), (float) r.getY1(), (float) r.getX2(), (float) r.getY2());
            } catch (Exception e) {

            }
        }
    }

    protected boolean isInsideAnnot(float screen_x, float screen_y) {
        if (mAnnot != null) {
            double[] pts = mPDFView.convScreenPtToPagePt((double) screen_x, (double) screen_y, mAnnotPageNum);
            if (mAnnotBBox.contains((float) pts[0], (float) pts[1])) {
                return true;
            }
        }
        return false;
    }

    protected RectF getAnnotRect() {
        if (mAnnot != null) {
            double[] pts1 = mPDFView.convPagePtToScreenPt(mAnnotBBox.left, mAnnotBBox.bottom, mAnnotPageNum);
            double[] pts2 = mPDFView.convPagePtToScreenPt(mAnnotBBox.right, mAnnotBBox.top, mAnnotPageNum);
            return new RectF((float) pts1[0], (float) pts1[1], (float) pts2[0], (float) pts2[1]);
        } else {
            return null;
        }
    }

    protected void doneTwoFingerScrolling() {
        mAllowTwoFingerScroll = false;
    }

    protected void doneOneFingerScrollingWithStylus() {
        mAllowOneFingerScrollWithStylus = false;
    }

    /**
     * Shows the quick menu.
     */
    public boolean showMenu(List<MenuEntry> menu_titles, RectF anchor_rect) {
        return showMenu(menu_titles, anchor_rect, null);
    }

    public boolean showMenu(List<MenuEntry> menu_titles, RectF anchor_rect, List<MenuEntry> overflow_menu_titles) {
        if (anchor_rect == null) {
            return false;
        }

        int menu_sz = menu_titles.size();
        if (menu_sz > 0) {
            if (mQuickMenu != null) {
                closeMenu();
                mQuickMenu = null;
            }

            RectF client_r = new RectF(0, 0, mPDFView.getWidth(), mPDFView.getHeight());
            if (!client_r.intersect(anchor_rect)) {
                return false;
            }

            View anchor = new View(mPDFView.getContext());
            anchor.setVisibility(View.INVISIBLE);

            RectF anchorRect = calculateQMAnchor(anchor_rect);
            int atop = (int) anchorRect.top;
            int abottom = (int) anchorRect.bottom;
            int aright = (int) anchorRect.right;
            int aleft = (int) anchorRect.left;

            anchor.layout(aleft, atop, aright, abottom);

            ToolManager toolManager = (ToolManager) mPDFView.getToolManager();

            toolManager.setQuickMenuJustClosed(false);

            mQuickMenu = new QuickMenu(anchor, atop, aleft, abottom, aright, menu_titles, overflow_menu_titles,
                    toolManager, new PopupWindow.OnDismissListener() {
                        public void onDismiss() {
                            if (mPDFView.getToolManager() instanceof ToolManager) {
                                ((ToolManager) mPDFView.getToolManager()).setQuickMenuJustClosed(true);
                            }
                            // When dismissed, trigger the menu-clicked call-back function.
                            mMenuShown = false;
                            if (mQuickMenu != null) {
                                int selected = mQuickMenu.getSelectedId();
                                if (selected >= 0) {
                                    com.pdftron.pdf.utils.AnalyticsHandlerAdapter.getInstance().sendEvent(
                                            AnalyticsHandlerAdapter.CATEGORY_QUICKTOOL,
                                            mQuickMenu.getSelectedType() + " selected");

                                    onQuickMenuClicked(selected, mQuickMenu.getSelectedType());
                                }
                            }
                        }
                    }, mPDFView.isHardwareAccelerated());
            //adriaan
            if (!ToolManager.isPenMarkActive && !ToolManager.isRedHighlight && !ToolManager.isGreenHighlight) {
                mQuickMenu.show();
                mMenuShown = true;
                return true;
            } else {
                ToolManager.isRedHighlight = false;
                ToolManager.isGreenHighlight = false;
                return false;
            }
        }
        return false;
    }

    public RectF calculateQMAnchor(RectF anchor_rect) {
        if (anchor_rect != null) {
            int left = (int) anchor_rect.left;
            int top = (int) anchor_rect.top;
            int right = (int) anchor_rect.right;
            int bottom = (int) anchor_rect.bottom;

            try {
                // normalize the rect
                com.pdftron.pdf.Rect rect = new com.pdftron.pdf.Rect((double) anchor_rect.left,
                        (double) anchor_rect.top, (double) anchor_rect.right, (double) anchor_rect.bottom);
                rect.normalize();
                left = (int) rect.getX1();
                top = (int) rect.getY1();
                right = (int) rect.getX2();
                bottom = (int) rect.getY2();
            } catch (PDFNetException e) {
            }

            int[] location = new int[2];
            mPDFView.getLocationInWindow(location);

            int atop = top + location[1];
            int aleft = left + location[0];
            int aright = right + location[0];
            int abottom = bottom + location[1];

            RectF qmAnchor = new RectF(aleft, atop, aright, abottom);
            return qmAnchor;
        }
        return null;
    }

    public void selectAnnot(Annot annot, int pageNum) {
        // Since find text locks the document, cancel it to release the document.
        mPDFView.cancelFindText();
        try {
            mPDFView.docLockRead();
            if (annot != null && annot.isValid()) {
                setAnnot(annot, pageNum);
                buildAnnotBBox();
            }
        } catch (Exception e1) {

        } finally {
            mPDFView.docUnlockRead();
        }
    }

    /**
     * Computes the page bounding box in the client space.
     */
    protected RectF buildPageBoundBoxOnClient(int page_num) {
        RectF rect = null;
        if (page_num >= 1) {
            try {
                mPDFView.docLockRead();
                Page page = mPDFView.getDoc().getPage(page_num);
                if (page != null) {
                    rect = new RectF();
                    com.pdftron.pdf.Rect r = page.getBox(mPDFView.getPageBox());

                    double x1 = r.getX1();
                    double y1 = r.getY1();
                    double x2 = r.getX2();
                    double y2 = r.getY2();
                    double[] pts1, pts2, pts3, pts4;

                    // Need to compute the transformed coordinates for the four
                    // corners of the page bounding box, since a page can be rotated.
                    pts1 = mPDFView.convPagePtToScreenPt(x1, y1, page_num);
                    pts2 = mPDFView.convPagePtToScreenPt(x2, y1, page_num);
                    pts3 = mPDFView.convPagePtToScreenPt(x2, y2, page_num);
                    pts4 = mPDFView.convPagePtToScreenPt(x1, y2, page_num);

                    double min_x = Math.min(Math.min(Math.min(pts1[0], pts2[0]), pts3[0]), pts4[0]);
                    double max_x = Math.max(Math.max(Math.max(pts1[0], pts2[0]), pts3[0]), pts4[0]);
                    double min_y = Math.min(Math.min(Math.min(pts1[1], pts2[1]), pts3[1]), pts4[1]);
                    double max_y = Math.max(Math.max(Math.max(pts1[1], pts2[1]), pts3[1]), pts4[1]);

                    float sx = mPDFView.getScrollX();
                    float sy = mPDFView.getScrollY();
                    rect = new RectF();
                    rect.set((float) min_x + sx, (float) min_y + sy, (float) max_x + sx, (float) max_y + sy);
                }

            } catch (Exception e) {

            } finally {
                mPDFView.docUnlockRead();
            }
        }
        return rect;
    }

    /**
     * Converts density independent pixels to physical pixels.
     */
    protected float convDp2Pix(float dp) {
        float density = mPDFView.getContext().getResources().getDisplayMetrics().density;
        return dp * density;
    }

    /**
     * Converts physical pixels to density independent pixels.
     */
    protected float convPix2Dp(float pix) {
        float density = mPDFView.getContext().getResources().getDisplayMetrics().density;
        return pix / density;
    }

    /**
     * Gets a rectangle to use when selecting text.
     */
    protected RectF getTextSelectRect(float x, float y) {
        float delta = 0.5f;
        float x2 = x + delta;
        float y2 = y + delta;
        delta *= 2;
        float x1 = x2 - delta >= 0 ? x2 - delta : 0;
        float y1 = y2 - delta >= 0 ? y2 - delta : 0;

        return new RectF(x1, y1, x2, y2);
    }

    protected String getStringFromResId(int id) {
        return mPDFView.getResources().getString(id);
    }

    protected void setAuthor(Markup annot) {
        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(mPDFView.getContext());
        boolean authorNameHasBeenAsked = pref.getBoolean("pref_author_name_has_been_asked", false);
        String authorName = pref.getString("pref_author_name", "");
        if (!authorNameHasBeenAsked && authorName.isEmpty()) {
            // Show dialog to get the author name.
            boolean askAuthor = false;
            if (mPDFView.getToolManager() instanceof ToolManager) {
                if (((ToolManager) mPDFView.getToolManager()).isShowAuthorDialog()) {
                    askAuthor = true;
                }
            }

            mMarkupToAuthor = annot;

            String possibleName = "";
            // If the author name in the preferences is empty, we try to get
            // the name of the current user in the device.
            int res = mPDFView.getContext().checkCallingOrSelfPermission("android.permission.GET_ACCOUNTS");
            if (res == PackageManager.PERMISSION_GRANTED) {

                Pattern emailPattern = Patterns.EMAIL_ADDRESS;
                //Account[] accounts = AccountManager.get(mPDFView.getContext()).getAccounts();
                //                for (Account account : accounts) {
                //                    if (emailPattern.matcher(account.name).matches()) {
                //                        possibleName = account.name;
                //                        break;
                //                    }
                //                }
            }

            final SharedPreferences.Editor editor = pref.edit();
            editor.putBoolean("pref_author_name_has_been_asked", true);
            editor.commit();

            if (askAuthor) {
                LayoutInflater inflater = (LayoutInflater) mPDFView.getContext()
                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                View authorNameDialog = inflater.inflate(R.layout.tools_dialog_author_name, null);
                final EditText authorNameEditText = (EditText) authorNameDialog
                        .findViewById(R.id.tools_dialog_author_name_edittext);
                authorNameEditText.setText(possibleName);
                authorNameEditText.selectAll();

                AlertDialog.Builder builder = new AlertDialog.Builder(mPDFView.getContext());
                final AlertDialog authorDialog = builder.setView(authorNameDialog)
                        .setTitle(R.string.tools_dialog_author_name_title)
                        .setPositiveButton(R.string.tools_misc_ok, new OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                String author = authorNameEditText.getText().toString().trim();
                                // Set author information on the markup
                                setAuthor(mMarkupToAuthor, author);
                                // Update preferences with the new name
                                //SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(mPDFView.getContext()).edit();
                                editor.putString("pref_author_name", author);
                                editor.commit();
                            }
                        }).setNegativeButton(R.string.tools_misc_skip, new OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                            }
                        }).create();
                authorDialog.show();
                if (authorNameEditText.getText().length() == 0) {
                    // empty, don't allow OK
                    authorDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
                } else {
                    authorDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true);
                }
                authorNameEditText.addTextChangedListener(new TextWatcher() {
                    @Override
                    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                    }

                    @Override
                    public void onTextChanged(CharSequence s, int start, int before, int count) {
                    }

                    @Override
                    public void afterTextChanged(Editable s) {
                        if (authorDialog != null) {
                            if (s.length() == 0) {
                                // empty, don't allow OK
                                authorDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
                            } else {
                                authorDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true);
                            }
                        }
                    }
                });
            } else {
                // Set author information on the markup
                setAuthor(mMarkupToAuthor, possibleName);
                // Update preferences with the new name
                //SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(mPDFView.getContext()).edit();
                editor.putString("pref_author_name", possibleName);
                editor.commit();
            }
        } else {
            // Use author name in the preferences
            String author = pref.getString("pref_author_name", "");
            setAuthor(annot, author);
        }
    }

    private void setAuthor(Markup annot, String author) {
        try {
            mPDFView.docLock(true);
            annot.setTitle(author);
        } catch (PDFNetException e) {
        } finally {
            mPDFView.docUnlock();
        }
    }

    class MenuEntry {

        private String mType;
        private String mText;

        public MenuEntry(String type, String text) {
            this.mType = type;
            this.mText = text;
        }

        public MenuEntry(String text) {
            this.mType = text;
            this.mText = text;
        }

        public void setText(String text) {
            this.mText = text;
        }

        public String getType() {
            return this.mType;
        }

        public String getText() {
            return this.mText;
        }
    }

    // Helper functions to obtain the storage key string for each of the annotation property
    public String getColorKey(int mode) {
        switch (mode) {
        case ToolManager.e_text_highlight:
            return Tool.PREF_ANNOTATION_CREATION_HIGHLIGHT + Tool.PREF_ANNOTATION_CREATION_COLOR;
        case ToolManager.e_text_underline:
            return Tool.PREF_ANNOTATION_CREATION_UNDERLINE + Tool.PREF_ANNOTATION_CREATION_COLOR;
        case ToolManager.e_text_strikeout:
            return Tool.PREF_ANNOTATION_CREATION_STRIKEOUT + Tool.PREF_ANNOTATION_CREATION_COLOR;
        case ToolManager.e_text_squiggly:
            return Tool.PREF_ANNOTATION_CREATION_SQUIGGLY + Tool.PREF_ANNOTATION_CREATION_COLOR;
        case ToolManager.e_text_create:
            return Tool.PREF_ANNOTATION_CREATION_FREETEXT + Tool.PREF_ANNOTATION_CREATION_COLOR;
        case ToolManager.e_ink_create:
            return Tool.PREF_ANNOTATION_CREATION_FREEHAND + Tool.PREF_ANNOTATION_CREATION_COLOR;
        case ToolManager.e_oval_create:
            return Tool.PREF_ANNOTATION_CREATION_OVAL + Tool.PREF_ANNOTATION_CREATION_COLOR;
        case ToolManager.e_rect_create:
            return Tool.PREF_ANNOTATION_CREATION_RECTANGLE + Tool.PREF_ANNOTATION_CREATION_COLOR;
        case ToolManager.e_text_annot_create:
            return Tool.PREF_ANNOTATION_CREATION_NOTE + Tool.PREF_ANNOTATION_CREATION_COLOR;
        default:
            return Tool.PREF_ANNOTATION_CREATION_LINE + Tool.PREF_ANNOTATION_CREATION_COLOR;
        }
    }

    protected String getThicknessKey(int mode) {
        switch (mode) {
        case ToolManager.e_text_underline:
            return Tool.PREF_ANNOTATION_CREATION_UNDERLINE + Tool.PREF_ANNOTATION_CREATION_THICKNESS;
        case ToolManager.e_text_strikeout:
            return Tool.PREF_ANNOTATION_CREATION_STRIKEOUT + Tool.PREF_ANNOTATION_CREATION_THICKNESS;
        case ToolManager.e_text_squiggly:
            return Tool.PREF_ANNOTATION_CREATION_SQUIGGLY + Tool.PREF_ANNOTATION_CREATION_THICKNESS;
        case ToolManager.e_text_create:
            return Tool.PREF_ANNOTATION_CREATION_FREETEXT + Tool.PREF_ANNOTATION_CREATION_THICKNESS;
        case ToolManager.e_ink_create:
            return Tool.PREF_ANNOTATION_CREATION_FREEHAND + Tool.PREF_ANNOTATION_CREATION_THICKNESS;
        case ToolManager.e_oval_create:
            return Tool.PREF_ANNOTATION_CREATION_OVAL + Tool.PREF_ANNOTATION_CREATION_THICKNESS;
        case ToolManager.e_rect_create:
            return Tool.PREF_ANNOTATION_CREATION_RECTANGLE + Tool.PREF_ANNOTATION_CREATION_THICKNESS;
        case ToolManager.e_ink_eraser:
            return Tool.PREF_ANNOTATION_CREATION_ERASER + Tool.PREF_ANNOTATION_CREATION_THICKNESS;
        default:
            return Tool.PREF_ANNOTATION_CREATION_LINE + Tool.PREF_ANNOTATION_CREATION_THICKNESS;
        }
    }

    protected String getOpacityKey(int mode) {
        switch (mode) {
        case ToolManager.e_text_highlight:
            return Tool.PREF_ANNOTATION_CREATION_HIGHLIGHT + Tool.PREF_ANNOTATION_CREATION_OPACITY;
        case ToolManager.e_text_underline:
            return Tool.PREF_ANNOTATION_CREATION_UNDERLINE + Tool.PREF_ANNOTATION_CREATION_OPACITY;
        case ToolManager.e_text_strikeout:
            return Tool.PREF_ANNOTATION_CREATION_STRIKEOUT + Tool.PREF_ANNOTATION_CREATION_OPACITY;
        case ToolManager.e_text_squiggly:
            return Tool.PREF_ANNOTATION_CREATION_SQUIGGLY + Tool.PREF_ANNOTATION_CREATION_OPACITY;
        case ToolManager.e_text_create:
            return Tool.PREF_ANNOTATION_CREATION_FREETEXT + Tool.PREF_ANNOTATION_CREATION_OPACITY;
        case ToolManager.e_ink_create:
            return Tool.PREF_ANNOTATION_CREATION_FREEHAND + Tool.PREF_ANNOTATION_CREATION_OPACITY;
        case ToolManager.e_oval_create:
            return Tool.PREF_ANNOTATION_CREATION_OVAL + Tool.PREF_ANNOTATION_CREATION_OPACITY;
        case ToolManager.e_rect_create:
            return Tool.PREF_ANNOTATION_CREATION_RECTANGLE + Tool.PREF_ANNOTATION_CREATION_OPACITY;
        default:
            return Tool.PREF_ANNOTATION_CREATION_LINE + Tool.PREF_ANNOTATION_CREATION_OPACITY;
        }
    }

    protected String getColorFillKey(int mode) {
        switch (mode) {
        case ToolManager.e_text_create:
            return Tool.PREF_ANNOTATION_CREATION_FREETEXT + Tool.PREF_ANNOTATION_CREATION_FILL_COLOR;
        case ToolManager.e_oval_create:
            return Tool.PREF_ANNOTATION_CREATION_OVAL + Tool.PREF_ANNOTATION_CREATION_FILL_COLOR;
        default:
            return Tool.PREF_ANNOTATION_CREATION_RECTANGLE + Tool.PREF_ANNOTATION_CREATION_FILL_COLOR;
        }
    }

    protected String getIconKey(int mode) {
        switch (mode) {
        case ToolManager.e_text_annot_create:
            return Tool.PREF_ANNOTATION_CREATION_NOTE + Tool.PREF_ANNOTATION_CREATION_ICON;
        default:
            return Tool.PREF_ANNOTATION_CREATION_NOTE + Tool.PREF_ANNOTATION_CREATION_ICON;
        }
    }

    protected String getFontKey(int mode) {
        switch (mode) {
        case ToolManager.e_text_create:
            return Tool.PREF_ANNOTATION_CREATION_FREETEXT + Tool.PREF_ANNOTATION_CREATION_FONT;
        default:
            return Tool.PREF_ANNOTATION_CREATION_FREETEXT + Tool.PREF_ANNOTATION_CREATION_FONT;
        }
    }

    protected String getOverflowMenuKey(int menuType) {
        switch (menuType) {
        case QUICK_TOOLS_MENU_TEXT_SELECT:
            return PREF_QUICK_TOOLS_MENU + PREF_QUICK_TOOLS_MENU_OVERFLOW + PREF_QUICK_TOOLS_MENU_TEXT_SELECT;
        case QUICK_TOOLS_MENU_TEXT_MARKUP:
            return PREF_QUICK_TOOLS_MENU + PREF_QUICK_TOOLS_MENU_OVERFLOW + PREF_QUICK_TOOLS_MENU_TEXT_MARKUP;
        default:
            return PREF_QUICK_TOOLS_MENU + PREF_QUICK_TOOLS_MENU_OVERFLOW + PREF_QUICK_TOOLS_MENU_TEXT_SELECT;
        }
    }

    protected String getOverflowMenuDefault(int menuType) {
        if (ToolManager.showExtraTextSelectMenuOptions()) {
            switch (menuType) {
            case QUICK_TOOLS_MENU_TEXT_MARKUP:
                return PREF_QUICK_TOOLS_MENU_EXTRA_TEXT_MARKUP_OVERFLOW_DEFAULT;
            case QUICK_TOOLS_MENU_TEXT_SELECT:
                return PREF_QUICK_TOOLS_MENU_EXTRA_TEXT_SELECT_OVERFLOW_DEFAULT;
            default:
                return PREF_QUICK_TOOLS_MENU_EXTRA_TEXT_SELECT_OVERFLOW_DEFAULT;
            }
        } else {
            switch (menuType) {
            case QUICK_TOOLS_MENU_TEXT_MARKUP:
                return PREF_QUICK_TOOLS_MENU_TEXT_MARKUP_OVERFLOW_DEFAULT;
            case QUICK_TOOLS_MENU_TEXT_SELECT:
                return PREF_QUICK_TOOLS_MENU_TEXT_SELECT_OVERFLOW_DEFAULT;
            default:
                return PREF_QUICK_TOOLS_MENU_TEXT_SELECT_OVERFLOW_DEFAULT;
            }
        }
    }

    protected String getMRUMenuKey(int menuType) {
        switch (menuType) {
        case QUICK_TOOLS_MENU_TEXT_SELECT:
            return PREF_QUICK_TOOLS_MENU + PREF_QUICK_TOOLS_MENU_MRU + PREF_QUICK_TOOLS_MENU_TEXT_SELECT;
        case QUICK_TOOLS_MENU_TEXT_MARKUP:
            return PREF_QUICK_TOOLS_MENU + PREF_QUICK_TOOLS_MENU_MRU + PREF_QUICK_TOOLS_MENU_TEXT_MARKUP;
        default:
            return PREF_QUICK_TOOLS_MENU + PREF_QUICK_TOOLS_MENU_MRU + PREF_QUICK_TOOLS_MENU_TEXT_SELECT;
        }
    }

    protected String getMRUMenuDefault(int menuType) {
        if (ToolManager.showExtraTextSelectMenuOptions()) {
            switch (menuType) {
            case QUICK_TOOLS_MENU_TEXT_MARKUP:
                return PREF_QUICK_TOOLS_MENU_EXTRA_TEXT_MARKUP_MRU_DEFAULT;
            case QUICK_TOOLS_MENU_TEXT_SELECT:
                return PREF_QUICK_TOOLS_MENU_EXTRA_TEXT_SELECT_MRU_DEFAULT;
            default:
                return PREF_QUICK_TOOLS_MENU_EXTRA_TEXT_SELECT_MRU_DEFAULT;
            }
        } else {
            switch (menuType) {
            case QUICK_TOOLS_MENU_TEXT_MARKUP:
                return PREF_QUICK_TOOLS_MENU_TEXT_MARKUP_MRU_DEFAULT;
            case QUICK_TOOLS_MENU_TEXT_SELECT:
                return PREF_QUICK_TOOLS_MENU_TEXT_SELECT_MRU_DEFAULT;
            default:
                return PREF_QUICK_TOOLS_MENU_TEXT_SELECT_MRU_DEFAULT;
            }
        }
    }

    protected int getModeFromAnnotType(Annot annot) {
        int mode = ToolManager.e_line_create;
        try {
            int annotType = annot.getType();
            switch (annotType) {
            case Annot.e_Square:
                return ToolManager.e_rect_create;
            case Annot.e_Circle:
                return ToolManager.e_oval_create;
            case Annot.e_Highlight:
                return ToolManager.e_text_highlight;
            case Annot.e_Underline:
                return ToolManager.e_text_underline;
            case Annot.e_StrikeOut:
                return ToolManager.e_text_strikeout;
            case Annot.e_Squiggly:
                return ToolManager.e_text_squiggly;
            case Annot.e_FreeText:
                return ToolManager.e_text_create;
            case Annot.e_Ink:
                return ToolManager.e_ink_create;
            case Annot.e_Text:
                return ToolManager.e_text_annot_create;
            default:
                return ToolManager.e_line_create;
            }
        } catch (PDFNetException e) {

        }
        return mode;
    }

    protected void raiseAnnotationAddedEvent(Annot annot, int page) {
        ToolManager toolManager = (ToolManager) mPDFView.getToolManager();
        toolManager.raiseAnnotationAddedEvent(annot, page);
    }

    protected void raiseAnnotationModifiedEvent(Annot annot, int page) {
        ToolManager toolManager = (ToolManager) mPDFView.getToolManager();
        toolManager.raiseAnnotationModifiedEvent(annot, page);
    }

    protected void raiseAnnotationRemovedEvent(Annot annot, int page) {
        ToolManager toolManager = (ToolManager) mPDFView.getToolManager();
        toolManager.raiseAnnotationRemovedEvent(annot, page);
        toolManager.myAnnotPage(annot, page);
    }

    protected void raiseAnnotationPreRemoveEvent(Annot annot, int page) {
        ToolManager toolManager = (ToolManager) mPDFView.getToolManager();
        toolManager.raiseAnnotationPreRemoveEvent(annot, page);
    }

    protected void addOldTools() {
        ToolManager toolManager = (ToolManager) mPDFView.getToolManager();
        if (null != toolManager) {
            toolManager.getOldTools().add(this);
        }
    }

    protected com.pdftron.pdf.Rect convertFromPageRectToScreenRect(com.pdftron.pdf.Rect pageRect, int page) {
        com.pdftron.pdf.Rect screenRect = null;

        if (pageRect != null) {
            try {
                float sx = mPDFView.getScrollX();
                float sy = mPDFView.getScrollY();

                float x1, y1, x2, y2;

                double[] pts1 = mPDFView.convPagePtToScreenPt(pageRect.getX1(), pageRect.getY1(), page);
                double[] pts2 = mPDFView.convPagePtToScreenPt(pageRect.getX2(), pageRect.getY2(), page);

                x1 = (float) pts1[0] + sx;
                y1 = (float) pts1[1] + sy;
                x2 = (float) pts2[0] + sx;
                y2 = (float) pts2[1] + sy;

                screenRect = new com.pdftron.pdf.Rect(x1, y1, x2, y2);

            } catch (PDFNetException ex) {
            }
        }

        return screenRect;
    }

    protected int getInvertColor(int color) {
        int a = Color.alpha(color);
        int r = Color.red(color);
        int g = Color.green(color);
        int b = Color.blue(color);

        int invertColor = Color.argb(a, 255 - r, 255 - g, 255 - b);
        return invertColor;
    }

    protected void setAnnot(Annot annot, int page) {
        mAnnot = annot;
        mAnnotPageNum = page;
        try {
            if (mAnnot.getUniqueID() != null && mPDFView.getToolManager() instanceof ToolManager) {
                ((ToolManager) mPDFView.getToolManager()).setSelectedAnnot(mAnnot.getUniqueID().getAsPDFText(),
                        mAnnotPageNum);
            }
        } catch (Exception e) {
            AnalyticsHandlerAdapter.getInstance().sendException(e);
        }
    }

    protected void unsetAnnot() {
        mAnnot = null;
        try {
            if (mPDFView.getToolManager() instanceof ToolManager) {
                ((ToolManager) mPDFView.getToolManager()).setSelectedAnnot(null, -1);
            }
        } catch (Exception e) {
            AnalyticsHandlerAdapter.getInstance().sendException(e);
        }
    }
}