cx.fbn.nevernote.gui.BrowserWindow.java Source code

Java tutorial

Introduction

Here is the source code for cx.fbn.nevernote.gui.BrowserWindow.java

Source

/*
 * This file is part of NixNote/NeighborNote 
 * Copyright 2009 Randy Baumgarte
 * Copyright 2013 Yuki Takahashi
 * 
 * This file may be licensed under the terms of of the
 * GNU General Public License Version 2 (the ``GPL'').
 *
 * Software distributed under the License is distributed
 * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
 * express or implied. See the GPL for the specific language
 * governing rights and limitations.
 *
 * You should have received a copy of the GPL along with this
 * program. If not, go to http://www.gnu.org/licenses/gpl.html
 * or write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

package cx.fbn.nevernote.gui;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.FileNameMap;
import java.net.URI;
import java.net.URLConnection;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;

import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;

import com.evernote.edam.limits.Constants;
import com.evernote.edam.type.Data;
import com.evernote.edam.type.Note;
import com.evernote.edam.type.Notebook;
import com.evernote.edam.type.Resource;
import com.evernote.edam.type.ResourceAttributes;
import com.evernote.edam.type.Tag;
import com.evernote.edam.type.User;
import com.swabunga.spell.engine.Configuration;
import com.swabunga.spell.engine.SpellDictionary;
import com.swabunga.spell.engine.SpellDictionaryHashMap;
import com.swabunga.spell.engine.Word;
import com.swabunga.spell.event.SpellCheckEvent;
import com.swabunga.spell.event.SpellCheckListener;
import com.swabunga.spell.event.SpellChecker;
import com.swabunga.spell.event.StringWordTokenizer;
import com.trolltech.qt.core.QByteArray;
import com.trolltech.qt.core.QCoreApplication;
import com.trolltech.qt.core.QDataStream;
import com.trolltech.qt.core.QDateTime;
import com.trolltech.qt.core.QEvent;
import com.trolltech.qt.core.QEvent.Type;
import com.trolltech.qt.core.QFile;
import com.trolltech.qt.core.QFileSystemWatcher;
import com.trolltech.qt.core.QIODevice;
import com.trolltech.qt.core.QMimeData;
import com.trolltech.qt.core.QSize;
import com.trolltech.qt.core.QTextCodec;
import com.trolltech.qt.core.QTimer;
import com.trolltech.qt.core.QUrl;
import com.trolltech.qt.core.Qt;
import com.trolltech.qt.core.Qt.Key;
import com.trolltech.qt.core.Qt.KeyboardModifier;
import com.trolltech.qt.core.Qt.KeyboardModifiers;
import com.trolltech.qt.gui.QAction;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QCalendarWidget;
import com.trolltech.qt.gui.QClipboard;
import com.trolltech.qt.gui.QClipboard.Mode;
import com.trolltech.qt.gui.QColor;
import com.trolltech.qt.gui.QComboBox;
import com.trolltech.qt.gui.QDateEdit;
import com.trolltech.qt.gui.QDesktopServices;
import com.trolltech.qt.gui.QFileDialog;
import com.trolltech.qt.gui.QFileDialog.AcceptMode;
import com.trolltech.qt.gui.QFileDialog.FileMode;
import com.trolltech.qt.gui.QFont;
import com.trolltech.qt.gui.QFontDatabase;
import com.trolltech.qt.gui.QFormLayout;
import com.trolltech.qt.gui.QGridLayout;
import com.trolltech.qt.gui.QHBoxLayout;
import com.trolltech.qt.gui.QIcon;
import com.trolltech.qt.gui.QImage;
import com.trolltech.qt.gui.QKeyEvent;
import com.trolltech.qt.gui.QKeySequence;
import com.trolltech.qt.gui.QLabel;
import com.trolltech.qt.gui.QLineEdit;
import com.trolltech.qt.gui.QListWidgetItem;
import com.trolltech.qt.gui.QMatrix;
import com.trolltech.qt.gui.QMessageBox;
import com.trolltech.qt.gui.QPalette;
import com.trolltech.qt.gui.QPalette.ColorRole;
import com.trolltech.qt.gui.QPushButton;
import com.trolltech.qt.gui.QShortcut;
import com.trolltech.qt.gui.QSizePolicy;
import com.trolltech.qt.gui.QSplitter;
import com.trolltech.qt.gui.QTextEdit;
import com.trolltech.qt.gui.QTextEdit.LineWrapMode;
import com.trolltech.qt.gui.QTimeEdit;
import com.trolltech.qt.gui.QToolButton;
import com.trolltech.qt.gui.QToolButton.ToolButtonPopupMode;
import com.trolltech.qt.gui.QVBoxLayout;
import com.trolltech.qt.gui.QWidget;
import com.trolltech.qt.network.QNetworkAccessManager;
import com.trolltech.qt.network.QNetworkReply;
import com.trolltech.qt.network.QNetworkReply.NetworkError;
import com.trolltech.qt.network.QNetworkRequest;
import com.trolltech.qt.webkit.QWebPage;
import com.trolltech.qt.webkit.QWebPage.WebAction;
import com.trolltech.qt.webkit.QWebSettings;
import com.trolltech.qt.webkit.QWebView;

import cx.fbn.nevernote.Global;
import cx.fbn.nevernote.clipboard.ClipBoardObserver;
import cx.fbn.nevernote.dialog.EnCryptDialog;
import cx.fbn.nevernote.dialog.EnDecryptDialog;
import cx.fbn.nevernote.dialog.GeoDialog;
import cx.fbn.nevernote.dialog.InsertLatexImage;
import cx.fbn.nevernote.dialog.InsertLinkDialog;
import cx.fbn.nevernote.dialog.NoteQuickLinkDialog;
import cx.fbn.nevernote.dialog.SpellCheck;
import cx.fbn.nevernote.dialog.TableDialog;
import cx.fbn.nevernote.dialog.TagAssign;
import cx.fbn.nevernote.evernote.EnCrypt;
import cx.fbn.nevernote.filters.FilterEditorTags;
import cx.fbn.nevernote.signals.NoteResourceSignal;
import cx.fbn.nevernote.signals.NoteSignal;
import cx.fbn.nevernote.sql.DatabaseConnection;
import cx.fbn.nevernote.utilities.ApplicationLogger;
import cx.fbn.nevernote.utilities.FileUtils;
import cx.fbn.nevernote.utilities.Pair;
import cx.fbn.nevernote.xml.HtmlTagModifier;

public class BrowserWindow extends QWidget {

    public final QLineEdit titleLabel;
    private final QLineEdit urlText;
    private final QLabel authorLabel;
    private final QLineEdit authorText;
    private final QComboBox geoBox;
    public final TagLineEdit tagEdit;
    public final QLabel tagLabel;
    private final QPushButton urlLabel;
    private final QLabel alteredLabel;
    private final QDateEdit alteredDate;
    private final QTimeEdit alteredTime;
    private final QDateEdit createdDate;
    private final QTimeEdit createdTime;
    private final QLabel subjectLabel;
    private final QDateEdit subjectDate;
    private final QTimeEdit subjectTime;
    public final QComboBox notebookBox;
    private final QLabel notebookLabel;
    private final QLabel createdLabel;
    public final QComboBox fontSize;
    public final QAction fontSizeAction;
    private boolean extendedOn;
    public boolean buttonsVisible;
    private final String iconPath;
    public final ContentView browser;
    private final QTextEdit sourceEdit;
    private String sourceEditHeader;
    Highlighter syntaxHighlighter;
    private List<Tag> allTags;
    private List<String> currentTags;
    public NoteSignal noteSignal;
    public Signal2<String, String> evernoteLinkClicked;
    private List<Notebook> notebookList;
    private Note currentNote;
    private String saveNoteTitle;
    private String saveTagList;
    private boolean insideList;
    private final DatabaseConnection conn;
    private final QCalendarWidget createdCalendarWidget;
    private final QCalendarWidget alteredCalendarWidget;
    private final QCalendarWidget subjectCalendarWidget;

    public final QPushButton undoButton;
    public final QAction undoAction;
    public final QPushButton redoButton;
    public final QAction redoAction;
    public final QPushButton cutButton;
    public final QAction cutAction;
    public final QPushButton copyButton;
    public final QAction copyAction;
    public final QPushButton pasteButton;
    public final QAction pasteAction;
    public final QPushButton boldButton;
    public final QAction boldAction;
    public final QPushButton underlineButton;
    public final QAction underlineAction;
    public final QPushButton italicButton;
    public final QAction italicAction;
    public final Signal0 focusLost;
    public final NoteResourceSignal resourceSignal;

    public QPushButton rightAlignButton;
    public final QAction rightAlignAction;
    public QPushButton leftAlignButton;
    public final QAction leftAlignAction;
    public QPushButton centerAlignButton;
    public final QAction centerAlignAction;

    public final QPushButton strikethroughButton;
    public final QAction strikethroughAction;
    public final QPushButton hlineButton;
    public final QAction hlineAction;
    public final QPushButton indentButton;
    public final QAction indentAction;
    public final QPushButton outdentButton;
    public final QAction outdentAction;
    public final QPushButton bulletListButton;
    public final QAction bulletListAction;
    public final QPushButton numberListButton;
    public final QAction numberListAction;
    public final QPushButton spellCheckButton;
    public final QAction spellCheckAction;
    public final QPushButton todoButton;
    public final QAction todoAction;

    public final QShortcut focusTitleShortcut;
    public final QShortcut focusTagShortcut;
    public final QShortcut focusNoteShortcut;
    public final QShortcut focusUrlShortcut;
    public final QShortcut focusAuthorShortcut;

    public EditorButtonBar buttonLayout;
    public final QComboBox fontList;
    public final QAction fontListAction;
    public final QToolButton fontColor;
    public final QAction fontColorAction;
    private final ColorMenu fontColorMenu;
    public final QToolButton fontHilight;
    public final QAction fontHilightAction;
    private final ColorMenu fontHilightColorMenu;
    public final QFileSystemWatcher fileWatcher;
    public int cursorPosition;
    private boolean forceTextPaste;
    private String selectedFile;
    private String currentHyperlink;
    public boolean keepPDFNavigationHidden;
    private final ApplicationLogger logger;
    SpellDictionary dictionary;
    SpellDictionary userDictionary;
    SpellChecker spellChecker;
    SuggestionListener spellListener;
    private final HashMap<String, Integer> previewPageList;
    boolean insertHyperlink;
    boolean insideTable;
    boolean insideEncryption;
    public Signal1<BrowserWindow> blockApplication;
    public Signal0 unblockApplication;
    public boolean awaitingHttpResponse;
    public long unblockTime;
    private final QTimer setSourceTimer;
    String latexGuid; // This is set if we are editing an existing LaTeX formula.  Useful to track guid.

    private final ClipBoardObserver cbObserver;

    public static class SuggestionListener implements SpellCheckListener {
        public boolean abortSpellCheck = false;
        public boolean errorsFound = false;
        private final SpellCheck spellCheckDialog;

        private final BrowserWindow parent;

        public SuggestionListener(BrowserWindow parent, SpellChecker checker) {
            this.parent = parent;
            spellCheckDialog = new SpellCheck(checker);
        }

        public void spellingError(SpellCheckEvent event) {
            errorsFound = true;
            spellCheckDialog.setWord(event.getInvalidWord());

            @SuppressWarnings("unchecked")
            List<Word> suggestions = event.getSuggestions();
            spellCheckDialog.clearSuggestions();
            if (!suggestions.isEmpty()) {
                //             spellCheckDialog.setCurrentSuggestion(suggestions.get(0).getWord());
                for (int i = 0; i < suggestions.size(); i++) {
                    spellCheckDialog.addSuggestion(suggestions.get(i).getWord());
                }
                spellCheckDialog.setSelectedSuggestion(0);
            }
            spellCheckDialog.exec();
            if (spellCheckDialog.cancelPressed()) {
                abortSpellCheck = true;
                event.cancel();
                return;
            }
            if (spellCheckDialog.replacePressed()) {
                QClipboard clipboard = QApplication.clipboard();
                clipboard.setText(spellCheckDialog.getReplacementWord());
                parent.pasteClicked();
            }
            event.cancel();
        }
    }

    // ?cbObserver
    public BrowserWindow(DatabaseConnection c, ClipBoardObserver cbObserver) {
        logger = new ApplicationLogger("browser.log");
        logger.log(logger.HIGH, "Setting up browser");
        iconPath = new String("classpath:cx/fbn/nevernote/icons/");
        forceTextPaste = false;
        insertHyperlink = true;
        insideTable = false;
        insideEncryption = false;

        fileWatcher = new QFileSystemWatcher();
        //      fileWatcher.fileChanged.connect(this, "fileChanged(String)");
        noteSignal = new NoteSignal();
        titleLabel = new QLineEdit();
        evernoteLinkClicked = new Signal2<String, String>();
        titleLabel.setMaxLength(Constants.EDAM_NOTE_TITLE_LEN_MAX);
        urlText = new QLineEdit();
        authorText = new QLineEdit();
        geoBox = new QComboBox();
        urlLabel = new QPushButton();
        urlLabel.clicked.connect(this, "sourceUrlClicked()");
        authorLabel = new QLabel();
        conn = c;

        this.cbObserver = cbObserver;

        focusLost = new Signal0();

        tagEdit = new TagLineEdit(allTags);
        tagLabel = new QLabel(tr("Tags:"));
        tagEdit.focusLost.connect(this, "modifyTagsTyping()");

        createdCalendarWidget = new QCalendarWidget();
        createdDate = new QDateEdit();
        createdDate.setDisplayFormat(Global.getDateFormat());
        createdDate.setCalendarPopup(true);
        createdDate.setCalendarWidget(createdCalendarWidget);
        createdTime = new QTimeEdit();
        createdDate.dateChanged.connect(this, "createdChanged()");
        createdTime.timeChanged.connect(this, "createdChanged()");

        alteredCalendarWidget = new QCalendarWidget();
        alteredDate = new QDateEdit();
        alteredDate.setDisplayFormat(Global.getDateFormat());
        alteredDate.setCalendarPopup(true);
        alteredDate.setCalendarWidget(alteredCalendarWidget);
        alteredTime = new QTimeEdit();
        alteredLabel = new QLabel(tr("Altered:"));
        alteredDate.dateChanged.connect(this, "alteredChanged()");
        alteredTime.timeChanged.connect(this, "alteredChanged()");

        subjectCalendarWidget = new QCalendarWidget();
        subjectDate = new QDateEdit();
        subjectDate.setDisplayFormat(Global.getDateFormat());
        subjectDate.setCalendarPopup(true);
        subjectDate.setCalendarWidget(subjectCalendarWidget);
        subjectTime = new QTimeEdit();
        subjectLabel = new QLabel(tr("Subject Date:"));
        subjectDate.dateChanged.connect(this, "subjectDateTimeChanged()");
        subjectTime.timeChanged.connect(this, "subjectDateTimeChanged()");
        authorText.textChanged.connect(this, "authorChanged()");
        urlText.textChanged.connect(this, "sourceUrlChanged()");

        notebookBox = new QComboBox();
        notebookLabel = new QLabel(tr("Notebook"));
        createdLabel = new QLabel(tr("Created:"));
        // selectedText = new String();

        urlLabel.setVisible(false);
        urlText.setVisible(false);
        authorLabel.setVisible(false);

        geoBox.setVisible(false);
        geoBox.addItem(new QIcon(iconPath + "globe.png"), "");
        geoBox.addItem(new String(tr("Set")));
        geoBox.addItem(new String(tr("Clear")));
        geoBox.addItem(new String(tr("View On Map")));
        geoBox.activated.connect(this, "geoBoxChanged()");

        authorText.setVisible(false);
        createdDate.setVisible(false);
        alteredLabel.setVisible(false);
        //notebookBox.setVisible(false);
        notebookLabel.setVisible(false);
        createdLabel.setVisible(false);
        createdTime.setVisible(false);
        alteredDate.setVisible(false);
        alteredTime.setVisible(false);
        subjectLabel.setVisible(false);
        subjectDate.setVisible(false);
        subjectTime.setVisible(false);
        extendedOn = false;
        buttonsVisible = true;
        setAcceptDrops(true);

        browser = new ContentView(this);

        browser.page().setLinkDelegationPolicy(QWebPage.LinkDelegationPolicy.DelegateAllLinks);
        browser.linkClicked.connect(this, "linkClicked(QUrl)");
        currentHyperlink = "";

        //Setup the source editor
        sourceEdit = new QTextEdit(this);
        sourceEdit.setVisible(false);
        sourceEdit.setTabChangesFocus(true);
        sourceEdit.setLineWrapMode(LineWrapMode.NoWrap);
        QFont font = new QFont();
        font.setFamily("Courier");
        font.setFixedPitch(true);
        font.setPointSize(10);
        sourceEdit.setFont(font);
        syntaxHighlighter = new Highlighter(sourceEdit.document());
        sourceEdit.textChanged.connect(this, "sourceEdited()");

        QVBoxLayout v = new QVBoxLayout();
        QFormLayout notebookLayout = new QFormLayout();
        QGridLayout dateLayout = new QGridLayout();
        titleLabel.setReadOnly(false);
        titleLabel.editingFinished.connect(this, "titleEdited()");
        browser.page().contentsChanged.connect(this, "contentChanged()");
        browser.page().selectionChanged.connect(this, "selectionChanged()");
        browser.page().mainFrame().javaScriptWindowObjectCleared.connect(this, "exposeToJavascript()");

        notebookBox.activated.connect(this, "notebookChanged()");
        resourceSignal = new NoteResourceSignal();

        QHBoxLayout tagLayout = new QHBoxLayout();
        v.addWidget(titleLabel, 0);
        notebookLayout.addRow(notebookLabel, notebookBox);
        tagLayout.addLayout(notebookLayout, 0);
        tagLayout.stretch(4);
        tagLayout.addWidget(tagLabel, 0);
        tagLayout.addWidget(tagEdit, 1);
        v.addLayout(tagLayout);

        QHBoxLayout urlLayout = new QHBoxLayout();
        urlLayout.addWidget(urlLabel, 0);
        urlLayout.addWidget(urlText, 0);
        v.addLayout(urlLayout);

        QHBoxLayout authorLayout = new QHBoxLayout();
        authorLayout.addWidget(authorLabel, 0);
        authorLayout.addWidget(authorText, 0);
        authorLayout.addWidget(geoBox);
        v.addLayout(authorLayout);

        dateLayout.addWidget(createdLabel, 0, 0);
        dateLayout.addWidget(createdDate, 0, 1);
        dateLayout.addWidget(createdTime, 0, 2);
        dateLayout.setColumnStretch(9, 100);
        dateLayout.addWidget(alteredLabel, 0, 3);
        dateLayout.addWidget(alteredDate, 0, 4);
        dateLayout.addWidget(alteredTime, 0, 5);
        dateLayout.addWidget(subjectLabel, 0, 6);
        dateLayout.addWidget(subjectDate, 0, 7);
        dateLayout.addWidget(subjectTime, 0, 8);
        v.addLayout(dateLayout, 0);

        undoButton = newEditorButton("undo", tr("Undo Change"));
        redoButton = newEditorButton("redo", tr("Redo Change"));
        cutButton = newEditorButton("cut", tr("Cut"));
        copyButton = newEditorButton("copy", tr("Copy"));
        pasteButton = newEditorButton("paste", tr("Paste"));
        boldButton = newEditorButton("bold", tr("Bold"));
        underlineButton = newEditorButton("underline", tr("Underline"));
        italicButton = newEditorButton("italic", tr("Italic"));

        rightAlignButton = newEditorButton("justifyRight", tr("Right Align"));
        leftAlignButton = newEditorButton("justifyLeft", tr("Left Align"));
        centerAlignButton = newEditorButton("justifyCenter", tr("Center Align"));

        strikethroughButton = newEditorButton("strikethrough", tr("Strikethrough"));
        hlineButton = newEditorButton("hline", tr("Insert Horizontal Line"));
        indentButton = newEditorButton("indent", tr("Shift Right"));
        outdentButton = newEditorButton("outdent", tr("Shift Left"));
        bulletListButton = newEditorButton("bulletList", tr("Bullet List"));
        numberListButton = newEditorButton("numberList", tr("Number List"));
        spellCheckButton = newEditorButton("spellCheck", tr("Spell Check"));
        todoButton = newEditorButton("todo", tr("To-do"));

        buttonLayout = new EditorButtonBar();
        v.addWidget(buttonLayout);

        undoAction = buttonLayout.addWidget(undoButton);
        buttonLayout.toggleUndoVisible.triggered.connect(this, "toggleUndoVisible(Boolean)");
        redoAction = buttonLayout.addWidget(redoButton);
        buttonLayout.toggleRedoVisible.triggered.connect(this, "toggleRedoVisible(Boolean)");

        buttonLayout.addWidget(newSeparator());
        cutAction = buttonLayout.addWidget(cutButton);
        buttonLayout.toggleCutVisible.triggered.connect(this, "toggleCutVisible(Boolean)");
        copyAction = buttonLayout.addWidget(copyButton);
        buttonLayout.toggleCopyVisible.triggered.connect(this, "toggleCopyVisible(Boolean)");
        pasteAction = buttonLayout.addWidget(pasteButton);
        buttonLayout.togglePasteVisible.triggered.connect(this, "togglePasteVisible(Boolean)");

        buttonLayout.addWidget(newSeparator());
        boldAction = buttonLayout.addWidget(boldButton);
        buttonLayout.toggleBoldVisible.triggered.connect(this, "toggleBoldVisible(Boolean)");
        italicAction = buttonLayout.addWidget(italicButton);
        buttonLayout.toggleItalicVisible.triggered.connect(this, "toggleItalicVisible(Boolean)");
        underlineAction = buttonLayout.addWidget(underlineButton);
        buttonLayout.toggleUnderlineVisible.triggered.connect(this, "toggleUnderlineVisible(Boolean)");
        strikethroughAction = buttonLayout.addWidget(strikethroughButton);
        buttonLayout.toggleStrikethroughVisible.triggered.connect(this, "toggleStrikethroughVisible(Boolean)");

        buttonLayout.addWidget(newSeparator());
        leftAlignAction = buttonLayout.addWidget(leftAlignButton);
        buttonLayout.toggleLeftAlignVisible.triggered.connect(this, "toggleLeftAlignVisible(Boolean)");
        centerAlignAction = buttonLayout.addWidget(centerAlignButton);
        buttonLayout.toggleCenterAlignVisible.triggered.connect(this, "toggleCenterAlignVisible(Boolean)");
        rightAlignAction = buttonLayout.addWidget(rightAlignButton);
        buttonLayout.toggleRightAlignVisible.triggered.connect(this, "toggleRightAlignVisible(Boolean)");

        buttonLayout.addWidget(newSeparator());
        hlineAction = buttonLayout.addWidget(hlineButton);
        buttonLayout.toggleHLineVisible.triggered.connect(this, "toggleHLineVisible(Boolean)");

        indentAction = buttonLayout.addWidget(indentButton);
        buttonLayout.toggleIndentVisible.triggered.connect(this, "toggleIndentVisible(Boolean)");
        outdentAction = buttonLayout.addWidget(outdentButton);
        buttonLayout.toggleOutdentVisible.triggered.connect(this, "toggleOutdentVisible(Boolean)");
        bulletListAction = buttonLayout.addWidget(bulletListButton);
        buttonLayout.toggleBulletListVisible.triggered.connect(this, "toggleBulletListVisible(Boolean)");
        numberListAction = buttonLayout.addWidget(numberListButton);
        buttonLayout.toggleNumberListVisible.triggered.connect(this, "toggleNumberListVisible(Boolean)");

        // Setup the font & font size combo boxes
        buttonLayout.addWidget(newSeparator());
        fontList = new QComboBox();
        fontSize = new QComboBox();
        fontList.setMaximumWidth(130);
        fontSize.setMaximumWidth(45);
        fontSize.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed);
        fontList.setToolTip("Font");
        fontSize.setToolTip("Font Size");
        fontList.setStyleSheet("QComboBox {padding: 3px;} ");
        fontSize.setStyleSheet("QComboBox {padding: 3px;} ");
        fontList.activated.connect(this, "fontChanged(String)");
        fontSize.activated.connect(this, "fontSizeChanged(String)");
        fontListAction = buttonLayout.addWidget(fontList);
        buttonLayout.toggleFontVisible.triggered.connect(this, "toggleFontListVisible(Boolean)");
        fontSizeAction = buttonLayout.addWidget(fontSize);
        buttonLayout.toggleFontSizeVisible.triggered.connect(this, "toggleFontSizeVisible(Boolean)");
        QFontDatabase fonts = new QFontDatabase();
        List<String> fontFamilies = fonts.families();
        for (int i = 0; i < fontFamilies.size(); i++) {
            fontList.addItem(fontFamilies.get(i));
            if (i == 0) {
                loadFontSize(fontFamilies.get(i));
            }
        }

        //      buttonLayout.addWidget(newSeparator(), 0);
        fontColor = newToolButton("fontColor", tr("Font Color"));
        fontColorMenu = new ColorMenu(this);
        fontColor.setMenu(fontColorMenu.getMenu());
        fontColor.setPopupMode(ToolButtonPopupMode.MenuButtonPopup);
        fontColor.setAutoRaise(false);
        fontColorMenu.getMenu().triggered.connect(this, "fontColorClicked()");
        fontColorAction = buttonLayout.addWidget(fontColor);
        buttonLayout.toggleFontColorVisible.triggered.connect(this, "toggleFontColorVisible(Boolean)");
        fontHilight = newToolButton("fontHilight", tr("Font Hilight Color"));
        fontHilight.setPopupMode(ToolButtonPopupMode.MenuButtonPopup);
        fontHilight.setAutoRaise(false);
        fontHilightColorMenu = new ColorMenu(this);
        fontHilightColorMenu.setDefault(QColor.yellow);
        fontHilight.setMenu(fontHilightColorMenu.getMenu());
        fontHilightColorMenu.getMenu().triggered.connect(this, "fontHilightClicked()");
        fontHilightAction = buttonLayout.addWidget(fontHilight);
        fontHilightColorMenu.setDefault(QColor.yellow);
        buttonLayout.toggleFontHilight.triggered.connect(this, "toggleFontHilightVisible(Boolean)");

        spellCheckAction = buttonLayout.addWidget(spellCheckButton);
        buttonLayout.toggleNumberListVisible.triggered.connect(this, "spellCheckClicked()");
        buttonLayout.toggleSpellCheck.triggered.connect(this, "toggleSpellCheckVisible(Boolean)");

        todoAction = buttonLayout.addWidget(todoButton);
        buttonLayout.toggleNumberListVisible.triggered.connect(this, "todoClicked()");
        buttonLayout.toggleTodo.triggered.connect(this, "toggleTodoVisible(Boolean)");

        // Setup the source browser);

        //      buttonLayout.addWidget(new QLabel(), 1);
        QSplitter editSplitter = new QSplitter(this);
        editSplitter.addWidget(browser);
        editSplitter.setOrientation(Qt.Orientation.Vertical);
        editSplitter.addWidget(sourceEdit);

        //      v.addWidget(browser, 1);
        //      v.addWidget(sourceEdit);
        v.addWidget(editSplitter);
        setLayout(v);

        browser.downloadAttachmentRequested.connect(this, "downloadAttachment(QNetworkRequest)");
        browser.downloadImageRequested.connect(this, "downloadImage(QNetworkRequest)");
        setTabOrder(notebookBox, tagEdit);
        setTabOrder(tagEdit, browser);

        focusNoteShortcut = new QShortcut(this);
        setupShortcut(focusNoteShortcut, "Focus_Note");
        focusNoteShortcut.activated.connect(this, "focusNote()");
        focusTitleShortcut = new QShortcut(this);
        setupShortcut(focusTitleShortcut, "Focus_Title");
        focusTitleShortcut.activated.connect(this, "focusTitle()");
        focusTagShortcut = new QShortcut(this);
        setupShortcut(focusTagShortcut, "Focus_Tag");
        focusTagShortcut.activated.connect(this, "focusTag()");
        focusAuthorShortcut = new QShortcut(this);
        setupShortcut(focusAuthorShortcut, "Focus_Author");
        focusAuthorShortcut.activated.connect(this, "focusAuthor()");
        focusUrlShortcut = new QShortcut(this);
        setupShortcut(focusUrlShortcut, "Focus_Url");
        focusUrlShortcut.activated.connect(this, "focusUrl()");

        browser.page().mainFrame().setTextSizeMultiplier(Global.getTextSizeMultiplier());
        browser.page().mainFrame().setZoomFactor(Global.getZoomFactor());

        previewPageList = new HashMap<String, Integer>();

        browser.page().microFocusChanged.connect(this, "microFocusChanged()");

        //Setup colors

        QPalette pal = new QPalette();
        pal.setColor(ColorRole.Text, QColor.black);
        titleLabel.setPalette(pal);
        authorText.setPalette(pal);
        authorLabel.setPalette(pal);
        urlLabel.setPalette(pal);
        urlText.setPalette(pal);
        createdDate.setPalette(pal);
        createdTime.setPalette(pal);
        alteredDate.setPalette(pal);
        alteredTime.setPalette(pal);
        subjectDate.setPalette(pal);
        subjectTime.setPalette(pal);
        tagEdit.setPalette(pal);
        notebookBox.setPalette(pal);

        blockApplication = new Signal1<BrowserWindow>();
        unblockApplication = new Signal0();

        setSourceTimer = new QTimer();
        setSourceTimer.timeout.connect(this, "setSource()");

        logger.log(logger.HIGH, "Browser setup complete");
    }

    private void setupShortcut(QShortcut action, String text) {
        if (!Global.shortcutKeys.containsAction(text))
            return;
        action.setKey(new QKeySequence(Global.shortcutKeys.getShortcut(text)));
    }

    // Getter for the QWebView
    public QWebView getBrowser() {
        return browser;
    }

    // Block signals while loading data or things are flagged as dirty by
    // mistake
    public void loadingData(boolean val) {
        logger.log(logger.EXTREME, "Entering BrowserWindow.loadingData() " + val);
        notebookBox.blockSignals(val);
        browser.page().blockSignals(val);
        browser.page().mainFrame().blockSignals(val);
        titleLabel.blockSignals(val);
        alteredDate.blockSignals(val);
        alteredTime.blockSignals(val);
        createdTime.blockSignals(val);
        createdDate.blockSignals(val);
        subjectDate.blockSignals(val);
        subjectTime.blockSignals(val);
        urlText.blockSignals(val);
        authorText.blockSignals(val);
        if (!val)
            exposeToJavascript();
        logger.log(logger.EXTREME, "Exiting BrowserWindow.loadingData() " + val);
    }

    // Enable/disable
    public void setReadOnly(boolean v) {
        setEnabled(true);
        titleLabel.setEnabled(!v);
        notebookBox.setEnabled(!v);
        tagEdit.setEnabled(!v);
        authorLabel.setEnabled(!v);
        geoBox.setEnabled(!v);
        urlText.setEnabled(!v);
        createdDate.setEnabled(!v);
        subjectDate.setEnabled(!v);
        alteredDate.setEnabled(!v);
        authorText.setEnabled(!v);
        createdTime.setEnabled(!v);
        alteredTime.setEnabled(!v);
        subjectTime.setEnabled(!v);
        getBrowser().setEnabled(true);
        getBrowser().page().setContentEditable(!v);
        //      getBrowser().setEnabled(!v);
    }

    // expose this class to Javascript on the web page
    private void exposeToJavascript() {
        browser.page().mainFrame().addToJavaScriptWindowObject("jambi", this);
    }

    // Custom event queue
    @Override
    public boolean event(QEvent e) {
        if (e.type().equals(QEvent.Type.FocusOut)) {
            logger.log(logger.EXTREME, "Focus lost");
            focusLost.emit();
        }
        return super.event(e);
    }

    // clear out browser
    public void clear() {
        logger.log(logger.EXTREME, "Entering BrowserWindow.clear()");
        setNote(null);
        setContent(new QByteArray());
        tagEdit.setText("");
        tagEdit.tagCompleter.reset();
        urlLabel.setText(tr("Source URL:"));
        titleLabel.setText("");
        logger.log(logger.EXTREME, "Exiting BrowserWindow.clear()");
    }

    public void setContent(QByteArray data) {
        sourceEdit.blockSignals(true);
        browser.setContent(data);
        setSource();
    }

    // get/set current note
    public void setNote(Note n) {
        currentNote = n;
        if (n == null)
            n = new Note();
        saveNoteTitle = n.getTitle();

    }

    public Note getNote() {
        return currentNote;
    }

    // New Editor Button
    private QPushButton newEditorButton(String name, String toolTip) {
        QPushButton button = new QPushButton();
        //      QIcon icon = new QIcon(iconPath + name + ".gif");
        QIcon icon = new QIcon(iconPath + name + ".png");
        button.setIcon(icon);
        button.setIconSize(new QSize(16, 16));

        button.setToolTip(toolTip);
        button.clicked.connect(this, name + "Clicked()");
        button.setStyleSheet("QPushButton {padding: 3px;} ");
        return button;
    }

    // New Editor Button
    private QToolButton newToolButton(String name, String toolTip) {
        QToolButton button = new QToolButton();
        //      QIcon icon = new QIcon(iconPath + name + ".gif");
        QIcon icon = new QIcon(iconPath + name + ".png");
        button.setIcon(icon);
        button.setIconSize(new QSize(16, 16));

        button.setToolTip(toolTip);
        button.clicked.connect(this, name + "Clicked()");
        button.setStyleSheet("QToolButton {padding: 3px;} ");
        button.setMaximumHeight(22);
        return button;
    }

    // New Separator
    private QLabel newSeparator() {
        return new QLabel("");
    }

    // Set the title in the window
    public void setTitle(String t) {
        titleLabel.setText(t);
        saveNoteTitle = t;
        checkNoteTitle();
    }

    // Return the current text title
    public String getTitle() {
        return titleLabel.text();
    }

    // Set the tag name string
    public void setTag(String t) {
        saveTagList = t;
        tagEdit.setText(t);
        tagEdit.tagCompleter.reset();
    }

    // Set the source URL
    public void setUrl(String t) {
        urlLabel.setText(tr("Source URL:\t"));
        urlText.setText(t);
    }

    // The user want's to launch a web browser on the source of the URL
    public void sourceUrlClicked() {
        // Make sure we have a valid URL
        if (urlText.text().trim().equals(""))
            return;

        String url = urlText.text();
        if (!url.toLowerCase().startsWith(tr("http://")))
            url = tr("http://") + url;

        if (!QDesktopServices.openUrl(new QUrl(url))) {
            logger.log(logger.LOW, "Error opening file :" + url);
        }
    }

    public void setAuthor(String t) {
        authorLabel.setText(tr("Author:\t"));
        authorText.setText(t);
    }

    // Set the creation date
    public void setCreation(long date) {
        QDateTime dt = new QDateTime();
        dt.setTime_t((int) (date / 1000));
        createdDate.setDateTime(dt);
        createdTime.setDateTime(dt);
        createdDate.setDisplayFormat(Global.getDateFormat());
        createdTime.setDisplayFormat(Global.getTimeFormat());
    }

    // Set the creation date
    public void setAltered(long date) {
        QDateTime dt = new QDateTime();
        dt.setTime_t((int) (date / 1000));
        alteredDate.setDateTime(dt);
        alteredTime.setDateTime(dt);
        alteredDate.setDisplayFormat(Global.getDateFormat());
        alteredTime.setDisplayFormat(Global.getTimeFormat());
    }

    // Set the subject date
    public void setSubjectDate(long date) {
        QDateTime dt = new QDateTime();
        dt.setTime_t((int) (date / 1000));
        subjectDate.setDateTime(dt);
        subjectTime.setDateTime(dt);
        subjectDate.setDisplayFormat(Global.getDateFormat());
        subjectTime.setDisplayFormat(Global.getTimeFormat());
    }

    // Toggle the extended attribute information
    public void toggleInformation() {
        if (extendedOn) {
            extendedOn = false;
        } else {
            extendedOn = true;
        }
        urlLabel.setVisible(extendedOn);
        urlText.setVisible(extendedOn);
        authorText.setVisible(extendedOn);
        geoBox.setVisible(extendedOn);
        authorLabel.setVisible(extendedOn);
        createdDate.setVisible(extendedOn);
        createdTime.setVisible(extendedOn);
        createdLabel.setVisible(extendedOn);
        alteredLabel.setVisible(extendedOn);
        alteredDate.setVisible(extendedOn);
        alteredTime.setVisible(extendedOn);
        //notebookBox.setVisible(extendedOn);
        notebookLabel.setVisible(extendedOn);
        subjectLabel.setVisible(extendedOn);
        subjectDate.setVisible(extendedOn);
        subjectTime.setVisible(extendedOn);
    }

    public void hideButtons() {

        undoButton.parentWidget().setVisible(false);
        buttonsVisible = false;
    }

    // Is the extended view on?
    public boolean isExtended() {
        return extendedOn;
    }

    // Listener for when a link is clicked
    @SuppressWarnings("unused")
    private void openFile() {
        logger.log(logger.EXTREME, "Starting openFile()");
        File fileHandle = new File(selectedFile);
        URI fileURL = fileHandle.toURI();
        String localURL = fileURL.toString();
        QUrl url = new QUrl(localURL);
        QFile file = new QFile(selectedFile);

        logger.log(logger.EXTREME, "Adding to fileWatcher:" + file.fileName());
        fileWatcher.addPath(file.fileName());

        if (!QDesktopServices.openUrl(url)) {
            logger.log(logger.LOW, "Error opening file :" + url);
        }
    }

    // Listener for when a link is clicked
    @SuppressWarnings("unused")
    private void linkClicked(QUrl url) {
        logger.log(logger.EXTREME, "URL Clicked: " + url.toString());
        if (url.toString().startsWith("latex:")) {
            int position = url.toString().lastIndexOf(".");
            String guid = url.toString().substring(0, position);
            position = guid.lastIndexOf("/");
            guid = guid.substring(position + 1);
            editLatex(guid);
            return;
        }
        if (url.toString().startsWith("evernote:/view/")) {
            StringTokenizer tokens = new StringTokenizer(url.toString().replace("evernote:/view/", ""), "/");
            tokens.nextToken();
            tokens.nextToken();
            String sid = tokens.nextToken();
            String lid = tokens.nextToken();

            // Emit that we want to switch to a new note
            evernoteLinkClicked.emit(sid, lid);

            return;
        }
        if (url.toString().startsWith("nnres://")) {
            logger.log(logger.EXTREME, "URL is NN resource");
            if (url.toString().endsWith("/vnd.evernote.ink")) {
                logger.log(logger.EXTREME, "Unable to open ink note");
                QMessageBox.information(this, tr("Unable Open"), tr("This is an ink note.\n"
                        + "Ink notes are not supported since Evernote has not\n published any specifications on them\n"
                        + "and I'm too lazy to figure them out by myself."));
                return;
            }
            String fullName = url.toString().substring(8);
            int index = fullName.indexOf(".");
            String guid = "";
            String type = "";
            if (index > -1) {
                type = fullName.substring(index + 1);
                guid = fullName.substring(0, index);
            }
            index = guid.indexOf(Global.attachmentNameDelimeter);
            if (index > -1) {
                guid = guid.substring(0, index);
            }
            List<Resource> resList = currentNote.getResources();
            Resource res = null;
            for (int i = 0; i < resList.size(); i++) {
                if (resList.get(i).getGuid().equals(guid)) {
                    res = resList.get(i);
                    i = resList.size();
                }
            }
            if (res == null) {
                String resGuid = Global.resourceMap.get(guid);
                if (resGuid != null)
                    res = conn.getNoteTable().noteResourceTable.getNoteResource(resGuid, true);
            }
            if (res != null) {
                String fileName;
                if (res.getAttributes() != null && res.getAttributes().getFileName() != null
                        && !res.getAttributes().getFileName().trim().equals(""))
                    fileName = res.getGuid() + Global.attachmentNameDelimeter + res.getAttributes().getFileName();
                else
                    fileName = res.getGuid() + "." + type;
                QFile file = new QFile(Global.getFileManager().getResDirPath(fileName));
                QFile.OpenMode mode = new QFile.OpenMode();
                mode.set(QFile.OpenModeFlag.WriteOnly);
                boolean openResult = file.open(mode);
                logger.log(logger.EXTREME, "File opened:" + openResult);
                QDataStream out = new QDataStream(file);
                Resource resBinary = conn.getNoteTable().noteResourceTable.getNoteResource(res.getGuid(), true);
                QByteArray binData = new QByteArray(resBinary.getData().getBody());
                resBinary = null;
                logger.log(logger.EXTREME, "Writing resource");
                out.writeBytes(binData.toByteArray());
                file.close();

                String whichOS = System.getProperty("os.name");
                if (whichOS.contains("Windows"))
                    url.setUrl("file:///" + file.fileName());
                else
                    url.setUrl("file://" + file.fileName());
                //       fileWatcher.removePath(file.fileName());
                logger.log(logger.EXTREME, "Adding file watcher " + file.fileName());
                fileWatcher.addPath(file.fileName());

                // If we can't open it, then prompt the user to save it.
                if (!QDesktopServices.openUrl(url)) {
                    logger.log(logger.EXTREME, "We can't handle this.  Where do we put it?");
                    QFileDialog dialog = new QFileDialog();
                    dialog.show();
                    if (dialog.exec() != 0) {
                        List<String> fileNames = dialog.selectedFiles(); //gets all selected filenames
                        if (fileNames.size() == 0)
                            return;
                        String sf = fileNames.get(0);
                        QFile saveFile = new QFile(sf);
                        mode.set(QFile.OpenModeFlag.WriteOnly);
                        saveFile.open(mode);
                        QDataStream saveOut = new QDataStream(saveFile);
                        saveOut.writeBytes(binData.toByteArray());
                        saveFile.close();
                        return;
                    }
                }
            }
            return;
        }
        logger.log(logger.EXTREME, "Launching URL");
        QDesktopServices.openUrl(url);
    }

    // Listener for when BOLD is clicked
    @SuppressWarnings("unused")
    private void undoClicked() {
        browser.page().triggerAction(WebAction.Undo);
        browser.setFocus();
    }

    // Listener for when BOLD is clicked
    @SuppressWarnings("unused")
    private void redoClicked() {
        browser.page().triggerAction(WebAction.Redo);
        browser.setFocus();
    }

    // Listener for when BOLD is clicked
    @SuppressWarnings("unused")
    private void boldClicked() {
        browser.page().triggerAction(WebAction.ToggleBold);
        microFocusChanged();
        browser.setFocus();
    }

    // Listener for when Italics is clicked
    @SuppressWarnings("unused")
    private void italicClicked() {
        browser.page().triggerAction(WebAction.ToggleItalic);
        microFocusChanged();
        browser.setFocus();
    }

    // Listener for when UNDERLINE is clicked
    @SuppressWarnings("unused")
    private void underlineClicked() {
        browser.page().triggerAction(WebAction.ToggleUnderline);
        microFocusChanged();
        browser.setFocus();
    }

    // Listener for when Strikethrough is clicked
    @SuppressWarnings("unused")
    private void strikethroughClicked() {
        browser.page().mainFrame().evaluateJavaScript("document.execCommand('strikeThrough', false, '');");
        browser.setFocus();
    }

    // Listener for when cut is clicked
    public void cutClicked() {
        cbObserver.setCopySourceGuid(currentNote.getGuid(), browser.page().selectedText());

        browser.page().triggerAction(WebAction.Cut);
        browser.setFocus();
    }

    // Listener when COPY is clicked
    public void copyClicked() {
        cbObserver.setCopySourceGuid(currentNote.getGuid(), browser.page().selectedText());

        browser.page().triggerAction(WebAction.Copy);
        browser.setFocus();
    }

    // Listener when PASTE is clicked
    public void pasteClicked() {
        logger.log(logger.EXTREME, "Paste Clicked");
        if (forceTextPaste) {
            pasteWithoutFormattingClicked();
            return;
        }

        // ???
        String srcGuid = cbObserver.getSourceGuid();
        String dstGuid = currentNote.getGuid();
        if (srcGuid != null && dstGuid != null) {
            if (!srcGuid.equals(dstGuid)) {
                conn.getHistoryTable().addHistory("copy & paste", srcGuid, dstGuid);
            }
        }

        QClipboard clipboard = QApplication.clipboard();
        QMimeData mime = clipboard.mimeData();

        if (mime.hasImage()) {
            logger.log(logger.EXTREME, "Image paste found");
            browser.setFocus();
            insertImage(mime);
            browser.setFocus();
            return;
        }

        if (mime.hasUrls()) {
            logger.log(logger.EXTREME, "URL paste found");
            if (mime.text().startsWith("evernote:")) {
                handleNoteLink(mime);
            } else {
                handleUrls(mime);
                browser.setFocus();
            }
            return;
        }

        String text = mime.html();
        if (text.contains("en-tag") && mime.hasHtml()) {
            logger.log(logger.EXTREME, "Intra-note paste found");
            text = fixInternotePaste(text);
            mime.setHtml(text);
            clipboard.setMimeData(mime);
        }

        logger.log(logger.EXTREME, "Final paste choice encountered");
        browser.page().triggerAction(WebAction.Paste);
        browser.setFocus();

    }

    // Paste text without formatting
    private void pasteWithoutFormattingClicked() {
        logger.log(logger.EXTREME, "Paste without format clipped");
        QClipboard clipboard = QApplication.clipboard();
        QMimeData mime = clipboard.mimeData();
        if (!mime.hasText())
            return;

        // ???
        String srcGuid = cbObserver.getSourceGuid();
        String dstGuid = currentNote.getGuid();
        if (srcGuid != null && dstGuid != null) {
            if (!srcGuid.equals(dstGuid)) {
                conn.getHistoryTable().addHistory("copy & paste", srcGuid, dstGuid);
            }
        }

        String text = mime.text();
        clipboard.clear();
        clipboard.setText(text, Mode.Clipboard);
        browser.page().triggerAction(WebAction.Paste);

        // This is done because pasting into an encryption block
        // can cause multiple cells (which can't happen).  It 
        // just goes through the table, extracts the data, & 
        // puts it back as one table cell.
        if (insideEncryption) {
            String js = new String("function fixEncryption() { " + "   var selObj = window.getSelection();"
                    + "   var selRange = selObj.getRangeAt(0);"
                    + "   var workingNode = window.getSelection().anchorNode;"
                    + "   while(workingNode != null && workingNode.nodeName.toLowerCase() != 'table') { "
                    + "           workingNode = workingNode.parentNode;" + "   } "
                    + "   workingNode.innerHTML = window.jambi.fixEncryptionPaste(workingNode.innerHTML);"
                    + "} fixEncryption();");
            browser.page().mainFrame().evaluateJavaScript(js);
        }
    }

    // This basically removes all the table tags and returns just the contents.
    // This is called by JavaScript to fix encryption pastes.
    public String fixEncryptionPaste(String data) {
        data = data.replace("<tbody>", "");
        data = data.replace("</tbody>", "");
        data = data.replace("<tr>", "");
        data = data.replace("</tr>", "");
        data = data.replace("<td>", "");
        data = data.replace("</td>", "<br>");
        data = data.replace("<br><br>", "<br>");

        return "<tbody><tr><td>" + data + "</td></tr></tbody>";
    }

    // insert date/time
    @SuppressWarnings("unused")
    private void insertDateTime() {
        String fmt = Global.getDateFormat() + " " + Global.getTimeFormat();
        String dateTimeFormat = new String(fmt);
        SimpleDateFormat simple = new SimpleDateFormat(dateTimeFormat);
        Calendar cal = Calendar.getInstance();

        browser.page().mainFrame().evaluateJavaScript(
                "document.execCommand('insertHtml', false, '" + simple.format(cal.getTime()) + "');");

        browser.setFocus();

    }

    // Listener when Left is clicked
    @SuppressWarnings("unused")
    private void justifyLeftClicked() {
        browser.page().mainFrame().evaluateJavaScript("document.execCommand('JustifyLeft', false, '');");
        browser.setFocus();
    }

    // Listener when Center is clicked
    @SuppressWarnings("unused")
    private void justifyCenterClicked() {
        browser.page().mainFrame().evaluateJavaScript("document.execCommand('JustifyCenter', false, '');");
        browser.setFocus();
    }

    // Listener when Left is clicked
    @SuppressWarnings("unused")
    private void justifyRightClicked() {
        browser.page().mainFrame().evaluateJavaScript("document.execCommand('JustifyRight', false, '');");
        browser.setFocus();
    }

    // Listener when HLINE is clicked
    @SuppressWarnings("unused")
    private void hlineClicked() {
        browser.page().mainFrame().evaluateJavaScript("document.execCommand('insertHorizontalRule', false, '');");
        browser.setFocus();
    }

    // Listener when outdent is clicked
    private void outdentClicked() {
        browser.page().mainFrame().evaluateJavaScript("document.execCommand('outdent', false, '');");
        browser.setFocus();
    }

    // Listener when a bullet list is clicked
    @SuppressWarnings("unused")
    private void bulletListClicked() {
        browser.page().mainFrame().evaluateJavaScript("document.execCommand('InsertUnorderedList', false, '');");
        browser.setFocus();
    }

    // Listener when a bullet list is clicked
    @SuppressWarnings("unused")
    private void numberListClicked() {
        browser.page().mainFrame().evaluateJavaScript("document.execCommand('InsertOrderedList', false, '');");
        browser.setFocus();
    }

    // Listener when indent is clicked
    private void indentClicked() {
        browser.page().mainFrame().evaluateJavaScript("document.execCommand('indent', false, '');");
        browser.setFocus();
    }

    // Listener when the font name is changed
    @SuppressWarnings("unused")
    private void fontChanged(String font) {
        browser.page().mainFrame().evaluateJavaScript("document.execCommand('fontName',false,'" + font + "');");
        browser.setFocus();
    }

    // Listener when a font size is changed
    @SuppressWarnings("unused")
    private void fontSizeChanged(String font) {
        String text = browser.selectedText();
        if (text.trim().equalsIgnoreCase(""))
            return;

        String selectedText = browser.selectedText();
        String url = "<span style=\"font-size:" + font + "pt; \">" + selectedText + "</a>";
        String script = "document.execCommand('insertHtml', false, '" + url + "');";
        browser.page().mainFrame().evaluateJavaScript(script);
        /*      browser.page().mainFrame().evaluateJavaScript(
        "document.execCommand('fontSize',false,'"
              + font + "');");
        */
        browser.setFocus();
    }

    // Load the font combo box based upon the font selected
    private void loadFontSize(String name) {
        QFontDatabase db = new QFontDatabase();
        fontSize.clear();
        List<Integer> points = db.pointSizes(name);
        for (int i = 0; i < points.size(); i++) {
            fontSize.addItem(points.get(i).toString());
        }
        /*
        fontSize.addItem("x-small");
        fontSize.addItem("small");
        fontSize.addItem("medium");
        fontSize.addItem("large");
        fontSize.addItem("x-large");
        fontSize.addItem("xx-large");
        fontSize.addItem("xxx-large");
        */
    }

    // Listener when a font size is changed
    @SuppressWarnings("unused")
    private void fontColorClicked() {
        //      QColorDialog dialog = new QColorDialog();
        //      QColor color = QColorDialog.getColor();
        QColor color = fontColorMenu.getColor();
        if (color.isValid())
            browser.page().mainFrame()
                    .evaluateJavaScript("document.execCommand('foreColor',false,'" + color.name() + "');");
        browser.setFocus();
    }

    // Listener for when a background color change is requested
    @SuppressWarnings("unused")
    private void fontHilightClicked() {
        //      QColorDialog dialog = new QColorDialog();
        //      QColor color = QColorDialog.getColor();
        QColor color = fontHilightColorMenu.getColor();
        if (color.isValid())
            browser.page().mainFrame()
                    .evaluateJavaScript("document.execCommand('backColor',false,'" + color.name() + "');");
        browser.setFocus();
    }

    // Listener for when a background color change is requested
    @SuppressWarnings("unused")
    private void superscriptClicked() {
        browser.page().mainFrame().evaluateJavaScript("document.execCommand('superscript');");
        browser.setFocus();
    }

    // Listener for when a background color change is requested
    @SuppressWarnings("unused")
    private void subscriptClicked() {
        browser.page().mainFrame().evaluateJavaScript("document.execCommand('subscript');");
        browser.setFocus();
    }

    // Insert a to-do checkbox
    @SuppressWarnings("unused")
    private void todoClicked() {
        FileNameMap fileNameMap = URLConnection.getFileNameMap();
        String script_start = new String("document.execCommand('insertHtml', false, '");
        String script_end = new String("');");
        String todo = new String(
                "<input TYPE=\"CHECKBOX\" value=\"false\" " + "onMouseOver=\"style.cursor=\\'hand\\'\" "
                        + "onClick=\"value=checked; window.jambi.contentChanged(); \" />");
        browser.page().mainFrame().evaluateJavaScript(script_start + todo + script_end);
        browser.setFocus();
    }

    // Encrypt the selected text
    @SuppressWarnings("unused")
    private void encryptText() {
        String text = browser.selectedText();
        if (text.trim().equalsIgnoreCase(""))
            return;
        text = new String(text.replaceAll("\n", "<br/>"));

        EnCryptDialog dialog = new EnCryptDialog();
        dialog.exec();
        if (!dialog.okPressed()) {
            return;
        }

        EnCrypt crypt = new EnCrypt();
        String encrypted = crypt.encrypt(text, dialog.getPassword().trim(), 64);
        String decrypted = crypt.decrypt(encrypted, dialog.getPassword().trim(), 64);

        if (encrypted.trim().equals("")) {
            QMessageBox.information(this, tr("Error"), tr("Error Encrypting String"));
            return;
        }
        StringBuffer buffer = new StringBuffer(encrypted.length() + 100);
        buffer.append("<img en-tag=\"en-crypt\" cipher=\"RC2\" hint=\"" + dialog.getHint().replace("'", "\\'")
                + "\" length=\"64\" ");
        buffer.append("contentEditable=\"false\" alt=\"");
        buffer.append(encrypted);
        buffer.append("\" src=\"").append(
                FileUtils.toForwardSlashedPath(Global.getFileManager().getImageDirPath("encrypt.png") + "\""));
        Global.cryptCounter++;
        buffer.append(" id=\"crypt" + Global.cryptCounter.toString() + "\"");
        buffer.append(" onMouseOver=\"style.cursor=\\'hand\\'\"");
        buffer.append(" onClick=\"window.jambi.decryptText(\\'crypt" + Global.cryptCounter.toString() + "\\', \\'"
                + encrypted + "\\', \\'" + dialog.getHint().replace("'", "\\&amp;apos;") + "\\');\"");
        buffer.append("style=\"display:block\" />");

        String script_start = new String("document.execCommand('insertHtml', false, '");
        String script_end = new String("');");
        browser.page().mainFrame().evaluateJavaScript(script_start + buffer.toString() + script_end);
    }

    // Insert a Quick hyperlink
    public void insertQuickLink() {
        logger.log(logger.EXTREME, "Inserting link");
        String text = browser.selectedText();
        if (text.trim().equalsIgnoreCase(""))
            return;

        NoteQuickLinkDialog dialog = new NoteQuickLinkDialog(logger, conn, text, cbObserver);

        if (dialog.getResults().size() == 0) {
            QMessageBox.critical(null, tr("No Matches Found"), tr("No matching notes found."));
            return;
        }
        if (dialog.getResults().size() > 1) {
            dialog.exec();
            if (!dialog.okPressed) {
                logger.log(logger.EXTREME, "Insert link canceled");
                return;
            }
        }

        User user = Global.getUserInformation();
        String dUrl = new String("evernote:///view/") + new String(user.getId() + "/" + user.getShardId() + "/"
                + dialog.getSelectedNote() + "/" + dialog.getSelectedNote() + "/ " + "style=\"color:#69aa35\"");

        String url = "<a title=\"" + dUrl + "\" href=" + dUrl + " >" + text + "</a>";
        String script = "document.execCommand('insertHtml', false, '" + url + "');";
        browser.page().mainFrame().evaluateJavaScript(script);
        contentChanged();
    }

    // Insert a hyperlink
    public void insertLink() {
        logger.log(logger.EXTREME, "Inserting link");
        String text = browser.selectedText();
        if (text.trim().equalsIgnoreCase(""))
            return;

        InsertLinkDialog dialog = new InsertLinkDialog(insertHyperlink);
        if (currentHyperlink != null && currentHyperlink != "") {
            dialog.setUrl(currentHyperlink);
        }
        dialog.exec();
        if (!dialog.okPressed()) {
            logger.log(logger.EXTREME, "Insert link canceled");
            return;
        }

        // Take care of inserting new links
        if (insertHyperlink) {
            String selectedText = browser.selectedText();
            if (dialog.getUrl().trim().equals(""))
                return;
            logger.log(logger.EXTREME, "Inserting link on text " + selectedText);
            logger.log(logger.EXTREME, "URL Link " + dialog.getUrl().trim());
            String dUrl = StringUtils.replace(dialog.getUrl().trim(), "'", "\\'");
            String url = "<a href=\"" + dUrl + "\" title=" + dUrl + " >" + selectedText + "</a>";
            String script = "document.execCommand('insertHtml', false, '" + url + "');";
            browser.page().mainFrame().evaluateJavaScript(script);
            return;
        }

        // Edit existing links
        String js = new String("function getCursorPos() {" + "var cursorPos;" + "if (window.getSelection) {"
                + "   var selObj = window.getSelection();" + "   var selRange = selObj.getRangeAt(0);"
                + "   var workingNode = window.getSelection().anchorNode.parentNode;"
                + "   while(workingNode != null) { "
                + "      if (workingNode.nodeName.toLowerCase()=='a') workingNode.setAttribute('href','"
                + dialog.getUrl() + "');" + "      workingNode = workingNode.parentNode;" + "   }" + "}"
                + "} getCursorPos();");
        browser.page().mainFrame().evaluateJavaScript(js);

        if (!dialog.getUrl().trim().equals("")) {
            contentChanged();
            return;
        }

        // Remove URL
        js = new String("function getCursorPos() {" + "var cursorPos;" + "if (window.getSelection) {"
                + "   var selObj = window.getSelection();" + "   var selRange = selObj.getRangeAt(0);"
                + "   var workingNode = window.getSelection().anchorNode.parentNode;"
                + "   while(workingNode != null) { " + "      if (workingNode.nodeName.toLowerCase()=='a') { "
                + "         workingNode.removeAttribute('href');" + "         workingNode.removeAttribute('title');"
                + "         var text = document.createTextNode(workingNode.innerText);"
                + "         workingNode.parentNode.insertBefore(text, workingNode);"
                + "         workingNode.parentNode.removeChild(workingNode);" + "      }"
                + "      workingNode = workingNode.parentNode;" + "   }" + "}" + "} getCursorPos();");
        browser.page().mainFrame().evaluateJavaScript(js);

        contentChanged();

    }

    // Insert a hyperlink
    public void insertLatex() {
        editLatex(null);
    }

    public void editLatex(String guid) {
        logger.log(logger.EXTREME, "Inserting latex");
        String text = browser.selectedText();
        if (text.trim().equalsIgnoreCase("\n") || text.trim().equalsIgnoreCase("")) {
            InsertLatexImage dialog = new InsertLatexImage();
            if (guid != null) {
                String formula = conn.getNoteTable().noteResourceTable.getNoteSourceUrl(guid)
                        .replace("http://latex.codecogs.com/gif.latex?", "");
                dialog.setFormula(formula);
            }
            dialog.exec();
            if (!dialog.okPressed()) {
                logger.log(logger.EXTREME, "Edit LaTex canceled");
                return;
            }
            text = dialog.getFormula().trim();
        }
        blockApplication.emit(this);
        logger.log(logger.EXTREME, "Inserting LaTeX formula:" + text);
        latexGuid = guid;
        text = StringUtils.replace(text, "'", "\\'");
        String url = "http://latex.codecogs.com/gif.latex?" + text;
        logger.log(logger.EXTREME, "Sending request to codecogs --> " + url);
        QNetworkAccessManager manager = new QNetworkAccessManager(this);
        manager.finished.connect(this, "insertLatexImageReady(QNetworkReply)");
        unblockTime = new GregorianCalendar().getTimeInMillis() + 5000;
        awaitingHttpResponse = true;
        manager.get(new QNetworkRequest(new QUrl(url)));
    }

    public void insertLatexImageReady(QNetworkReply reply) {
        logger.log(logger.EXTREME, "Response received from CodeCogs");
        if (reply.error() != NetworkError.NoError)
            return;

        unblockTime = -1;
        if (!awaitingHttpResponse)
            return;

        awaitingHttpResponse = false;
        QUrl replyUrl = reply.url();
        QByteArray image = reply.readAll();
        reply.close();
        logger.log(logger.EXTREME, "New image size: " + image.size());

        Resource newRes = null;
        QFile tfile;
        String path;
        if (latexGuid == null) {
            logger.log(logger.EXTREME, "Creating temporary gif");
            path = Global.getFileManager().getResDirPath("latex-temp.gif");
            tfile = new QFile(path);
            tfile.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.WriteOnly));
            logger.log(logger.EXTREME, "File Open: " + tfile.errorString());
            tfile.write(image);
            logger.log(logger.EXTREME, "Bytes writtes: " + tfile.size());
            tfile.close();
            logger.log(logger.EXTREME, "Creating resource");
            int sequence = 0;
            if (currentNote.getResources() != null || currentNote.getResources().size() > 0)
                sequence = currentNote.getResources().size();
            newRes = createResource(path, sequence, "image/gif", false);
            QImage pix = new QImage();
            pix.loadFromData(image);
            newRes.setHeight(new Integer(pix.height()).shortValue());
            newRes.setWidth(new Integer(pix.width()).shortValue());
            logger.log(logger.EXTREME, "Renaming temporary file to " + newRes.getGuid() + ".gif");
            path = Global.getFileManager().getResDirPath(newRes.getGuid() + ".gif");
            tfile.rename(path);
        } else {
            newRes = conn.getNoteTable().noteResourceTable.getNoteResource(latexGuid, false);
            path = Global.getFileManager().getResDirPath(newRes.getGuid() + ".gif");
            tfile = new QFile(path);
            tfile.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.WriteOnly));
            tfile.write(image);
            tfile.close();
            newRes.getData().setBody(image.toByteArray());
            // Calculate the new hash value
            MessageDigest md;

            logger.log(logger.EXTREME, "Generating MD5");
            try {
                md = MessageDigest.getInstance("MD5");
                md.update(image.toByteArray());
                byte[] hash = md.digest();
                newRes.getData().setBodyHash(hash);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
            QImage pix = new QImage();
            pix.loadFromData(image);
            newRes.setHeight(new Integer(pix.height()).shortValue());
            newRes.setWidth(new Integer(pix.width()).shortValue());
            conn.getNoteTable().noteResourceTable.updateNoteResource(newRes, true);
        }

        logger.log(logger.EXTREME, "Setting source: " + replyUrl.toString());
        newRes.getAttributes().setSourceURL(replyUrl.toString());
        conn.getNoteTable().noteResourceTable.updateNoteSourceUrl(newRes.getGuid(), replyUrl.toString(), true);

        for (int i = 0; i < currentNote.getResourcesSize(); i++) {
            if (currentNote.getResources().get(i).getGuid().equals(newRes.getGuid())) {
                currentNote.getResources().remove(i);
                i = currentNote.getResourcesSize();
            }
        }
        currentNote.getResources().add(newRes);

        // do the actual insert into the note.  We only do this on new formulas.  
        if (latexGuid == null) {
            StringBuffer buffer = new StringBuffer(100);
            String formula = replyUrl.toString().toLowerCase().replace("http://latex.codecogs.com/gif.latex?", "");
            buffer.append("<a href=\"latex://" + path.replace("\\", "/") + "\" title=\"" + formula + "\""
                    + "><img src=\"");
            buffer.append(path.replace("\\", "/"));
            buffer.append("\" en-tag=\"en-latex\" type=\"image/gif\"" + " hash=\""
                    + Global.byteArrayToHexString(newRes.getData().getBodyHash()) + "\"" + " guid=\""
                    + newRes.getGuid() + "\"" + " /></a>");

            String script_start = new String("document.execCommand('insertHTML', false, '");
            String script_end = new String("');");
            browser.page().mainFrame().evaluateJavaScript(script_start + buffer + script_end);
        } else {
            HtmlTagModifier modifier = new HtmlTagModifier(getContent());
            modifier.modifyLatexTagHash(newRes);
            String newContent = modifier.getHtml();
            setContent(new QByteArray(newContent));
        }

        logger.log(logger.EXTREME, "New HTML set\n" + browser.page().currentFrame().toHtml());
        QWebSettings.setMaximumPagesInCache(0);
        QWebSettings.setObjectCacheCapacities(0, 0, 0);

        browser.page().mainFrame().setHtml(browser.page().mainFrame().toHtml());
        browser.reload();
        contentChanged();
        //      resourceSignal.contentChanged.emit(path);
        unblockTime = -1;
        unblockApplication.emit();
        return;

    }

    // Insert a table
    public void insertTable() {
        TableDialog dialog = new TableDialog();
        dialog.exec();
        if (!dialog.okPressed()) {
            return;
        }

        int cols = dialog.getCols();
        int rows = dialog.getRows();
        int width = dialog.getWidth();
        boolean percent = dialog.isPercent();

        String newHTML = "<table border=\"1\" width=\"" + new Integer(width).toString();
        if (percent)
            newHTML = newHTML + "%";
        newHTML = newHTML + "\"><tbody>";

        for (int i = 0; i < rows; i++) {
            newHTML = newHTML + "<tr>";
            for (int j = 0; j < cols; j++) {
                newHTML = newHTML + "<td>&nbsp;</td>";
            }
            newHTML = newHTML + "</tr>";
        }
        newHTML = newHTML + "</tbody></table>";

        String script = "document.execCommand('insertHtml', false, '" + newHTML + "');";
        browser.page().mainFrame().evaluateJavaScript(script);
    }

    // Text content changed
    @SuppressWarnings("unused")
    private void selectionChanged() {
        browser.encryptAction.setEnabled(true);
        browser.insertLinkAction.setEnabled(true);
        browser.insertQuickLinkAction.setEnabled(true);
        String scriptStart = "var selection_text = (window.getSelection()).toString();"
                + "var range = (window.getSelection()).getRangeAt(0);"
                + "var parent_html = range.commonAncestorContainer.innerHTML;"
                + "if (parent_html == undefined) {window.jambi.saveSelectedText(selection_text); return;}"
                + "var first_text = range.startContainer.nodeValue.substr(range.startOffset);"
                + "var last_text = (range.endContainer.nodeValue).substring(0,range.endOffset);"
                + "var start = parent_html.indexOf(first_text);"
                + "var end = parent_html.indexOf(last_text,start+1)+last_text.length;"
                + "var value = parent_html.substring(start,end);" + "window.jambi.saveSelectedText(value);";
        browser.page().mainFrame().evaluateJavaScript(scriptStart);

    }

    public void saveSelectedText(String text) {
        boolean enabled = true;
        if (text.trim().length() == 0)
            enabled = false;
        if (text.indexOf("en-tag=\"en-crypt\"") >= 0)
            enabled = false;
        if (text.indexOf("<img en-tag=\"en-media\"") >= 0)
            enabled = false;
        if (text.indexOf("<a en-tag=\"en-media\"") >= 0)
            enabled = false;
        if (text.indexOf("<input ") >= 0)
            enabled = false;

        browser.encryptAction.setEnabled(enabled);
        browser.insertLinkAction.setEnabled(enabled);
        browser.insertQuickLinkAction.setEnabled(enabled);
        //      selectedText = text;
    }

    // Decrypt clicked text
    public void decryptText(String id, String text, String hint) {
        EnCrypt crypt = new EnCrypt();
        String plainText = null;
        Calendar currentTime = new GregorianCalendar();
        Long l = new Long(currentTime.getTimeInMillis());
        String slot = new String(Long.toString(l));

        // First, try to decrypt with any keys we already have
        for (int i = 0; i < Global.passwordRemember.size(); i++) {
            plainText = crypt.decrypt(text, Global.passwordRemember.get(i).getFirst(), 64);
            if (plainText != null) {
                slot = new String(Long.toString(l));
                Global.passwordSafe.put(slot, Global.passwordRemember.get(i));
                removeEncryption(id, plainText, false, slot);
                return;
            }
        }

        EnDecryptDialog dialog = new EnDecryptDialog();
        dialog.setHint(hint);
        while (plainText == null || !dialog.okPressed()) {
            dialog.exec();
            if (!dialog.okPressed()) {
                return;
            }
            plainText = crypt.decrypt(text, dialog.getPassword().trim(), 64);
            if (plainText == null) {
                QMessageBox.warning(this, tr("Incorrect Password"), tr("The password entered is not correct"));
            }
        }
        Pair<String, String> passwordPair = new Pair<String, String>();
        passwordPair.setFirst(dialog.getPassword());
        passwordPair.setSecond(dialog.getHint());
        Global.passwordSafe.put(slot, passwordPair);
        //      removeEncryption(id, plainText.replaceAll("\n", "<br/>"), dialog.permanentlyDecrypt(), slot);
        removeEncryption(id, plainText, dialog.permanentlyDecrypt(), slot);
        if (dialog.rememberPassword()) {
            Pair<String, String> pair = new Pair<String, String>();
            pair.setFirst(dialog.getPassword());
            pair.setSecond(dialog.getHint());
            Global.passwordRemember.add(pair);
        }

    }

    // Get the editor tag line
    public TagLineEdit getTagLine() {
        return tagEdit;
    }

    // Modify a note's tags
    @SuppressWarnings("unused")
    private void modifyTags() {
        TagAssign tagWindow = new TagAssign(allTags, currentTags,
                !conn.getNotebookTable().isLinked(currentNote.getNotebookGuid()));
        tagWindow.exec();
        if (tagWindow.okClicked()) {
            currentTags.clear();
            StringBuffer tagDisplay = new StringBuffer();

            List<QListWidgetItem> newTags = tagWindow.getTagList().selectedItems();
            for (int i = 0; i < newTags.size(); i++) {
                currentTags.add(newTags.get(i).text());
                tagDisplay.append(newTags.get(i).text());
                if (i < newTags.size() - 1) {
                    tagDisplay.append(Global.tagDelimeter + " ");
                }
            }
            tagEdit.setText(tagDisplay.toString());
            noteSignal.tagsChanged.emit(currentNote.getGuid(), currentTags);
        }
    }

    // Tag line has been modified by typing text
    @SuppressWarnings("unused")
    private void modifyTagsTyping() {
        String completionText = "";
        if (tagEdit.currentCompleterSelection != null && !tagEdit.currentCompleterSelection.equals("")) {
            completionText = tagEdit.currentCompleterSelection;
            tagEdit.currentCompleterSelection = "";
        }

        if (tagEdit.text().equalsIgnoreCase(saveTagList))
            return;

        if (saveTagList == null) {
            return;
        }

        // We know something has changed...
        String oldTagArray[] = saveTagList.split(Global.tagDelimeter);
        String newTagArray[];
        if (!completionText.equals("")) {
            String before = tagEdit.text().substring(0, tagEdit.cursorPosition());
            int lastDelimiter = before.lastIndexOf(Global.tagDelimeter);
            if (lastDelimiter > 0)
                before = before.substring(0, before.lastIndexOf(Global.tagDelimeter));
            else
                before = "";
            String after = tagEdit.text().substring(tagEdit.cursorPosition());
            newTagArray = (before + Global.tagDelimeter + completionText + Global.tagDelimeter + after)
                    .split(Global.tagDelimeter);
        } else {
            newTagArray = tagEdit.text().split(Global.tagDelimeter);
        }

        // Remove any traling or leading blanks
        for (int i = 0; i < newTagArray.length; i++)
            newTagArray[i] = newTagArray[i].trim().replaceAll("^\\s+", "");
        ;

        // Remove any potential duplicates from the new list
        for (int i = 0; i < newTagArray.length; i++) {
            boolean foundOnce = false;
            for (int j = 0; j < newTagArray.length; j++) {
                if (newTagArray[j].equalsIgnoreCase(newTagArray[i])) {
                    if (!foundOnce) {
                        foundOnce = true;
                    } else
                        newTagArray[j] = "";
                }
            }
        }

        List<String> newTagList = new ArrayList<String>();
        List<String> oldTagList = new ArrayList<String>();

        for (int i = 0; i < oldTagArray.length; i++)
            if (!oldTagArray[i].trim().equals(""))
                oldTagList.add(oldTagArray[i]);
        for (int i = 0; i < newTagArray.length; i++)
            if (!newTagArray[i].trim().equals(""))
                newTagList.add(newTagArray[i]);

        if (conn.getNotebookTable().isLinked(currentNote.getNotebookGuid())) {
            for (int i = newTagList.size() - 1; i >= 0; i--) {
                boolean found = false;
                for (int j = 0; j < allTags.size(); j++) {
                    if (allTags.get(j).getName().equalsIgnoreCase(newTagList.get(i))) {
                        found = true;
                        j = allTags.size();
                    }
                }
                if (!found)
                    newTagList.remove(i);
            }
        }

        // Let's cleanup the appearance of the tag list
        Collections.sort(newTagList);
        String newDisplay = "";
        for (int i = 0; i < newTagList.size(); i++) {
            newDisplay = newDisplay + newTagList.get(i);
            if (i < newTagList.size() - 1)
                newDisplay = newDisplay + Global.tagDelimeter + " ";
        }
        tagEdit.blockSignals(true);
        tagEdit.setText(newDisplay);
        tagEdit.blockSignals(false);

        // We now have lists of the new & old. Remove duplicates. If all
        // are removed from both then nothing has really changed
        for (int i = newTagList.size() - 1; i >= 0; i--) {
            String nTag = newTagList.get(i);
            for (int j = oldTagList.size() - 1; j >= 0; j--) {
                String oTag = oldTagList.get(j);
                if (oTag.equalsIgnoreCase(nTag)) {
                    oldTagList.remove(j);
                    newTagList.remove(i);
                    j = -1;
                }
            }
        }

        if (oldTagList.size() != 0 || newTagList.size() != 0) {
            currentTags.clear();
            newTagArray = tagEdit.text().split(Global.tagDelimeter);
            for (int i = 0; i < newTagArray.length; i++)
                if (!newTagArray[i].trim().equals(""))
                    currentTags.add(newTagArray[i].trim());

            noteSignal.tagsChanged.emit(currentNote.getGuid(), currentTags);
        }

    }

    // Tab button was pressed
    public void tabPressed() {
        if (insideEncryption)
            return;
        if (!insideList && !insideTable) {
            String script_start = new String(
                    "document.execCommand('insertHtml', false, '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;');");
            browser.page().mainFrame().evaluateJavaScript(script_start);
            return;
        }
        if (insideList) {
            indentClicked();
        }
        if (insideTable) {
            String js = new String("function getCursorPosition() { " + "   var selObj = window.getSelection();"
                    + "   var selRange = selObj.getRangeAt(0);"
                    + "   var workingNode = window.getSelection().anchorNode;" + "   var rowCount = 0;"
                    + "   var colCount = 0;"
                    + "   while(workingNode != null && workingNode.nodeName.toLowerCase() != 'table') { "
                    + "      if (workingNode.nodeName.toLowerCase()=='tr') {" + "         rowCount = rowCount+1;"
                    + "      }" + "      if (workingNode.nodeName.toLowerCase() == 'td') {"
                    + "         colCount = colCount+1;" + "      }"
                    + "      if (workingNode.previousSibling != null)"
                    + "          workingNode = workingNode.previousSibling;" + "      else "
                    + "           workingNode = workingNode.parentNode;" + "   }"
                    + "   var nodes = workingNode.getElementsByTagName('tr');" + "   var tableRows = nodes.length;"
                    + "   nodes = nodes[0].getElementsByTagName('td');" + "   var tableColumns = nodes.length;"
                    + "   window.jambi.setTableCursorPositionTab(rowCount, colCount, tableRows, tableColumns);"
                    + "} getCursorPosition();");
            browser.page().mainFrame().evaluateJavaScript(js);
        }
    }

    // If a user presses tab from within a table
    public void setTableCursorPositionTab(int currentRow, int currentCol, int tableRows, int tableColumns) {
        if (tableRows == currentRow && currentCol == tableColumns) {
            insertTableRow();
        }
        KeyboardModifiers modifiers = new KeyboardModifiers(KeyboardModifier.NoModifier);
        QKeyEvent right = new QKeyEvent(Type.KeyPress, Qt.Key.Key_Right.value(), modifiers);
        QKeyEvent end = new QKeyEvent(Type.KeyPress, Qt.Key.Key_End.value(), modifiers);
        QKeyEvent end2 = new QKeyEvent(Type.KeyPress, Qt.Key.Key_End.value(), modifiers);
        getBrowser().focusWidget();
        QCoreApplication.postEvent(getBrowser(), end);
        QCoreApplication.postEvent(getBrowser(), right);
        QCoreApplication.postEvent(getBrowser(), end2);
    }

    public void backtabPressed() {
        if (insideEncryption)
            return;
        if (insideList)
            outdentClicked();
        if (insideTable) {
            String js = new String("function getCursorPosition() { " + "   var selObj = window.getSelection();"
                    + "   var selRange = selObj.getRangeAt(0);"
                    + "   var workingNode = window.getSelection().anchorNode;" + "   var rowCount = 0;"
                    + "   var colCount = 0;"
                    + "   while(workingNode != null && workingNode.nodeName.toLowerCase() != 'table') { "
                    + "      if (workingNode.nodeName.toLowerCase()=='tr') {" + "         rowCount = rowCount+1;"
                    + "      }" + "      if (workingNode.nodeName.toLowerCase() == 'td') {"
                    + "         colCount = colCount+1;" + "      }"
                    + "      if (workingNode.previousSibling != null)"
                    + "          workingNode = workingNode.previousSibling;" + "      else "
                    + "           workingNode = workingNode.parentNode;" + "   }"
                    + "   var nodes = workingNode.getElementsByTagName('tr');" + "   var tableRows = nodes.length;"
                    + "   nodes = nodes[0].getElementsByTagName('td');" + "   var tableColumns = nodes.length;"
                    + "   window.jambi.setTableCursorPositionBackTab(rowCount, colCount, tableRows, tableColumns);"
                    + "} getCursorPosition();");
            browser.page().mainFrame().evaluateJavaScript(js);

        }
    }

    // If a user presses backtab from within a table
    public void setTableCursorPositionBackTab(int currentRow, int currentCol, int tableRows, int tableColumns) {
        if (currentRow == 1 && currentCol == 1) {
            return;
        }
        KeyboardModifiers modifiers = new KeyboardModifiers(KeyboardModifier.NoModifier);
        QKeyEvent left = new QKeyEvent(Type.KeyPress, Qt.Key.Key_Left.value(), modifiers);
        QKeyEvent home = new QKeyEvent(Type.KeyPress, Qt.Key.Key_Home.value(), modifiers);
        getBrowser().focusWidget();
        QCoreApplication.postEvent(getBrowser(), home);
        QCoreApplication.postEvent(getBrowser(), left);
    }

    public void setInsideList() {
        insideList = true;
    }

    // The title has been edited
    @SuppressWarnings("unused")
    private void titleEdited() {
        // If we don't have a good note, or if the current title
        // matches the old title then we don't need to do anything
        if (currentNote == null)
            return;
        if (currentNote.getTitle().trim().equals(titleLabel.text().trim()))
            return;

        // If we have a real change, we need to save it.
        String text = titleLabel.text().trim();
        if (text.equals(""))
            text = tr("Untitled Note");
        noteSignal.titleChanged.emit(currentNote.getGuid(), text);
        currentNote.setTitle(text);
        saveNoteTitle = text;
        checkNoteTitle();
    }

    // Set the list of note tags
    public void setAllTags(List<Tag> l) {
        allTags = l;
        tagEdit.setTagList(l);
    }

    // Setter for the current tags
    public void setCurrentTags(List<String> s) {
        currentTags = s;
    }

    // Save the list of notebooks
    public void setNotebookList(List<Notebook> n) {
        notebookList = n;
        loadNotebookList();
    }

    // Load the notebook list and select the current notebook
    private void loadNotebookList() {
        if (notebookBox.count() != 0)
            notebookBox.clear();
        if (notebookList == null)
            return;

        for (int i = 0; i < notebookList.size(); i++) {
            notebookBox.addItem(notebookList.get(i).getName());
            if (currentNote != null) {
                if (currentNote.getNotebookGuid().equals(notebookList.get(i).getGuid())) {
                    notebookBox.setCurrentIndex(i);
                }
            }
        }
    }

    // Set the notebook for a note
    public void setNotebook(String notebook) {
        currentNote.setNotebookGuid(notebook);
        loadNotebookList();
    }

    // Get the contents of the editor
    public String getContent() {
        return browser.page().currentFrame().toHtml();
    }

    // The note contents have changed
    public void contentChanged() {
        String content = getContent();

        // This puts in a 1/2 second delay
        // before updating the source editor.
        // It improves response when someone is doing
        // frequent updates on a large note.
        // If the source editor isn't visible, then there
        // is no point to doing any of this.
        if (sourceEdit.isVisible()) {
            setSourceTimer.stop();
            setSourceTimer.setInterval(500);
            setSourceTimer.setSingleShot(true);
            setSourceTimer.start();
        }

        checkNoteTitle();
        noteSignal.noteChanged.emit(currentNote.getGuid(), content);
    }

    // The notebook selection has changed
    @SuppressWarnings("unused")
    private void notebookChanged() {
        boolean changed = false;
        String n = notebookBox.currentText();
        for (int i = 0; i < notebookList.size(); i++) {
            if (n.equals(notebookList.get(i).getName())) {
                if (!notebookList.get(i).getGuid().equals(currentNote.getNotebookGuid())) {
                    String guid = conn.getNotebookTable().findNotebookByName(n);
                    if (conn.getNotebookTable().isLinked(guid)) {
                        tagEdit.setText("");
                        noteSignal.tagsChanged.emit(currentNote.getGuid(), new ArrayList<String>());
                        FilterEditorTags t = new FilterEditorTags(conn, logger);
                        setAllTags(t.getValidTags(currentNote));
                    }
                    currentNote.setNotebookGuid(notebookList.get(i).getGuid());
                    changed = true;
                }
                i = notebookList.size();
            }
        }

        // If the notebook changed, signal the update
        if (changed)
            noteSignal.notebookChanged.emit(currentNote.getGuid(), currentNote.getNotebookGuid());
    }

    // Check the note title
    private void checkNoteTitle() {
        String text = browser.page().currentFrame().toPlainText();
        if (saveNoteTitle == null)
            saveNoteTitle = new String();
        text = text.trim();
        if (!saveNoteTitle.trim().equals("") && !saveNoteTitle.trim().equals("Untitled Note"))
            text = saveNoteTitle.trim();
        int newLine = text.indexOf("\n");
        if (newLine > 0)
            text = text.substring(0, newLine);
        if (saveNoteTitle.trim().equals("") || saveNoteTitle.trim().equals("Untitled Note")) {
            if (text.trim().equals(""))
                text = tr("Untitled Note");
            titleLabel.setText(text);
        } else {
            if (text.length() > Constants.EDAM_NOTE_TITLE_LEN_MAX)
                titleLabel.setText(text.substring(0, Constants.EDAM_NOTE_TITLE_LEN_MAX));
            else {
                titleLabel.blockSignals(true);
                if (text.trim().equals(""))
                    titleLabel.setText(tr("Untitled Note"));
                else
                    titleLabel.setText(text);
                titleLabel.blockSignals(false);
            }
        }
        if (currentNote != null && titleLabel != null && !currentNote.getTitle().equals(text))
            noteSignal.titleChanged.emit(currentNote.getGuid(), text);
    }

    // Return the note contents so we can email them
    public String getContentsToEmail() {
        return browser.page().currentFrame().toPlainText().trim();
        /*
         * int body = browser.page().currentFrame().toHtml().indexOf("<body>");
         * String temp = browser.page().currentFrame().toHtml(); if (body == -1)
         * temp = "<html><body><b>Test</b></body></html>"; else temp =
         * "<html>"+temp.substring(body); return temp; // return
         * urlEncode(browser.page().currentFrame().toHtml());
         */
    }

    // Insert an image into the editor
    private void insertImage(QMimeData mime) {
        logger.log(logger.EXTREME, "Entering insertImage");
        QImage img = (QImage) mime.imageData();
        String script_start = new String("document.execCommand('insertHTML', false, '");
        String script_end = new String("');");

        long now = new Date().getTime();
        String path = Global.getFileManager().getResDirPath((new Long(now).toString()) + ".jpg");

        // This block is just a hack to make sure we wait at least 1ms so we
        // don't
        // have collisions on image names
        long i = new Date().getTime();
        while (now == i)
            i = new Date().getTime();

        // Open the file & write the data
        QFile tfile = new QFile(path);
        tfile.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.WriteOnly));
        if (!img.save(tfile)) {
            tfile.close();
            return;
        }
        tfile.close();

        Resource newRes = createResource(QUrl.fromLocalFile(path).toString(), 0, "image/jpeg", false);
        if (newRes == null)
            return;
        currentNote.getResources().add(newRes);

        // do the actual insert into the note
        StringBuffer buffer = new StringBuffer(100);
        buffer.append("<img src=\"");
        buffer.append(tfile.fileName());
        buffer.append("\" en-tag=en-media type=\"image/jpeg\"" + " hash=\""
                + Global.byteArrayToHexString(newRes.getData().getBodyHash()) + "\"" + " guid=\"" + newRes.getGuid()
                + "\"" + " onContextMenu=\"window.jambi.imageContextMenu(&amp." + tfile.fileName() + "&amp.);\""
                + " />");

        browser.page().mainFrame().evaluateJavaScript(script_start + buffer + script_end);

        return;
    }

    // Handle pasting of a note-to-note link
    private void handleNoteLink(QMimeData mime) {
        for (int i = 0; i < mime.urls().size(); i++) {
            StringTokenizer tokens = new StringTokenizer(
                    mime.urls().get(i).toString().replace("evernote:///view/", ""), "/");
            tokens.nextToken();
            tokens.nextToken();
            String sid = tokens.nextToken();
            String lid = tokens.nextToken();

            if (!sid.equals(currentNote.getGuid()) && !lid.equals(currentNote.getGuid())) {

                Note note = conn.getNoteTable().getNote(sid, false, false, false, false, false);
                if (note == null)
                    note = conn.getNoteTable().getNote(lid, false, false, false, false, false);

                if (note == null)
                    return;

                // If we've gotten this far, we have a bunch of values.  We need to build the link.
                StringBuffer url = new StringBuffer(100);
                String script_start = new String("document.execCommand('insertHtml', false, '");
                String script_end = new String("');");

                url.append("<a href=\"" + mime.urls().get(i).toString() + "\" style=\"color:#69aa35\">");
                url.append(note.getTitle());
                url.append("</a>");
                if (mime.urls().size() > 1)
                    url.append("&nbsp;");
                browser.page().mainFrame().evaluateJavaScript(script_start + url + script_end);
            }
        }
    }

    // Handle URLs that are trying to be pasted
    public void handleUrls(QMimeData mime) {
        logger.log(logger.EXTREME, "Starting handleUrls");
        FileNameMap fileNameMap = URLConnection.getFileNameMap();

        List<QUrl> urlList = mime.urls();
        String url = new String();
        String script_start = new String("document.execCommand('createLink', false, '");
        String script_end = new String("');");

        for (int i = 0; i < urlList.size(); i++) {
            url = urlList.get(i).toString();
            // Find out what type of file we have
            String mimeType = fileNameMap.getContentTypeFor(url);

            // If null returned, we need to guess at the file type
            if (mimeType == null)
                mimeType = "application/" + url.substring(url.lastIndexOf(".") + 1);

            // Check if we have an image or some other type of file
            if (url.substring(0, 5).equalsIgnoreCase("file:")
                    && mimeType.substring(0, 5).equalsIgnoreCase("image")) {
                handleLocalImageURLPaste(mime, mimeType);
                return;
            }

            boolean smallEnough = checkFileAttachmentSize(url);
            if (smallEnough && url.substring(0, 5).equalsIgnoreCase("file:")
                    && !mimeType.substring(0, 5).equalsIgnoreCase("image")) {
                handleLocalAttachment(mime, mimeType);
                return;
            }
            browser.page().mainFrame().evaluateJavaScript(script_start + url + script_end);
        }
        return;
    }

    // If a URL being pasted is an image URL, then attach the image
    private void handleLocalImageURLPaste(QMimeData mime, String mimeType) {
        List<QUrl> urlList = mime.urls();
        String url = new String();
        String script_start_image = new String("document.execCommand('insertHtml', false, '");
        String script_end = new String("');");
        StringBuffer buffer;

        // Copy the image over into the resource directory and create a new resource 
        // record for each url pasted
        for (int i = 0; i < urlList.size(); i++) {
            url = urlList.get(i).toString();

            Resource newRes = createResource(url, i, mimeType, false);
            if (newRes == null)
                return;
            currentNote.getResources().add(newRes);
            buffer = new StringBuffer(100);

            // Open the file & write the data
            String fileName = Global.getFileManager().getResDirPath(newRes.getGuid());
            QFile tfile = new QFile(fileName);
            tfile.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.WriteOnly));
            tfile.write(newRes.getData().getBody());
            tfile.close();
            buffer.append(script_start_image);
            buffer.append("<img src=\"" + FileUtils.toForwardSlashedPath(fileName));
            //         if (mimeType.equalsIgnoreCase("image/jpg"))
            //            mimeType = "image/jpeg";
            buffer.append("\" en-tag=\"en-media\" type=\"" + mimeType + "\"" + " hash=\""
                    + Global.byteArrayToHexString(newRes.getData().getBodyHash()) + "\"" + " guid=\""
                    + newRes.getGuid() + "\"" + " onContextMenu=\"window.jambi.imageContextMenu(&apos;"
                    + tfile.fileName() + "&apos;);\"" + " />");
            buffer.append(script_end);
            browser.page().mainFrame().evaluateJavaScript(buffer.toString());
        }
        return;
    }

    // If a URL being pasted is a local file URL, then attach the file
    private void handleLocalAttachment(QMimeData mime, String mimeType) {
        logger.log(logger.EXTREME, "Attaching local file");
        List<QUrl> urlList = mime.urls();
        String script_start = new String("document.execCommand('insertHtml', false, '");
        String script_end = new String("');");
        StringBuffer buffer;

        String[] type = mimeType.split("/");
        String icon = findIcon(type[1]);
        if (icon.equals("attachment.png"))
            icon = findIcon(type[0]);
        buffer = new StringBuffer(100);

        for (int i = 0; i < urlList.size(); i++) {
            String url = urlList.get(i).toString();

            // Start building the HTML
            if (icon.equals("attachment.png"))
                icon = findIcon(url.substring(url.lastIndexOf(".") + 1));
            String imageURL = FileUtils.toFileURLString(Global.getFileManager().getImageDirFile(icon));

            logger.log(logger.EXTREME, "Creating resource ");
            Resource newRes = createResource(url, i, mimeType, true);
            if (newRes == null)
                return;
            logger.log(logger.EXTREME, "New resource size: " + newRes.getData().getSize());
            currentNote.getResources().add(newRes);

            String fileName = newRes.getGuid() + Global.attachmentNameDelimeter
                    + newRes.getAttributes().getFileName();
            // If we have a PDF, we need to setup the preview.
            if (icon.equalsIgnoreCase("pdf.png") && Global.pdfPreview()) {
                logger.log(logger.EXTREME, "Setting up PDF preview");
                if (newRes.getAttributes() != null && newRes.getAttributes().getFileName() != null
                        && !newRes.getAttributes().getFileName().trim().equals(""))
                    fileName = newRes.getGuid() + Global.attachmentNameDelimeter
                            + newRes.getAttributes().getFileName();
                else
                    fileName = newRes.getGuid() + ".pdf";
                QFile file = new QFile(Global.getFileManager().getResDirPath(fileName));
                QFile.OpenMode mode = new QFile.OpenMode();
                mode.set(QFile.OpenModeFlag.WriteOnly);
                file.open(mode);
                QDataStream out = new QDataStream(file);
                //              Resource resBinary = conn.getNoteTable().noteResourceTable.getNoteResource(newRes.getGuid(), true);
                QByteArray binData = new QByteArray(newRes.getData().getBody());
                //            resBinary = null;
                out.writeBytes(binData.toByteArray());
                file.close();

                PDFPreview pdfPreview = new PDFPreview();
                if (pdfPreview.setupPreview(Global.getFileManager().getResDirPath(fileName), "pdf", 0)) {
                    imageURL = file.fileName() + ".png";
                }
            }

            logger.log(logger.EXTREME, "Generating link tags");
            buffer.delete(0, buffer.length());
            buffer.append("<a en-tag=\"en-media\" guid=\"" + newRes.getGuid() + "\" ");
            buffer.append(" onContextMenu=\"window.jambi.imageContextMenu(&apos;")
                    .append(Global.getFileManager().getResDirPath(fileName)).append("&apos;);\" ");
            buffer.append("type=\"" + mimeType + "\" href=\"nnres://" + fileName + "\" hash=\""
                    + Global.byteArrayToHexString(newRes.getData().getBodyHash()) + "\" >");
            buffer.append("<img src=\"" + imageURL + "\" title=\"" + newRes.getAttributes().getFileName());
            buffer.append("\"></img>");
            buffer.append("</a>");
            browser.page().mainFrame().evaluateJavaScript(script_start + buffer.toString() + script_end);
        }
        return;
    }

    private Resource createResource(String url, int sequence, String mime, boolean attachment) {
        logger.log(logger.EXTREME, "Inside create resource");
        QFile resourceFile;
        //These two lines are added to handle odd characters in the name like #.  Without it
        // toLocalFile() chokes and returns the wrong name.
        logger.log(logger.EXTREME, "File URL:" + url);
        String whichOS = System.getProperty("os.name");
        if (whichOS.contains("Windows"))
            url = url.replace("file:///", "");
        else
            url = url.replace("file://", "");
        String urlTest = new QUrl(url).toLocalFile();
        logger.log(logger.EXTREME, "File URL toLocalFile():" + urlTest);
        urlTest = url;
        if (!urlTest.equals(""))
            url = urlTest;
        //      url = url.replace("/", File.separator);
        logger.log(logger.EXTREME, "Reading from file to create resource:" + url);
        resourceFile = new QFile(url);
        resourceFile.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.ReadOnly));
        logger.log(logger.EXTREME, "Error opening file " + url.toString() + ": " + resourceFile.errorString());
        byte[] fileData = resourceFile.readAll().toByteArray();
        resourceFile.close();
        logger.log(logger.EXTREME, "File Length: " + fileData.length);
        if (fileData.length == 0)
            return null;
        MessageDigest md;
        try {
            logger.log(logger.EXTREME, "Generating MD5");
            md = MessageDigest.getInstance("MD5");
            md.update(fileData);
            byte[] hash = md.digest();

            Resource r = new Resource();
            Calendar time = new GregorianCalendar();
            long prevTime = time.getTimeInMillis();
            while (prevTime == time.getTimeInMillis()) {
                time = new GregorianCalendar();
            }
            r.setGuid(time.getTimeInMillis() + new Integer(sequence).toString());
            r.setNoteGuid(currentNote.getGuid());
            r.setMime(mime);
            r.setActive(true);
            r.setUpdateSequenceNum(0);
            r.setWidth((short) 0);
            r.setHeight((short) 0);
            r.setDuration((short) 0);

            Data d = new Data();
            d.setBody(fileData);
            d.setBodyIsSet(true);
            d.setBodyHash(hash);
            d.setBodyHashIsSet(true);
            r.setData(d);
            d.setSize(fileData.length);

            int fileNamePos = url.lastIndexOf(File.separator);
            if (fileNamePos == -1)
                fileNamePos = url.lastIndexOf("/");
            String fileName = url.substring(fileNamePos + 1);
            ResourceAttributes a = new ResourceAttributes();
            a.setAltitude(0);
            a.setAltitudeIsSet(false);
            a.setLongitude(0);
            a.setLongitudeIsSet(false);
            a.setLatitude(0);
            a.setLatitudeIsSet(false);
            a.setCameraMake("");
            a.setCameraMakeIsSet(false);
            a.setCameraModel("");
            a.setCameraModelIsSet(false);
            a.setAttachment(attachment);
            a.setAttachmentIsSet(true);
            a.setClientWillIndex(false);
            a.setClientWillIndexIsSet(true);
            a.setRecoType("");
            a.setRecoTypeIsSet(false);
            a.setSourceURL(url);
            a.setSourceURLIsSet(true);
            a.setTimestamp(0);
            a.setTimestampIsSet(false);
            a.setFileName(fileName);
            a.setFileNameIsSet(true);
            r.setAttributes(a);

            conn.getNoteTable().noteResourceTable.saveNoteResource(r, true);
            logger.log(logger.EXTREME, "Resource created");
            return r;
        } catch (NoSuchAlgorithmException e1) {
            e1.printStackTrace();
        }
        return null;
    }

    // find the appropriate icon for an attachment
    private String findIcon(String appl) {
        appl = appl.toLowerCase();
        File f = Global.getFileManager().getImageDirFile(appl + ".png");
        if (f.exists())
            return appl + ".png";
        return "attachment.png";
    }

    // Check the file attachment to be sure it isn't over 25 mb
    private boolean checkFileAttachmentSize(String url) {
        String fileName = url.substring(8);
        QFile resourceFile = new QFile(fileName);
        resourceFile.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.ReadOnly));
        long size = resourceFile.size();
        resourceFile.close();
        size = size / 1024 / 1024;
        if (size < 50 && Global.isPremium())
            return true;
        if (size < 25)
            return true;

        String error = tr("A file attachment may not exceed 25MB.");
        QMessageBox.information(this, tr("Attachment Size"), error);
        return false;
    }

    @SuppressWarnings("unused")
    private void createdChanged() {
        QDateTime dt = new QDateTime();
        dt.setDate(createdDate.date());
        dt.setTime(createdTime.time());
        noteSignal.createdDateChanged.emit(currentNote.getGuid(), dt);

    }

    @SuppressWarnings("unused")
    private void alteredChanged() {
        QDateTime dt = new QDateTime();
        dt.setDate(alteredDate.date());
        dt.setTime(alteredTime.time());
        noteSignal.alteredDateChanged.emit(currentNote.getGuid(), dt);
    }

    @SuppressWarnings("unused")
    private void subjectDateTimeChanged() {
        QDateTime dt = new QDateTime();
        dt.setDate(subjectDate.date());
        dt.setTime(subjectTime.time());
        noteSignal.subjectDateChanged.emit(currentNote.getGuid(), dt);

    }

    @SuppressWarnings("unused")
    private void sourceUrlChanged() {
        noteSignal.sourceUrlChanged.emit(currentNote.getGuid(), urlText.text());
    }

    @SuppressWarnings("unused")
    private void authorChanged() {
        noteSignal.authorChanged.emit(currentNote.getGuid(), authorText.text());
    }

    @SuppressWarnings("unused")
    private void geoBoxChanged() {
        int index = geoBox.currentIndex();
        geoBox.setCurrentIndex(0);
        if (index == 1) {
            GeoDialog box = new GeoDialog();
            box.setLongitude(currentNote.getAttributes().getLongitude());
            box.setLatitude(currentNote.getAttributes().getLatitude());
            box.setAltitude(currentNote.getAttributes().getAltitude());
            box.exec();
            if (!box.okPressed())
                return;
            double alt = box.getAltitude();
            double lat = box.getLatitude();
            double lon = box.getLongitude();
            if (alt != currentNote.getAttributes().getAltitude()
                    || lon != currentNote.getAttributes().getLongitude()
                    || lat != currentNote.getAttributes().getLatitude()) {
                noteSignal.geoChanged.emit(currentNote.getGuid(), lon, lat, alt);
                currentNote.getAttributes().setAltitude(alt);
                currentNote.getAttributes().setLongitude(lon);
                currentNote.getAttributes().setLatitude(lat);
            }
        }

        if (index == 2) {
            noteSignal.geoChanged.emit(currentNote.getGuid(), 0.0, 0.0, 0.0);
            currentNote.getAttributes().setAltitude(0.0);
            currentNote.getAttributes().setLongitude(0.0);
            currentNote.getAttributes().setLatitude(0.0);
        }

        if (index == 3 || index == 0) {
            QDesktopServices.openUrl(
                    new QUrl("http://maps.google.com/maps?z=6&q=" + currentNote.getAttributes().getLatitude() + ","
                            + currentNote.getAttributes().getLongitude()));
        }
    }

    // ************************************************************
    // * User chose to save an attachment. Pares out the request *
    // * into a guid & file. Save the result. *
    // ************************************************************
    public void downloadAttachment(QNetworkRequest request) {
        String guid;
        QFileDialog fd = new QFileDialog(this);
        fd.setFileMode(FileMode.AnyFile);
        fd.setConfirmOverwrite(true);
        fd.setWindowTitle(tr("Save File"));
        fd.setAcceptMode(AcceptMode.AcceptSave);
        fd.setDirectory(System.getProperty("user.home"));
        String name = request.url().toString();

        int pos = name.lastIndexOf(Global.attachmentNameDelimeter);
        if (pos > -1) {
            guid = name.substring(0, pos).replace("nnres://", "");
            name = name.substring(pos + Global.attachmentNameDelimeter.length());
            fd.selectFile(name);
            pos = name.lastIndexOf('.');
            if (pos > -1) {
                String mimeType = "(*." + name.substring(pos + 1) + ");; All Files (*)";
                fd.setFilter(tr(mimeType));
            }
        } else {
            guid = name;
        }

        // Strip URL prefix and base dir
        guid = guid.replace("nnres://", "")
                .replace(FileUtils.toForwardSlashedPath(Global.getFileManager().getResDirPath()), "");
        guid = guid.replace("file://", "").replace("/", "")
                .replace(FileUtils.toForwardSlashedPath(Global.getFileManager().getResDirPath()), "");

        pos = guid.lastIndexOf('.');
        if (pos > 0)
            guid = guid.substring(0, pos);
        if (fd.exec() != 0 && fd.selectedFiles().size() > 0) {
            name = name.replace('\\', '/');
            Resource resBinary = conn.getNoteTable().noteResourceTable.getNoteResource(guid, true);
            QFile saveFile = new QFile(fd.selectedFiles().get(0));
            QFile.OpenMode mode = new QFile.OpenMode();
            mode.set(QFile.OpenModeFlag.WriteOnly);
            saveFile.open(mode);
            QDataStream saveOut = new QDataStream(saveFile);
            QByteArray binData = new QByteArray(resBinary.getData().getBody());
            saveOut.writeBytes(binData.toByteArray());
            saveFile.close();

        }
    }

    // ************************************************************
    // * User chose to save an attachment. Pares out the request *
    // * into a guid & file. Save the result. --- DONE FROM downloadAttachment now!!!!!   
    // ************************************************************
    public void downloadImage(QNetworkRequest request) {
        QFileDialog fd = new QFileDialog(this);
        fd.setFileMode(FileMode.AnyFile);
        fd.setConfirmOverwrite(true);
        fd.setWindowTitle(tr("Save File"));
        fd.setAcceptMode(AcceptMode.AcceptSave);
        fd.setDirectory(System.getProperty("user.home"));
        String name = request.url().toString();
        name = name.replace("nnres://", "");
        String dPath = FileUtils.toForwardSlashedPath(Global.getFileManager().getResDirPath());
        name = name.replace(dPath, "");
        int pos = name.lastIndexOf('.');
        String guid = name;
        if (pos > -1) {
            String mimeType = "(*." + name.substring(pos + 1) + ");; All Files (*)";
            fd.setFilter(tr(mimeType));
            guid = guid.substring(0, pos);
        }
        pos = name.lastIndexOf(Global.attachmentNameDelimeter);
        if (pos > -1) {
            guid = name.substring(0, pos);
            fd.selectFile(name.substring(pos + Global.attachmentNameDelimeter.length()));
        }
        if (fd.exec() != 0 && fd.selectedFiles().size() > 0) {
            Resource resBinary = conn.getNoteTable().noteResourceTable.getNoteResource(guid, true);
            String fileName = fd.selectedFiles().get(0);
            QFile saveFile = new QFile(fileName);
            QFile.OpenMode mode = new QFile.OpenMode();
            mode.set(QFile.OpenModeFlag.WriteOnly);
            saveFile.open(mode);
            QDataStream saveOut = new QDataStream(saveFile);
            QByteArray binData = new QByteArray(resBinary.getData().getBody());
            saveOut.writeBytes(binData.toByteArray());
            saveFile.close();
        }
    }

    // *************************************************************
    // * decrypt any hidden text.  We could do an XML parse, but 
    // * it is quicker here just to scan for an <img tag & do the fix
    // * the manual way
    // *************************************************************
    private void removeEncryption(String id, String plainText, boolean permanent, String slot) {
        if (!permanent) {
            plainText = " <table class=\"en-crypt-temp\" slot=\"" + slot + "\""
                    + "border=1 width=100%><tbody><tr><td>" + plainText + "</td></tr></tbody></table>";
        }

        String html = browser.page().mainFrame().toHtml();
        String text = html;
        int imagePos = html.indexOf("<img");
        int endPos;
        for (; imagePos > 0;) {
            // Find the end tag
            endPos = text.indexOf(">", imagePos);
            String tag = text.substring(imagePos - 1, endPos);
            if (tag.indexOf("id=\"" + id + "\"") > -1) {
                text = text.substring(0, imagePos) + plainText + text.substring(endPos + 1);
                QTextCodec codec = QTextCodec.codecForName("UTF-8");
                QByteArray unicode = codec.fromUnicode(text);
                setContent(unicode);
                if (permanent)
                    contentChanged();
            }
            imagePos = text.indexOf("<img", imagePos + 1);
        }
    }

    //****************************************************************
    //* Focus shortcuts
    //****************************************************************
    @SuppressWarnings("unused")
    private void focusTitle() {
        titleLabel.setFocus();
    }

    @SuppressWarnings("unused")
    private void focusTag() {
        tagEdit.setFocus();
    }

    @SuppressWarnings("unused")
    private void focusNote() {
        browser.setFocus();
    }

    @SuppressWarnings("unused")
    private void focusAuthor() {
        authorLabel.setFocus();
    }

    @SuppressWarnings("unused")
    private void focusUrl() {
        urlLabel.setFocus();
    }

    //*****************************************************************
    //* Set the document background color
    //*****************************************************************
    public void setBackgroundColor(String color) {
        String js = "function changeBackground(color) {" + "document.body.style.background = color;" + "}"
                + "changeBackground('" + color + "');";
        browser.page().mainFrame().evaluateJavaScript(js);
        contentChanged();
    }

    //****************************************************************
    //* MicroFocus changed
    //****************************************************************
    private void microFocusChanged() {
        boldButton.setDown(false);
        italicButton.setDown(false);
        underlineButton.setDown(false);
        browser.openAction.setEnabled(false);
        browser.downloadAttachment.setEnabled(false);
        browser.downloadImage.setEnabled(false);
        browser.rotateImageLeft.setEnabled(false);
        browser.rotateImageRight.setEnabled(false);
        browser.insertTableAction.setEnabled(true);
        browser.deleteTableColumnAction.setEnabled(false);
        browser.insertTableRowAction.setEnabled(false);
        browser.insertTableColumnAction.setEnabled(false);
        browser.deleteTableRowAction.setEnabled(false);
        browser.insertLinkAction.setText(tr("Insert Hyperlink"));
        insertHyperlink = true;
        browser.insertQuickLinkAction.setEnabled(true);
        currentHyperlink = "";
        insideList = false;
        insideTable = false;
        insideEncryption = false;
        forceTextPaste = false;

        String js = new String("function getCursorPos() {" + "var cursorPos;" + "if (window.getSelection) {"
                + "   var selObj = window.getSelection();" + "   var selRange = selObj.getRangeAt(0);"
                + "   var workingNode = window.getSelection().anchorNode.parentNode;"
                + "   while(workingNode != null) { "
                //         +"      window.jambi.printNode(workingNode.nodeName);"
                + "      if (workingNode.nodeName=='TABLE') { if (workingNode.getAttribute('class').toLowerCase() == 'en-crypt-temp') window.jambi.insideEncryption(); }"
                + "      if (workingNode.nodeName=='B') window.jambi.boldActive();"
                + "      if (workingNode.nodeName=='I') window.jambi.italicActive();"
                + "      if (workingNode.nodeName=='U') window.jambi.underlineActive();"
                + "      if (workingNode.nodeName=='UL') window.jambi.setInsideList();"
                + "      if (workingNode.nodeName=='OL') window.jambi.setInsideList();"
                + "      if (workingNode.nodeName=='LI') window.jambi.setInsideList();"
                + "      if (workingNode.nodeName=='TBODY') window.jambi.setInsideTable();"
                + "      if (workingNode.nodeName=='A') {for(var x = 0; x < workingNode.attributes.length; x++ ) {if (workingNode.attributes[x].nodeName.toLowerCase() == 'href') window.jambi.setInsideLink(workingNode.attributes[x].nodeValue);}}"
                + "      if (workingNode.nodeName=='SPAN') {"
                + "         if (workingNode.getAttribute('style') == 'text-decoration: underline;') window.jambi.underlineActive();"
                + "      }" + "      workingNode = workingNode.parentNode;" + "   }" + "}" + "} getCursorPos();");
        browser.page().mainFrame().evaluateJavaScript(js);
    }

    public void printNode(String n) {
        System.out.println("Node Vaule: " + n);
    }

    public void insideEncryption() {
        insideEncryption = true;
        forceTextPaste();
    }

    //****************************************************************
    //* Insert a table row
    //****************************************************************
    public void insertTableRow() {

        String js = new String("function insertTableRow() {" + "   var selObj = window.getSelection();"
                + "   var selRange = selObj.getRangeAt(0);"
                + "   var workingNode = window.getSelection().anchorNode.parentNode;" + "   var cellCount = 0;"
                + "   while(workingNode != null) { " + "      if (workingNode.nodeName.toLowerCase()=='tr') {"
                + "           row = document.createElement('TR');"
                + "           var nodes = workingNode.getElementsByTagName('td');"
                + "           for (j=0; j<nodes.length; j=j+1) {"
                + "              cell = document.createElement('TD');" + "              cell.innerHTML='&nbsp;';"
                + "              row.appendChild(cell);" + "           }"
                + "           workingNode.parentNode.insertBefore(row,workingNode.nextSibling);"
                + "           return;" + "      }" + "      workingNode = workingNode.parentNode;" + "   }"
                + "} insertTableRow();");
        browser.page().mainFrame().evaluateJavaScript(js);
        contentChanged();
    }

    public void insertTableColumn() {
        String js = new String("function insertTableColumn() {" + "   var selObj = window.getSelection();"
                + "   var selRange = selObj.getRangeAt(0);"
                + "   var workingNode = window.getSelection().anchorNode.parentNode;" + "   var current = 0;"
                + "   while (workingNode.nodeName.toLowerCase() != 'table' && workingNode != null) {"
                + "       if (workingNode.nodeName.toLowerCase() == 'td') {" + "          var td = workingNode;"
                + "          while (td.previousSibling != null) { "
                + "             current = current+1; td = td.previousSibling;" + "          }" + "       }"
                + "       workingNode = workingNode.parentNode; " + "   }" + "   if (workingNode == null) return;"
                + "   for (var i=0; i<workingNode.rows.length; i++) { "
                + "      var cell = workingNode.rows[i].insertCell(current+1); "
                + "      cell.innerHTML = '&nbsp'; " + "   }" + "} insertTableColumn();");
        browser.page().mainFrame().evaluateJavaScript(js);
        contentChanged();
    }

    //****************************************************************
    //* Delete a table row
    //****************************************************************
    public void deleteTableRow() {

        String js = new String("function deleteTableRow() {" + "   var selObj = window.getSelection();"
                + "   var selRange = selObj.getRangeAt(0);"
                + "   var workingNode = window.getSelection().anchorNode.parentNode;" + "   var cellCount = 0;"
                + "   while(workingNode != null) { " + "      if (workingNode.nodeName.toLowerCase()=='tr') {"
                + "           workingNode.parentNode.removeChild(workingNode);" + "           return;" + "      }"
                + "      workingNode = workingNode.parentNode;" + "   }" + "} deleteTableRow();");
        browser.page().mainFrame().evaluateJavaScript(js);
        contentChanged();
    }

    public void deleteTableColumn() {
        String js = new String("function deleteTableColumn() {" + "   var selObj = window.getSelection();"
                + "   var selRange = selObj.getRangeAt(0);"
                + "   var workingNode = window.getSelection().anchorNode.parentNode;" + "   var current = 0;"
                + "   while (workingNode.nodeName.toLowerCase() != 'table' && workingNode != null) {"
                + "       if (workingNode.nodeName.toLowerCase() == 'td') {" + "          var td = workingNode;"
                + "          while (td.previousSibling != null) { "
                + "             current = current+1; td = td.previousSibling;" + "          }" + "       }"
                + "       workingNode = workingNode.parentNode; " + "   }" + "   if (workingNode == null) return;"
                + "   for (var i=0; i<workingNode.rows.length; i++) { "
                + "      workingNode.rows[i].deleteCell(current); " + "   }" + "} deleteTableColumn();");
        browser.page().mainFrame().evaluateJavaScript(js);
        contentChanged();
    }

    public void setInsideTable() {
        browser.insertTableRowAction.setEnabled(true);
        browser.insertTableColumnAction.setEnabled(true);
        browser.deleteTableRowAction.setEnabled(true);
        browser.deleteTableColumnAction.setEnabled(true);
        browser.insertTableAction.setEnabled(false);
        browser.encryptAction.setEnabled(false);
        insideTable = true;
    }

    public void setInsideLink(String link) {
        browser.insertLinkAction.setText(tr("Edit Hyperlink"));
        currentHyperlink = link;
        insertHyperlink = false;
    }

    public void italicActive() {
        italicButton.setDown(true);
    }

    public void boldActive() {
        boldButton.setDown(true);
    }

    public void underlineActive() {
        underlineButton.setDown(true);
    }

    public void forceTextPaste() {
        forceTextPaste = true;
    }

    public void imageContextMenu(String f) {
        browser.downloadImage.setEnabled(true);
        browser.rotateImageRight.setEnabled(true);
        browser.rotateImageLeft.setEnabled(true);
        browser.openAction.setEnabled(true);
        selectedFile = f;
    }

    public void rotateImageRight() {
        QWebSettings.setMaximumPagesInCache(0);
        QWebSettings.setObjectCacheCapacities(0, 0, 0);
        QImage image = new QImage(selectedFile);
        QMatrix matrix = new QMatrix();
        matrix.rotate(90.0);
        image = image.transformed(matrix);
        image.save(selectedFile);
        QWebSettings.setMaximumPagesInCache(0);
        QWebSettings.setObjectCacheCapacities(0, 0, 0);
        browser.setHtml(browser.page().mainFrame().toHtml());
        browser.reload();
        contentChanged();
        resourceSignal.contentChanged.emit(selectedFile);

    }

    public void rotateImageLeft() {
        QImage image = new QImage(selectedFile);
        QMatrix matrix = new QMatrix();
        matrix.rotate(-90.0);
        image = image.transformed(matrix);
        image.save(selectedFile);
        browser.setHtml(browser.page().mainFrame().toHtml());
        browser.reload();
        contentChanged();
        resourceSignal.contentChanged.emit(selectedFile);
    }

    public void resourceContextMenu(String f) {
        browser.downloadAttachment.setEnabled(true);
        browser.openAction.setEnabled(true);
        selectedFile = f;
    }

    public void latexContextMenu(String f) {
        browser.downloadImage.setEnabled(true);
        browser.rotateImageRight.setEnabled(true);
        browser.rotateImageLeft.setEnabled(true);
        browser.openAction.setEnabled(true);
        selectedFile = f;
    }

    //****************************************************************
    //* Apply CSS style to specified word
    //****************************************************************
    /*   public void applyStyleToWords(String word, String style) {
          QFile script = new QFile("D:\\NeverNote\\js\\hilight1.js");
          script.open(OpenModeFlag.ReadOnly);
          String s = script.readAll().toString();
          String js = new String(s +" findit('"+word+"', '"+style+"');");
          browser.page().mainFrame().evaluateJavaScript(js);
          System.out.println(getContent());
       }
    */
    //****************************************************************
    //* Someone tried to paste a resource between notes, so we need  *
    //* to do some special handling.                                 *
    //****************************************************************
    private String fixInternotePaste(String text) {
        logger.log(logger.EXTREME, "Fixing internote paste");
        String returnValue = fixInternotePasteSearch(text, "<img", "src=\"");
        return fixInternotePasteSearch(returnValue, "<a", "href=\"nnres://");
    }

    private String fixInternotePasteSearch(String text, String type, String locTag) {

        // First, let's fix the images.
        int startPos = text.indexOf(type);
        int endPos;
        for (; startPos >= 0;) {
            endPos = text.indexOf(">", startPos + 1);
            String segment = text.substring(startPos, endPos);
            if (segment.indexOf("en-tag") > -1) {
                String newSegment = segment;

                int guidStartPos = segment.indexOf("guid=\"");
                int guidEndPos = segment.indexOf("\"", guidStartPos + 7);
                String guid = segment.substring(guidStartPos + 6, guidEndPos);

                int mimeStartPos = segment.indexOf("type");
                int mimeEndPos = segment.indexOf("\"", mimeStartPos + 7);
                String mime = segment.substring(mimeStartPos + 6, mimeEndPos);

                int srcStartPos = segment.indexOf("src");
                int srcEndPos = segment.indexOf("\"", srcStartPos + 6);
                String src = segment.substring(srcStartPos + 5, srcEndPos);

                Calendar currentTime = new GregorianCalendar();
                Long l = new Long(currentTime.getTimeInMillis());
                long prevTime = l;
                while (l == prevTime) {
                    currentTime = new GregorianCalendar();
                    l = new Long(currentTime.getTimeInMillis());
                }

                Resource r = conn.getNoteTable().noteResourceTable.getNoteResource(guid, true);
                // if r==null, then the image doesn't exist (it was probably cut out of another note, so 
                // we need to recereate it
                if (r == null) {
                    r = createResource(src, 1, mime, false);
                    if (r == null)
                        return "";
                }
                String randint = new String(Long.toString(l));
                String extension = null;
                if (r.getMime() != null) {
                    extension = r.getMime().toLowerCase();
                    if (extension.indexOf("/") > -1)
                        extension = extension.substring(extension.indexOf("/") + 1);
                }
                String newFile = randint;
                if (r.getAttributes().getFileName() != null && r.getAttributes().getFileName() != "")
                    if (!locTag.startsWith("src"))
                        newFile = newFile + Global.attachmentNameDelimeter + r.getAttributes().getFileName();
                r.setNoteGuid(currentNote.getGuid());

                r.setGuid(randint);
                conn.getNoteTable().noteResourceTable.saveNoteResource(r, true);
                QFile f = new QFile(Global.getFileManager().getResDirPath(newFile));
                QByteArray bin = new QByteArray(r.getData().getBody());
                f.open(QFile.OpenModeFlag.WriteOnly);
                f.write(bin);
                f.close();
                newSegment = newSegment.replace("guid=\"" + guid, "guid=\"" + randint);
                currentNote.getResources().add(r);

                int startSrcPos = newSegment.indexOf(locTag);
                int endSrcPos = newSegment.indexOf("\"", startSrcPos + locTag.length() + 1);
                String source;
                if (locTag.startsWith("src")) {
                    source = newSegment.substring(startSrcPos + locTag.length(), endSrcPos);
                    newSegment = newSegment.replace(source,
                            FileUtils.toForwardSlashedPath(Global.getFileManager().getResDirPath(newFile)));
                } else {
                    source = newSegment.substring(startSrcPos + locTag.length(), endSrcPos);
                    newSegment = newSegment.replace(source, newFile);
                }

                text = text.substring(0, startPos) + newSegment + text.substring(endPos);
            }
            startPos = text.indexOf(type, startPos + 1);
        }
        return text;
    }

    public void nextPage(String file) {
        logger.log(logger.EXTREME, "Starting nextPage()");

        Integer pageNumber;
        if (previewPageList.containsKey(file))
            pageNumber = previewPageList.get(file) + 1;
        else
            pageNumber = 2;
        previewPageList.remove(file);
        previewPageList.put(file, pageNumber);
        PDFPreview pdfPreview = new PDFPreview();
        boolean goodPreview = pdfPreview.setupPreview(file, "pdf", pageNumber);
        if (goodPreview) {

            //         String html = getContent();
            QWebSettings.setMaximumPagesInCache(0);
            QWebSettings.setObjectCacheCapacities(0, 0, 0);
            //         browser.setContent(new QByteArray());
            browser.setHtml(browser.page().mainFrame().toHtml());
            browser.reload();
            //         browser.setContent(new QByteArray(html));
            //         browser.triggerPageAction(WebAction.Reload);
            //         pdfMouseOver(selectedFile);
        }
    }

    public void previousPage(String file) {
        logger.log(logger.EXTREME, "Starting previousPage()");

        Integer pageNumber;
        if (previewPageList.containsKey(file))
            pageNumber = previewPageList.get(file) - 1;
        else
            pageNumber = 1;
        previewPageList.remove(file);
        previewPageList.put(file, pageNumber);
        PDFPreview pdfPreview = new PDFPreview();
        boolean goodPreview = pdfPreview.setupPreview(file, "pdf", pageNumber);
        if (goodPreview) {

            //         String html = getContent();
            QWebSettings.setMaximumPagesInCache(0);
            QWebSettings.setObjectCacheCapacities(0, 0, 0);
            browser.setHtml(browser.page().mainFrame().toHtml());
            browser.reload();
            //         browser.setContent(new QByteArray(html));
            //         browser.triggerPageAction(WebAction.Reload);
        }
    }

    /*   public void pdfMouseOver(String name) { 
          int pageNumber;
          if (previewPageList.containsKey(selectedFile))
     pageNumber = previewPageList.get(selectedFile)+1;
          else
     pageNumber = 1;
              
          if (pageNumber <= 1)
     browser.previousPageAction.setEnabled(false);
          else
     browser.previousPageAction.setEnabled(true);
              
          PDFPreview pdf = new PDFPreview();
          int totalPages = pdf.getPageCount(name);
          if (previewPageList.containsKey(selectedFile))
     pageNumber = previewPageList.get(selectedFile)+1;
          else
     pageNumber = 1;
          if (totalPages > pageNumber)
     browser.nextPageAction.setEnabled(true);
          else
     browser.nextPageAction.setEnabled(false);
       }
           
        
       public void pdfMouseOut() { 
    //      browser.nextPageAction.setVisible(false);
    //      browser.previousPageAction.setVisible(false);
       }
    */

    @SuppressWarnings("unused")
    private void toggleUndoVisible(Boolean toggle) {
        undoAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("undo", toggle);
    }

    @SuppressWarnings("unused")
    private void toggleRedoVisible(Boolean toggle) {
        redoAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("redo", toggle);
    }

    @SuppressWarnings("unused")
    private void toggleCutVisible(Boolean toggle) {
        cutAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("cut", toggle);
    }

    @SuppressWarnings("unused")
    private void toggleCopyVisible(Boolean toggle) {
        copyAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("copy", toggle);
    }

    @SuppressWarnings("unused")
    private void togglePasteVisible(Boolean toggle) {
        pasteAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("paste", toggle);
    }

    @SuppressWarnings("unused")
    private void toggleBoldVisible(Boolean toggle) {
        boldAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("bold", toggle);
    }

    @SuppressWarnings("unused")
    private void toggleItalicVisible(Boolean toggle) {
        italicAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("italic", toggle);
    }

    @SuppressWarnings("unused")
    private void toggleUnderlineVisible(Boolean toggle) {
        underlineAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("underline", toggle);
    }

    @SuppressWarnings("unused")
    private void toggleStrikethroughVisible(Boolean toggle) {
        strikethroughAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("strikethrough", toggle);
    }

    @SuppressWarnings("unused")
    private void toggleLeftAlignVisible(Boolean toggle) {
        leftAlignAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("alignLeft", toggle);
    }

    @SuppressWarnings("unused")
    private void toggleRightAlignVisible(Boolean toggle) {
        rightAlignAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("alignRight", toggle);
    }

    @SuppressWarnings("unused")
    private void toggleCenterAlignVisible(Boolean toggle) {
        centerAlignAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("alignCenter", toggle);
    }

    @SuppressWarnings("unused")
    private void toggleHLineVisible(Boolean toggle) {
        hlineAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("hline", toggle);
    }

    @SuppressWarnings("unused")
    private void toggleIndentVisible(Boolean toggle) {
        indentAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("indent", toggle);
    }

    @SuppressWarnings("unused")
    private void toggleTodoVisible(Boolean toggle) {
        todoAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("todo", toggle);
    }

    @SuppressWarnings("unused")
    private void toggleOutdentVisible(Boolean toggle) {
        outdentAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("outdent", toggle);
    }

    @SuppressWarnings("unused")
    private void toggleBulletListVisible(Boolean toggle) {
        bulletListAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("bulletList", toggle);
    }

    @SuppressWarnings("unused")
    private void toggleNumberListVisible(Boolean toggle) {
        numberListAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("numberList", toggle);
    }

    @SuppressWarnings("unused")
    private void toggleFontListVisible(Boolean toggle) {
        fontListAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("font", toggle);
    }

    @SuppressWarnings("unused")
    private void toggleFontColorVisible(Boolean toggle) {
        fontColorAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("fontColor", toggle);
    }

    @SuppressWarnings("unused")
    private void toggleFontSizeVisible(Boolean toggle) {
        fontSizeAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("fontSize", toggle);
    }

    @SuppressWarnings("unused")
    private void toggleFontHilightVisible(Boolean toggle) {
        fontHilightAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("fontHilight", toggle);
    }

    @SuppressWarnings("unused")
    private void toggleSpellCheckVisible(Boolean toggle) {
        spellCheckAction.setVisible(toggle);
        Global.saveEditorButtonsVisible("spellCheck", toggle);
    }

    private void setupDictionary() {
        File wordList = new File(Global.getFileManager().getSpellDirPath() + Locale.getDefault() + ".dic");
        try {
            dictionary = new SpellDictionaryHashMap(wordList);
            spellChecker = new SpellChecker(dictionary);

            File userWordList;
            userWordList = new File(Global.getFileManager().getSpellDirPathUser() + "user.dic");

            // Get the local user spell dictionary
            try {
                userDictionary = new SpellDictionaryHashMap(userWordList);
            } catch (FileNotFoundException e) {
                userWordList.createNewFile();
                userDictionary = new SpellDictionaryHashMap(userWordList);
            } catch (IOException e) {
                userWordList.createNewFile();
                userDictionary = new SpellDictionaryHashMap(userWordList);
            }

            spellListener = new SuggestionListener(this, spellChecker);

            // Add the user dictionary
            spellChecker.addSpellCheckListener(spellListener);
            spellChecker.setUserDictionary(userDictionary);

        } catch (FileNotFoundException e) {
            QMessageBox.critical(this, tr("Spell Check Error"), tr("Dictionary ")
                    + Global.getFileManager().getSpellDirPath() + Locale.getDefault() + tr(".dic was not found."));
        } catch (IOException e) {
            QMessageBox.critical(this, tr("Spell Check Error"), tr("Dictionary ")
                    + Global.getFileManager().getSpellDirPath() + Locale.getDefault() + tr(".dic is invalid."));
        }

    }

    // Invoke spell checker dialog
    @SuppressWarnings("unused")
    private void spellCheckClicked() {

        if (spellChecker == null) {
            setupDictionary();
        }

        // Read user settings
        spellChecker.getConfiguration().setBoolean(Configuration.SPELL_IGNOREDIGITWORDS,
                Global.getSpellSetting(Configuration.SPELL_IGNOREDIGITWORDS));
        spellChecker.getConfiguration().setBoolean(Configuration.SPELL_IGNOREINTERNETADDRESSES,
                Global.getSpellSetting(Configuration.SPELL_IGNOREINTERNETADDRESSES));
        spellChecker.getConfiguration().setBoolean(Configuration.SPELL_IGNOREMIXEDCASE,
                Global.getSpellSetting(Configuration.SPELL_IGNOREMIXEDCASE));
        spellChecker.getConfiguration().setBoolean(Configuration.SPELL_IGNOREUPPERCASE,
                Global.getSpellSetting(Configuration.SPELL_IGNOREUPPERCASE));
        spellChecker.getConfiguration().setBoolean(Configuration.SPELL_IGNORESENTENCECAPITALIZATION,
                Global.getSpellSetting(Configuration.SPELL_IGNORESENTENCECAPITALIZATION));

        spellListener.abortSpellCheck = false;
        spellListener.errorsFound = false;
        String content = getBrowser().page().mainFrame().toPlainText();
        StringWordTokenizer tokenizer = new StringWordTokenizer(content);
        if (!tokenizer.hasMoreWords())
            return;
        getBrowser().page().action(WebAction.MoveToStartOfDocument);

        getBrowser().setFocus();
        boolean found;

        // Move to the start of page
        KeyboardModifiers ctrl = new KeyboardModifiers(KeyboardModifier.ControlModifier.value());
        QKeyEvent home = new QKeyEvent(Type.KeyPress, Key.Key_Home.value(), ctrl);
        browser.keyPressEvent(home);
        getBrowser().setFocus();

        tokenizer = new StringWordTokenizer(content);
        String word;

        while (tokenizer.hasMoreWords()) {
            word = tokenizer.nextWord();
            found = getBrowser().page().findText(word);
            if (found && !spellListener.abortSpellCheck) {
                spellChecker.checkSpelling(new StringWordTokenizer(word));
                getBrowser().setFocus();
            }
        }

        // Go to the end of the document & finish up.
        home = new QKeyEvent(Type.KeyPress, Key.Key_End.value(), ctrl);
        browser.keyPressEvent(home);
        if (!spellListener.errorsFound)
            QMessageBox.information(this, tr("Spell Check Complete"), tr("No Errors Found"));

    }

    // Source edited
    @SuppressWarnings("unused")
    private void sourceEdited() {
        QTextCodec codec = QTextCodec.codecForLocale();
        codec = QTextCodec.codecForName("UTF-8");
        String content = codec.fromUnicode(sourceEdit.toHtml()).toString();
        content = StringEscapeUtils.unescapeHtml4(removeTags(content));
        QByteArray data = new QByteArray(sourceEditHeader + content + "</body></html>");
        getBrowser().setContent(data);
        checkNoteTitle();
        if (currentNote != null && sourceEdit != null)
            noteSignal.noteChanged.emit(currentNote.getGuid(), sourceEdit.toPlainText());
    }

    private void setSource() {
        String text = getContent();
        sourceEdit.blockSignals(true);
        int body = text.indexOf("<body");
        if (body > 0) {
            body = text.indexOf(">", body);
            if (body > 0) {
                sourceEditHeader = text.substring(0, body + 1);
                text = text.substring(body + 1);
            }
        }
        text = text.replace("</body></html>", "");
        sourceEdit.setPlainText(text);
        sourceEdit.setReadOnly(!getBrowser().page().isContentEditable());
        //syntaxHighlighter.rehighlight();
        sourceEdit.blockSignals(false);
    }

    // show/hide view source window
    public void showSource(boolean value) {
        setSource();
        sourceEdit.setVisible(value);
    }

    // Remove HTML tags
    private String removeTags(String text) {
        StringBuffer buffer = new StringBuffer(text);
        boolean inTag = false;
        int bodyPosition = text.indexOf("<body");
        for (int i = buffer.length() - 1; i >= 0; i--) {
            if (buffer.charAt(i) == '>')
                inTag = true;
            if (buffer.charAt(i) == '<')
                inTag = false;
            if (inTag || buffer.charAt(i) == '<' || i < bodyPosition)
                buffer.deleteCharAt(i);
        }

        return buffer.toString();
    }
}