X-Git-Url: http://git.sourceforge.jp/view?p=neighbornote%2FNeighborNote.git;a=blobdiff_plain;f=src%2Fcx%2Ffbn%2Fnevernote%2Fgui%2FBrowserWindow.java;h=9797e08ad950cbf90824dd6ca3addc41120f0310;hp=e64c2c1f732315cd59db2f7634c415a05e176503;hb=e1ec65eeffd0b72923aef21332dc43524c8f3d7e;hpb=537bd85a4416310ec6a15a2ebb99df91edb27143 diff --git a/src/cx/fbn/nevernote/gui/BrowserWindow.java b/src/cx/fbn/nevernote/gui/BrowserWindow.java index e64c2c1..9797e08 100644 --- a/src/cx/fbn/nevernote/gui/BrowserWindow.java +++ b/src/cx/fbn/nevernote/gui/BrowserWindow.java @@ -1,5 +1,5 @@ /* - * This file is part of NeverNote + * This file is part of NixNote * Copyright 2009 Randy Baumgarte * * This file may be licensed under the terms of of the @@ -20,6 +20,8 @@ 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; @@ -33,6 +35,11 @@ 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; @@ -41,19 +48,37 @@ 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.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; @@ -61,23 +86,35 @@ 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.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; @@ -88,15 +125,21 @@ import cx.fbn.nevernote.Global; 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 { @@ -123,17 +166,20 @@ public class BrowserWindow extends QWidget { public final QAction fontSizeAction; private boolean extendedOn; public boolean buttonsVisible; - private final String iconPath = new String("classpath:cx/fbn/nevernote/icons/"); + private final String iconPath; private final ContentView browser; + private final QTextEdit sourceEdit; + private String sourceEditHeader; + Highlighter syntaxHighlighter; private List allTags; private List currentTags; public NoteSignal noteSignal; + public Signal2 evernoteLinkClicked; private List notebookList; private Note currentNote; private String saveNoteTitle; private String saveTagList; private boolean insideList; -// private String selectedText; private final DatabaseConnection conn; private final QCalendarWidget createdCalendarWidget; private final QCalendarWidget alteredCalendarWidget; @@ -177,6 +223,10 @@ public class BrowserWindow extends QWidget { 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; @@ -187,32 +237,91 @@ public class BrowserWindow extends QWidget { public EditorButtonBar buttonLayout; public final QComboBox fontList; public final QAction fontListAction; - public final QPushButton fontColor; + public final QToolButton fontColor; public final QAction fontColorAction; private final ColorMenu fontColorMenu; - public final QPushButton fontHilight; + public final QToolButton fontHilight; public final QAction fontHilightAction; -// public final ColorComboBox fontHilight; private final ColorMenu fontHilightColorMenu; public final QFileSystemWatcher fileWatcher; public int cursorPosition; - private boolean forceTextPaste = false; + 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 previewPageList; + boolean insertHyperlink; + boolean insideTable; + boolean insideEncryption; + public Signal1 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 HashMap previewPageList; + 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 suggestions = event.getSuggestions(); + spellCheckDialog.clearSuggestions(); + if (!suggestions.isEmpty()) { +// spellCheckDialog.setCurrentSuggestion(suggestions.get(0).getWord()); + for (int i=0; i(); titleLabel.setMaxLength(Constants.EDAM_NOTE_TITLE_LEN_MAX); urlText = new QLineEdit(); authorText = new QLineEdit(); @@ -225,7 +334,7 @@ public class BrowserWindow extends QWidget { focusLost = new Signal0(); tagEdit = new TagLineEdit(allTags); - tagLabel = new QLabel("Tags:"); + tagLabel = new QLabel(tr("Tags:")); tagEdit.focusLost.connect(this, "modifyTagsTyping()"); createdCalendarWidget = new QCalendarWidget(); @@ -243,7 +352,7 @@ public class BrowserWindow extends QWidget { alteredDate.setCalendarPopup(true); alteredDate.setCalendarWidget(alteredCalendarWidget); alteredTime = new QTimeEdit(); - alteredLabel = new QLabel("Altered:"); + alteredLabel = new QLabel(tr("Altered:")); alteredDate.dateChanged.connect(this, "alteredChanged()"); alteredTime.timeChanged.connect(this, "alteredChanged()"); @@ -292,11 +401,25 @@ public class BrowserWindow extends QWidget { 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(); @@ -361,10 +484,11 @@ public class BrowserWindow extends QWidget { 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(); -// buttonLayout.setSpacing(0); v.addWidget(buttonLayout); undoAction = buttonLayout.addWidget(undoButton); @@ -434,21 +558,46 @@ public class BrowserWindow extends QWidget { } // buttonLayout.addWidget(newSeparator(), 0); - fontColor = newEditorButton("fontColor", tr("Font Color")); + 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 = newEditorButton("fontHilight", tr("Font Hilight Color")); + 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); - v.addWidget(browser, 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, @@ -477,13 +626,39 @@ public class BrowserWindow extends QWidget { browser.page().mainFrame().setTextSizeMultiplier(Global.getTextSizeMultiplier()); browser.page().mainFrame().setZoomFactor(Global.getZoomFactor()); - previewPageList = new HashMap(); + previewPageList = new HashMap(); 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(); + 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; @@ -531,7 +706,12 @@ public class BrowserWindow extends QWidget { 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().setEnabled(!v); } // expose this class to Javascript on the web page @@ -553,7 +733,7 @@ public class BrowserWindow extends QWidget { public void clear() { logger.log(logger.EXTREME, "Entering BrowserWindow.clear()"); setNote(null); - browser.setContent(new QByteArray()); + setContent(new QByteArray()); tagEdit.setText(""); tagEdit.tagCompleter.reset(); urlLabel.setText(tr("Source URL:")); @@ -561,6 +741,11 @@ public class BrowserWindow extends QWidget { 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; @@ -577,7 +762,18 @@ public class BrowserWindow extends QWidget { // 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 + ".gif"); + QIcon icon = new QIcon(iconPath + name + ".png"); + button.setIcon(icon); + button.setToolTip(toolTip); + button.clicked.connect(this, name + "Clicked()"); + 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.setToolTip(toolTip); button.clicked.connect(this, name + "Clicked()"); @@ -724,7 +920,27 @@ public class BrowserWindow extends QWidget { @SuppressWarnings("unused") private void linkClicked(QUrl url) { logger.log(logger.EXTREME, "URL Clicked: " +url.toString()); - if (url.toString().substring(0,8).equals("nnres://")) { + 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"); @@ -875,7 +1091,7 @@ public class BrowserWindow extends QWidget { } // Listener when PASTE is clicked - void pasteClicked() { + public void pasteClicked() { logger.log(logger.EXTREME, "Paste Clicked"); if (forceTextPaste) { pasteWithoutFormattingClicked(); @@ -888,6 +1104,7 @@ public class BrowserWindow extends QWidget { if (mime.hasImage()) { logger.log(logger.EXTREME, "Image paste found"); + browser.setFocus(); insertImage(mime); browser.setFocus(); return; @@ -895,8 +1112,12 @@ public class BrowserWindow extends QWidget { if (mime.hasUrls()) { logger.log(logger.EXTREME, "URL paste found"); - handleUrls(mime); - browser.setFocus(); + if (!mime.text().startsWith("evernote:")) { + handleNoteLink(mime); + } else { + handleUrls(mime); + browser.setFocus(); + } return; } @@ -922,11 +1143,40 @@ public class BrowserWindow extends QWidget { if (!mime.hasText()) return; String text = mime.text(); - clipboard.setText(text); + clipboard.clear(); + clipboard.setText(text, Mode.Clipboard); browser.page().triggerAction(WebAction.Paste); - QApplication.clipboard().setMimeData(mime); - browser.setFocus(); + // 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("", ""); + data = data.replace("", ""); + data = data.replace("", ""); + data = data.replace("", ""); + data = data.replace("", ""); + data = data.replace("", "
"); + data = data.replace("

