private static Logger log = Logger.getLogger(NeverNote.class);
private String saveLastPath; // last path we used
private final QTimer messageTimer; // Timer to clear the status message.
+ private long blockTime; // When the app. is blocked, this is the time to unblock it.
String iconPath = new String("classpath:cx/fbn/nevernote/icons/");
browserWindow.noteSignal.tagsChanged.connect(newBrowser, "updateTags(String, List)");
browserWindow.noteSignal.titleChanged.connect(newBrowser, "updateTitle(String, String)");
browserWindow.noteSignal.notebookChanged.connect(newBrowser, "updateNotebook(String, String)");
+ browserWindow.blockApplication.connect(this, "blockApplication(Long)");
+ browserWindow.unblockApplication.connect(this, "unblockApplication()");
newBrowser.show();
}
return true;
}
+
+ //*************************************************
+ // Block the program. This is used for things
+ // like async web calls.
+ //*************************************************
+ private void blockApplication(Long time) {
+ Calendar currentTime = new GregorianCalendar();
+ waitCursor(true);
+ blockTime = new Long(currentTime.getTimeInMillis())+time;
+
+ for (;currentTime.getTimeInMillis()>blockTime;) {
+ try {
+ wait(1);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ waitCursor(false);
+ }
+
+ }
+ private void unblockApplication() {
+ blockTime = -1;
+ }
}
--- /dev/null
+/*\r
+ * This file is part of NeverNote \r
+ * Copyright 2009 Randy Baumgarte\r
+ * \r
+ * This file may be licensed under the terms of of the\r
+ * GNU General Public License Version 2 (the ``GPL'').\r
+ *\r
+ * Software distributed under the License is distributed\r
+ * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either\r
+ * express or implied. See the GPL for the specific language\r
+ * governing rights and limitations.\r
+ *\r
+ * You should have received a copy of the GPL along with this\r
+ * program. If not, go to http://www.gnu.org/licenses/gpl.html\r
+ * or write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
+ *\r
+*/\r
+\r
+package cx.fbn.nevernote.dialog;\r
+\r
+\r
+import com.trolltech.qt.core.Qt;\r
+import com.trolltech.qt.gui.QDialog;\r
+import com.trolltech.qt.gui.QGridLayout;\r
+import com.trolltech.qt.gui.QIcon;\r
+import com.trolltech.qt.gui.QLabel;\r
+import com.trolltech.qt.gui.QPushButton;\r
+import com.trolltech.qt.gui.QTextEdit;\r
+\r
+public class InsertLatexImage extends QDialog {\r
+\r
+ private boolean okPressed;\r
+ private final QTextEdit url;\r
+ private final QPushButton ok;\r
+ private String latexText;\r
+ private final String iconPath = new String("classpath:cx/fbn/nevernote/icons/");\r
+ \r
+ // Constructor\r
+ public InsertLatexImage() {\r
+ okPressed = false;\r
+ setWindowTitle(tr("Insert LaTeX Formula"));\r
+ setWindowIcon(new QIcon(iconPath+"link.png"));\r
+ QGridLayout grid = new QGridLayout();\r
+ QGridLayout input = new QGridLayout();\r
+ QGridLayout button = new QGridLayout();\r
+ setLayout(grid);\r
+ \r
+ \r
+ url = new QTextEdit("");\r
+ \r
+ input.addWidget(new QLabel(tr("Formula")), 1,1);\r
+ input.addWidget(url, 2, 1);\r
+ input.setContentsMargins(10, 10, -10, -10);\r
+ grid.addLayout(input, 1,1);\r
+ \r
+ ok = new QPushButton(tr("OK"));\r
+ ok.clicked.connect(this, "accept()");\r
+ ok.setEnabled(false);\r
+ \r
+ QPushButton cancel = new QPushButton(tr("Cancel"));\r
+ cancel.clicked.connect(this, "reject()");\r
+ button.addWidget(ok, 1, 1);\r
+ button.addWidget(cancel, 1,2);\r
+ grid.addLayout(button, 3, 1);\r
+ url.textChanged.connect(this, "validateInput()");\r
+ \r
+ setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose);\r
+ }\r
+ // Set the formula\r
+ public void setFormula(String x) {\r
+ url.setText(x);\r
+ }\r
+ // Get the formula \r
+ public String getFormula() {\r
+ return latexText;\r
+ }\r
+ // Set the url\r
+ public void setUrl(String u) {\r
+ url.setText(u);\r
+ }\r
+ // Check if the OK button was pressed\r
+ public boolean okPressed() {\r
+ return okPressed;\r
+ }\r
+ // Check that we have a valid URL\r
+ @SuppressWarnings("unused")\r
+ private void validateInput() {\r
+ ok.setEnabled(true);\r
+ if (url.toPlainText().trim().equals("")) \r
+ ok.setEnabled(false);\r
+ }\r
+ \r
+ @Override\r
+ public void accept() {\r
+ if (ok.isEnabled()) {\r
+ okPressed = true;\r
+ latexText = url.toPlainText();\r
+ super.accept();\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public void reject() {\r
+ okPressed=false;\r
+ super.reject();\r
+ }\r
+}\r
import com.trolltech.qt.gui.QToolButton.ToolButtonPopupMode;\r
import com.trolltech.qt.gui.QVBoxLayout;\r
import com.trolltech.qt.gui.QWidget;\r
+import com.trolltech.qt.network.QNetworkAccessManager;\r
+import com.trolltech.qt.network.QNetworkReply;\r
+import com.trolltech.qt.network.QNetworkReply.NetworkError;\r
import com.trolltech.qt.network.QNetworkRequest;\r
import com.trolltech.qt.webkit.QWebPage;\r
import com.trolltech.qt.webkit.QWebPage.WebAction;\r
import cx.fbn.nevernote.dialog.EnCryptDialog;\r
import cx.fbn.nevernote.dialog.EnDecryptDialog;\r
import cx.fbn.nevernote.dialog.GeoDialog;\r
+import cx.fbn.nevernote.dialog.InsertLatexImage;\r
import cx.fbn.nevernote.dialog.InsertLinkDialog;\r
import cx.fbn.nevernote.dialog.SpellCheck;\r
import cx.fbn.nevernote.dialog.TableDialog;\r
private String saveNoteTitle;\r
private String saveTagList;\r
private boolean insideList;\r
-// private String selectedText;\r
private final DatabaseConnection conn;\r
private final QCalendarWidget createdCalendarWidget;\r
private final QCalendarWidget alteredCalendarWidget;\r
boolean insertHyperlink = true;\r
boolean insideTable = false;\r
boolean insideEncryption = false;\r
+ public Signal1<Long> blockApplication;\r
+ public Signal0 unblockApplication;\r
+ String latexGuid; // This is set if we are editing an existing LaTeX formula. Useful to track guid.\r
\r
\r
public static class SuggestionListener implements SpellCheckListener {\r
tagEdit.setPalette(pal);\r
notebookBox.setPalette(pal);\r
\r
+ blockApplication = new Signal1<Long>();\r
+ unblockApplication = new Signal0();\r
+ \r
logger.log(logger.HIGH, "Browser setup complete");\r
}\r
\r
@SuppressWarnings("unused")\r
private void linkClicked(QUrl url) {\r
logger.log(logger.EXTREME, "URL Clicked: " +url.toString());\r
- if (url.toString().substring(0,8).equals("nnres://")) {\r
+ if (url.toString().startsWith("latex://")) {\r
+ int position = url.toString().lastIndexOf(".");\r
+ String guid = url.toString().substring(0,position);\r
+ position = guid.lastIndexOf("/");\r
+ guid = guid.substring(position+1);\r
+ editLatex(guid);\r
+ return;\r
+ }\r
+ if (url.toString().startsWith("nnres://")) {\r
logger.log(logger.EXTREME, "URL is NN resource");\r
if (url.toString().endsWith("/vnd.evernote.ink")) {\r
logger.log(logger.EXTREME, "Unable to open ink note");\r
}\r
\r
\r
+ // Insert a hyperlink\r
+ public void insertLatex() {\r
+ editLatex(null);\r
+ }\r
+ public void editLatex(String guid) {\r
+ logger.log(logger.EXTREME, "Inserting latex");\r
+ String text = browser.selectedText();\r
+ if (text.trim().equalsIgnoreCase("")) {\r
+ InsertLatexImage dialog = new InsertLatexImage();\r
+ if (guid != null) {\r
+ String formula = conn.getNoteTable().noteResourceTable.getNoteSourceUrl(guid).replace("http://latex.codecogs.com/gif.latex?", "");\r
+ dialog.setFormula(formula);\r
+ }\r
+ dialog.exec();\r
+ if (!dialog.okPressed()) {\r
+ logger.log(logger.EXTREME, "Edit LaTex canceled");\r
+ return;\r
+ }\r
+ text = dialog.getFormula().trim();\r
+ }\r
+ blockApplication.emit(new Long(5000));\r
+ logger.log(logger.EXTREME, "Inserting LaTeX formula:" +text);\r
+ latexGuid = guid;\r
+ text = StringUtils.replace(text, "'", "\\'");\r
+ String url = "http://latex.codecogs.com/gif.latex?" +text;\r
+ QNetworkAccessManager manager = new QNetworkAccessManager(this);\r
+ manager.finished.connect(this, "insertLatexImageReady(QNetworkReply)");\r
+ manager.get(new QNetworkRequest(new QUrl(url)));\r
+ }\r
+ \r
+ public void insertLatexImageReady(QNetworkReply reply) {\r
+ if (reply.error() != NetworkError.NoError) \r
+ return;\r
+ \r
+ QByteArray image = reply.readAll();\r
+\r
+\r
+ Resource newRes = null;\r
+ QFile tfile;\r
+ String path;\r
+ if (latexGuid == null) {\r
+ path = Global.getFileManager().getResDirPath("latex-temp.gif");\r
+ tfile = new QFile(path);\r
+ tfile.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.WriteOnly));\r
+ tfile.write(image);\r
+ tfile.close();\r
+ newRes = createResource(path,0,"image/gif", false);\r
+ path = Global.getFileManager().getResDirPath(newRes.getGuid()+".gif");\r
+ tfile.rename(path);\r
+ } else {\r
+ newRes = conn.getNoteTable().noteResourceTable.getNoteResource(latexGuid, false);\r
+ path = Global.getFileManager().getResDirPath(newRes.getGuid()+".gif");\r
+ tfile = new QFile(path);\r
+ tfile.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.WriteOnly));\r
+ tfile.write(image);\r
+ tfile.close();\r
+ }\r
+\r
+\r
+ newRes.getAttributes().setSourceURL(reply.url().toString());\r
+// newRes.getData().setBody(image.toByteArray());\r
+// conn.getNoteTable().noteResourceTable.updateNoteResource(newRes, true);\r
+ conn.getNoteTable().noteResourceTable.updateNoteSourceUrl(newRes.getGuid(), reply.url().toString(), true);\r
+ \r
+ for(int i=0; i<currentNote.getResourcesSize(); i++) {\r
+ if (currentNote.getResources().get(i).getGuid().equals(newRes.getGuid())) {\r
+ currentNote.getResources().remove(i);\r
+ i=currentNote.getResourcesSize();\r
+ }\r
+ }\r
+ currentNote.getResources().add(newRes);\r
+ \r
+\r
+ // do the actual insert into the note. We only do this on new formulas. Existing ones we\r
+ // just write out the file (which is aleady done) and reload.\r
+ if (latexGuid == null) {\r
+ StringBuffer buffer = new StringBuffer(100);\r
+ String formula = reply.url().toString().toLowerCase().replace("http://latex.codecogs.com/gif.latex?", "");\r
+ buffer.append("<a href=\"latex://"+path.replace("\\", "/")+"\" title=\""+formula+"\"><img src=\"");\r
+ buffer.append(path.replace("\\", "/"));\r
+ buffer.append("\" en-tag=en-media type=\"image/gif\""\r
+ +" hash=\""+Global.byteArrayToHexString(newRes.getData().getBodyHash()) +"\""\r
+ +" guid=\"" +newRes.getGuid() +"\""\r
+ + " /></a>");\r
+ \r
+ String script_start = new String("document.execCommand('insertHTML', false, '");\r
+ String script_end = new String("');");\r
+ browser.page().mainFrame().evaluateJavaScript(\r
+ script_start + buffer + script_end);\r
+ }\r
+ QWebSettings.setMaximumPagesInCache(0);\r
+ QWebSettings.setObjectCacheCapacities(0, 0, 0);\r
+ browser.setHtml(browser.page().mainFrame().toHtml());\r
+ browser.reload();\r
+ contentChanged();\r
+ resourceSignal.contentChanged.emit(path);\r
+ unblockApplication.emit();\r
+ return;\r
+ \r
+ }\r
+\r
+ \r
+ \r
// Insert a table\r
public void insertTable() {\r
TableDialog dialog = new TableDialog();\r
buffer.append("\" en-tag=en-media type=\"image/jpeg\""\r
+" hash=\""+Global.byteArrayToHexString(newRes.getData().getBodyHash()) +"\""\r
+" guid=\"" +newRes.getGuid() +"\""\r
-// +" onContextMenu=\"window.jambi.imageContextMenu('" +tfile.fileName() +"');\""\r
+" onContextMenu=\"window.jambi.imageContextMenu(&." +tfile.fileName() +"&.);\""\r
+ " />");\r
\r
browser.openAction.setEnabled(true);\r
selectedFile = f;\r
}\r
- \r
- \r
+ public void latexContextMenu(String f) {\r
+ browser.downloadImage.setEnabled(true);\r
+ browser.rotateImageRight.setEnabled(true);\r
+ browser.rotateImageLeft.setEnabled(true);\r
+ browser.openAction.setEnabled(true);\r
+ selectedFile = f;\r
+ }\r
+\r
//****************************************************************\r
//* Apply CSS style to specified word\r
//****************************************************************\r
QShortcut rotateImageLeftShortcut; \r
QAction insertLinkAction;\r
QShortcut insertLinkShortcut;\r
+ QAction insertLatexAction;\r
+ QShortcut insertLatexShortcut;\r
QAction insertTableAction;\r
QShortcut insertTableShortcut;\r
QAction insertTableRowAction;\r
insertLinkShortcut = new QShortcut(this);\r
setupShortcut(insertLinkShortcut, "Edit_Insert_Hyperlink");\r
insertLinkShortcut.activated.connect(parent, "insertLink()");\r
+\r
+ insertLatexAction = new QAction(tr("Insert LaTeX Formula"), this);\r
+ insertLatexAction.triggered.connect(parent, "insertLatex()");\r
+ setupShortcut(insertLatexAction, "Insert_Latex");\r
+ contextMenu.addAction(insertLatexAction);\r
+ insertLatexShortcut = new QShortcut(this);\r
+ setupShortcut(insertLatexShortcut, "Latex_Insert");\r
+ insertLatexShortcut.activated.connect(parent, "insertLatex()");\r
\r
contextMenu.addMenu(tableMenu);\r
tableMenu.setTitle("Table");\r
+"recognitionHash, recognitionSize, "\r
+"attributeLatitude, attributeLongitude, attributeAltitude, "\r
+"attributeCameraMake, attributeCameraModel, attributeClientWillIndex, "\r
- +"attributeRecoType, attributeFileName, attributeAttachment, recognitionBinary "\r
+ +"attributeRecoType, attributeFileName, attributeAttachment, attributeSourceUrl "\r
+" from NoteResources where guid=:guid");\r
\r
\r
a.setRecoType(stringValue(query.valueString(18))); // Recognition Type\r
a.setFileName(stringValue(query.valueString(19))); // File Name\r
a.setAttachment(booleanValue(query.valueString(20).toString(),false));\r
+ a.setSourceURL(query.valueString(21));\r
r.setAttributes(a);\r
\r
if (withBinary) {\r
return unknown;\r
}\r
\r
+ // Update note source url. \r
+ public void updateNoteSourceUrl(String guid, String url, boolean isDirty) {\r
+ logger.log(logger.HIGH, "Entering RNoteResourceTable.updateNoteSourceUrl()");\r
+ NSqlQuery query = new NSqlQuery(db.getResourceConnection());\r
+ query.prepare("update NoteResources set attributesourceurl=:url, isDirty=:isDirty where guid=:guid");\r
+ query.bindValue(":guid", guid);\r
+ query.bindValue(":isDirty", isDirty);\r
+ query.bindValue(":url", url);\r
+ query.exec();\r
+ query.exec("commit");\r
+ logger.log(logger.HIGH, "Leaving RNoteResourceTable.updateNoteSourceUrl()");\r
+ }\r
+ \r
+ // Get note source\r
+ public String getNoteSourceUrl(String guid) {\r
+ logger.log(logger.HIGH, "Entering RNoteResourceTable.getNoteSourceUrl()");\r
+ NSqlQuery query = new NSqlQuery(db.getResourceConnection());\r
+ query.prepare("Select attributesourceurl from noteresources where guid=:guid");\r
+ query.bindValue(":guid", guid);\r
+ query.exec();\r
+ if (query.next()) {\r
+ logger.log(logger.HIGH, "Leaving RNoteResourceTable.getNoteSourceUrl()");\r
+ return query.valueString(0);\r
+ }\r
+ logger.log(logger.HIGH, "Leaving RNoteResourceTable.getNoteSourceUrl() - no value found");\r
+ return null;\r
+ }\r
}\r
}\r
\r
// Modify the en-media tag into an image tag so it can be displayed.\r
- private void modifyImageTags(QDomElement docElem, QDomElement enmedia, QDomAttr hash) {\r
+ private void modifyImageTags(QDomDocument doc, QDomElement docElem, QDomElement enmedia, QDomAttr hash) {\r
logger.log(logger.HIGH, "Entering NeverNote.modifyImageTags");\r
String type = enmedia.attribute("type");\r
if (type.startsWith("image/"))\r
// enmedia.setAttribute("src", QUrl.fromLocalFile(tfile.fileName()).toString());\r
enmedia.setAttribute("src", tfile.fileName().toString());\r
enmedia.setAttribute("en-tag", "en-media");\r
- enmedia.setAttribute("onContextMenu", "window.jambi.imageContextMenu('" +tfile.fileName() +"');");\r
+ enmedia.setTagName("img");\r
+ if (r.getAttributes().getSourceURL() == null || !r.getAttributes().getSourceURL().toLowerCase().startsWith("http://latex.codecogs.com/gif.latex?"))\r
+ enmedia.setAttribute("onContextMenu", "window.jambi.imageContextMenu('" +tfile.fileName() +"');");\r
+ else {\r
+ QDomElement newText = doc.createElement("a");\r
+ enmedia.setAttribute("src", tfile.fileName().toString());\r
+ enmedia.setAttribute("en-tag", "en-latex");\r
+ newText.setAttribute("onMouseOver", "style.cursor='hand'");\r
+ newText.setAttribute("title", r.getAttributes().getSourceURL());\r
+ newText.setAttribute("href", "latex://"+tfile.fileName().toString());\r
+ enmedia.parentNode().replaceChild(newText, enmedia);\r
+ newText.appendChild(enmedia);\r
+\r
+ }\r
enmedia.setNodeValue("");\r
enmedia.setAttribute("guid", resGuid);\r
- enmedia.setTagName("img");\r
+\r
\r
logger.log(logger.HIGH, "Leaving NeverNote.modifyImageTags");\r
}\r
\r
if (type[0] != null) {\r
if (type[0].equals("image")) {\r
- modifyImageTags(docElem, enmedia, hash);\r
+ modifyImageTags(doc, docElem, enmedia, hash);\r
}\r
if (!type[0].equals("image")) {\r
modifyApplicationTags(doc, docElem, enmedia, hash, appl);\r
enCryptLen = anchors.length();\r
for (int i=0; i<anchors.length(); i++) {\r
QDomElement element = anchors.at(i).toElement();\r
- element.setAttribute("title", element.attribute("href"));\r
+ if (!element.attribute("href").toLowerCase().startsWith("latex://"))\r
+ element.setAttribute("title", element.attribute("href"));\r
+ else {\r
+ element.setAttribute("title", element.attribute("title").toLowerCase().replace("http://latex.codecogs.com/gif.latex?",""));\r
+ }\r
}\r
\r
logger.log(logger.HIGH, "Leaving NeverNote.modifyTags");\r
node.removeChild(e);\r
return;\r
}\r
+\r
+ // Check if we have a LaTeX image. Remove the parent link tag\r
+ if (enType.equalsIgnoreCase("en-latex")) {\r
+ enType = "en-media";\r
+ QDomNode parent = e.parentNode();\r
+ parent.removeChild(e);\r
+ parent.parentNode().replaceChild(e, parent);\r
+ }\r
+\r
\r
// If we've gotten this far, we have an en-media tag\r
e.setTagName(enType);\r