", "
"); + + return ""+data+""; } // insert date/time @@ -1100,7 +1350,9 @@ public class BrowserWindow extends QWidget { "document.execCommand('insertHtml', false, '"); String script_end = new String("');"); String todo = new String( - ""); + ""); browser.page().mainFrame().evaluateJavaScript( script_start + todo + script_end); browser.setFocus(); @@ -1112,6 +1364,7 @@ public class BrowserWindow extends QWidget { String text = browser.selectedText(); if (text.trim().equalsIgnoreCase("")) return; + text = new String(text.replaceAll("\n", "
")); EnCryptDialog dialog = new EnCryptDialog(); dialog.exec(); @@ -1121,6 +1374,7 @@ public class BrowserWindow extends QWidget { 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")); @@ -1131,7 +1385,6 @@ public class BrowserWindow extends QWidget { + dialog.getHint().replace("'","\\'") + "\" length=\"64\" "); buffer.append("contentEditable=\"false\" alt=\""); buffer.append(encrypted); - // NFC FIXME: should this be a file URL like in handleLocalAttachment and importAttachment? buffer.append("\" src=\"").append(FileUtils.toForwardSlashedPath(Global.getFileManager().getImageDirPath("encrypt.png") +"\"")); Global.cryptCounter++; buffer.append(" id=\"crypt"+Global.cryptCounter.toString() +"\""); @@ -1147,7 +1400,39 @@ public class BrowserWindow extends QWidget { 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); + 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 = ""+text +""; + 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"); @@ -1155,7 +1440,7 @@ public class BrowserWindow extends QWidget { if (text.trim().equalsIgnoreCase("")) return; - InsertLinkDialog dialog = new InsertLinkDialog(); + InsertLinkDialog dialog = new InsertLinkDialog(insertHyperlink); if (currentHyperlink != null && currentHyperlink != "") { dialog.setUrl(currentHyperlink); } @@ -1164,34 +1449,220 @@ public class BrowserWindow extends QWidget { logger.log(logger.EXTREME, "Insert link canceled"); return; } - if (browser.insertLinkAction.text().equalsIgnoreCase("Insert Hyperlink")) { + + // 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 url = ""+selectedText +""; 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(" ") || 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 { - 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); - contentChanged(); + 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"); + + 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() { @@ -1230,6 +1701,7 @@ public class BrowserWindow extends QWidget { 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;" @@ -1259,6 +1731,7 @@ public class BrowserWindow extends QWidget { browser.encryptAction.setEnabled(enabled); browser.insertLinkAction.setEnabled(enabled); + browser.insertQuickLinkAction.setEnabled(enabled); // selectedText = text; } @@ -1272,7 +1745,7 @@ public class BrowserWindow extends QWidget { // First, try to decrypt with any keys we already have for (int i=0; i passwordPair = new Pair(); + passwordPair.setFirst(dialog.getPassword()); + passwordPair.setSecond(dialog.getHint()); + Global.passwordSafe.put(slot, passwordPair); +// removeEncryption(id, plainText.replaceAll("\n", "
"), dialog.permanentlyDecrypt(), slot); removeEncryption(id, plainText, dialog.permanentlyDecrypt(), slot); - if (dialog.rememberPassword()) - Global.passwordRemember.add(dialog.getPassword()); + if (dialog.rememberPassword()) { + Pair pair = new Pair(); + pair.setFirst(dialog.getPassword()); + pair.setSecond(dialog.getHint()); + Global.passwordRemember.add(pair); + } } @@ -1309,7 +1790,7 @@ public class BrowserWindow extends QWidget { // Modify a note's tags @SuppressWarnings("unused") private void modifyTags() { - TagAssign tagWindow = new TagAssign(allTags, currentTags); + TagAssign tagWindow = new TagAssign(allTags, currentTags, !conn.getNotebookTable().isLinked(currentNote.getNotebookGuid())); tagWindow.exec(); if (tagWindow.okClicked()) { currentTags.clear(); @@ -1343,11 +1824,25 @@ public class BrowserWindow extends QWidget { // We know something has changed... String oldTagArray[] = saveTagList.split(Global.tagDelimeter); - String newTagArray[] = tagEdit.text().split(Global.tagDelimeter); - - if (!completionText.equals("") && newTagArray.length > 0) { - newTagArray[newTagArray.length-1] = completionText; + 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=0; i--) { + boolean found = false; + for (int j=0; j()); + FilterEditorTags t = new FilterEditorTags(conn, logger); + setAllTags(t.getValidTags(currentNote)); + } currentNote.setNotebookGuid(notebookList.get(i).getGuid()); changed = true; } @@ -1516,7 +2144,9 @@ public class BrowserWindow extends QWidget { // Check the note title private void checkNoteTitle() { String text = browser.page().currentFrame().toPlainText(); - if (saveNoteTitle.trim().equals("")) { + if (saveNoteTitle == null) + saveNoteTitle = new String(); + if (saveNoteTitle.trim().equals("") || saveNoteTitle.trim().equals("Untitled Note")) { int newLine = text.indexOf("\n"); if (newLine > 0) { text = text.substring(0, newLine); @@ -1524,17 +2154,19 @@ public class BrowserWindow extends QWidget { text = tr("Untitled Note"); titleLabel.setText(text); } else { - if (text.length() > 20) - titleLabel.setText(text.substring(0, 20)); + 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); } } - noteSignal.titleChanged.emit(currentNote.getGuid(), titleLabel - .text()); + if (currentNote != null && titleLabel != null) + noteSignal.titleChanged.emit(currentNote.getGuid(), titleLabel.text()); } } @@ -1572,7 +2204,10 @@ public class BrowserWindow extends QWidget { // Open the file & write the data QFile tfile = new QFile(path); tfile.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.WriteOnly)); - img.save(tfile); + if (!img.save(tfile)) { + tfile.close(); + return; + } tfile.close(); Resource newRes = createResource(QUrl.fromLocalFile(path).toString(), 0, "image/jpeg", false); @@ -1587,7 +2222,6 @@ public class BrowserWindow extends QWidget { buffer.append("\" en-tag=en-media type=\"image/jpeg\"" +" hash=\""+Global.byteArrayToHexString(newRes.getData().getBodyHash()) +"\"" +" guid=\"" +newRes.getGuid() +"\"" -// +" onContextMenu=\"window.jambi.imageContextMenu('" +tfile.fileName() +"');\"" +" onContextMenu=\"window.jambi.imageContextMenu(&." +tfile.fileName() +"&.);\"" + " />"); @@ -1597,6 +2231,41 @@ public class BrowserWindow extends QWidget { return; } + // Handle pasting of a note-to-note link + private void handleNoteLink(QMimeData mime) { + for (int i=0; i"); + url.append(note.getTitle()); + url.append(""); + if (mime.urls().size() > 1) + url.append(" "); + 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"); @@ -1735,7 +2404,6 @@ public class BrowserWindow extends QWidget { PDFPreview pdfPreview = new PDFPreview(); if (pdfPreview.setupPreview(Global.getFileManager().getResDirPath(fileName), "pdf",0)) { - // NFC TODO: should this be a 'file://' url like the ones above? imageURL = file.fileName() + ".png"; } } @@ -1762,14 +2430,17 @@ public class BrowserWindow extends QWidget { if (!urlTest.equals("")) url = urlTest; url = url.replace("/", File.separator); + logger.log(logger.EXTREME, "Reading from file to create resource"); 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(); 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(); @@ -1827,6 +2498,7 @@ public class BrowserWindow extends QWidget { r.setAttributes(a); conn.getNoteTable().noteResourceTable.saveNoteResource(r, true); + logger.log(logger.EXTREME, "Resource created"); return r; } catch (NoSuchAlgorithmException e1) { e1.printStackTrace(); @@ -1993,6 +2665,8 @@ public class BrowserWindow extends QWidget { // 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) @@ -2017,7 +2691,6 @@ public class BrowserWindow extends QWidget { // * User chose to save an attachment. Pares out the request * // * into a guid & file. Save the result. --- DONE FROM downloadAttachment now!!!!! // ************************************************************ - // NFC TODO: unused? remove public void downloadImage(QNetworkRequest request) { QFileDialog fd = new QFileDialog(this); fd.setFileMode(FileMode.AnyFile); @@ -2064,7 +2737,11 @@ public class BrowserWindow extends QWidget { // ************************************************************* private void removeEncryption(String id, String plainText, boolean permanent, String slot) { if (!permanent) { - plainText = " " +plainText+" "; + plainText = "
" + +plainText+"
"; } String html = browser.page().mainFrame().toHtml(); @@ -2076,10 +2753,12 @@ public class BrowserWindow extends QWidget { 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); - - browser.setContent(new QByteArray(text)); - contentChanged(); + 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(""); + 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(" 0) { + body = text.indexOf(">",body); + if (body > 0) { + sourceEditHeader =text.substring(0, body+1); + text = text.substring(body+1); + } + } + text = text.replace("", ""); + 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("=0; i--) { + if (buffer.charAt(i) == '>') + inTag = true; + if (buffer.charAt(i) == '<') + inTag = false; + if (inTag || buffer.charAt(i) == '<' || i