1 package cx.fbn.nevernote.xml;
\r
4 import java.util.ArrayList;
\r
5 import java.util.List;
\r
7 import com.evernote.edam.type.Note;
\r
8 import com.evernote.edam.type.Resource;
\r
9 import com.trolltech.qt.core.QByteArray;
\r
10 import com.trolltech.qt.core.QDataStream;
\r
11 import com.trolltech.qt.core.QFile;
\r
12 import com.trolltech.qt.core.QIODevice;
\r
13 import com.trolltech.qt.core.QTemporaryFile;
\r
14 import com.trolltech.qt.core.QUrl;
\r
15 import com.trolltech.qt.xml.QDomAttr;
\r
16 import com.trolltech.qt.xml.QDomDocument;
\r
17 import com.trolltech.qt.xml.QDomElement;
\r
18 import com.trolltech.qt.xml.QDomNodeList;
\r
20 import cx.fbn.nevernote.Global;
\r
21 import cx.fbn.nevernote.config.FileManager;
\r
22 import cx.fbn.nevernote.filters.EnSearch;
\r
23 import cx.fbn.nevernote.gui.PDFPreview;
\r
24 import cx.fbn.nevernote.sql.DatabaseConnection;
\r
25 import cx.fbn.nevernote.utilities.ApplicationLogger;
\r
27 public class NoteFormatter {
\r
29 private final ApplicationLogger logger;
\r
30 private final DatabaseConnection conn;
\r
31 public boolean resourceError = false;
\r
32 public boolean readOnly = false;
\r
33 public boolean addHighlight = true;
\r
34 private Note currentNote;
\r
35 private String currentNoteGuid;
\r
36 private boolean pdfPreview;
\r
37 ArrayList<QTemporaryFile> tempFiles;
\r
38 private EnSearch enSearch;
\r
39 private boolean noteHistory;
\r
41 public NoteFormatter(ApplicationLogger logger, DatabaseConnection conn, List<QTemporaryFile> tempFiles2) {
\r
42 this.logger = logger;
\r
44 noteHistory = false;
\r
48 public void setNote(Note note, boolean pdfPreview) {
\r
50 this.pdfPreview = pdfPreview;
\r
51 currentNoteGuid = note.getGuid();
\r
53 resourceError = false;
\r
56 public void setHighlight(EnSearch search) {
\r
57 if (search==null || search.hilightWords == null ||search.hilightWords.size() == 0) {
\r
59 addHighlight = false;
\r
62 addHighlight = true;
\r
66 // Set if we are coming here through note histary. It triggers longer file names to avoid conflicts
\r
67 public void setNoteHistory(boolean value) {
\r
68 noteHistory = value;
\r
71 // Rebuild the note HTML to something usable
\r
72 public String rebuildNoteHTML() {
\r
73 logger.log(logger.HIGH, "Entering NeverNote.rebuildNoteHTML");
\r
74 logger.log(logger.EXTREME, "Note guid: " +currentNoteGuid);
\r
75 logger.log(logger.EXTREME, "Note Text:" +currentNote);
\r
76 QDomDocument doc = new QDomDocument();
\r
77 QDomDocument.Result result = doc.setContent(currentNote.getContent());
\r
78 if (!result.success) {
\r
79 logger.log(logger.MEDIUM, "Parse error when rebuilding HTML");
\r
80 logger.log(logger.MEDIUM, "Note guid: " +currentNoteGuid);
\r
81 logger.log(logger.EXTREME, "Start of unmodified note HTML");
\r
82 logger.log(logger.EXTREME, currentNote.getContent());
\r
83 logger.log(logger.EXTREME, "End of unmodified note HTML");
\r
84 return currentNote.getContent();
\r
87 if (tempFiles == null)
\r
88 tempFiles = new ArrayList<QTemporaryFile>();
\r
91 doc = modifyTags(doc);
\r
93 doc = addHilight(doc);
\r
94 QDomElement docElem = doc.documentElement();
\r
95 docElem.setTagName("Body");
\r
96 // docElem.setAttribute("bgcolor", "green");
\r
97 logger.log(logger.EXTREME, "Rebuilt HTML:");
\r
98 logger.log(logger.EXTREME, doc.toString());
\r
99 logger.log(logger.HIGH, "Leaving NeverNote.rebuildNoteHTML");
\r
100 // Fix the stupid problem where inserting an <img> tag after an <a> tag (which is done
\r
101 // to get the <en-media> application tag to work properly) causes spaces to be inserted
\r
102 // between the <a> & <img>. This messes things up later. This is an ugly hack.
\r
103 StringBuffer html = new StringBuffer(doc.toString());
\r
104 for (int i=html.indexOf("<a en-tag=\"en-media\" ", 0); i>-1; i=html.indexOf("<a en-tag=\"en-media\" ", i)) {
\r
105 i=html.indexOf(">\n",i+1);
\r
106 int z = html.indexOf("<img",i);
\r
107 for (int j=z-1; j>i; j--)
\r
108 html.deleteCharAt(j);
\r
109 i=html.indexOf("/>", z+1);
\r
110 z = html.indexOf("</a>",i);
\r
111 for (int j=z-1; j>i+1; j--)
\r
112 html.deleteCharAt(j);
\r
114 return html.toString();
\r
118 // Modify the en-media tag into an image tag so it can be displayed.
\r
119 private void modifyImageTags(QDomElement docElem, QDomElement enmedia, QDomAttr hash) {
\r
120 logger.log(logger.HIGH, "Entering NeverNote.modifyImageTags");
\r
121 String type = enmedia.attribute("type");
\r
122 if (type.startsWith("image/"))
\r
123 type = "."+type.substring(6);
\r
127 String resGuid = conn.getNoteTable().noteResourceTable.getNoteResourceGuidByHashHex(currentNoteGuid, hash.value());
\r
128 QFile tfile = new QFile(Global.getFileManager().getResDirPath(resGuid + type));
\r
129 if (!tfile.exists()) {
\r
131 if (resGuid != null)
\r
132 r = conn.getNoteTable().noteResourceTable.getNoteResource(resGuid,true);
\r
133 if (r==null || r.getData() == null || r.getData().getBody().length == 0)
\r
134 resourceError = true;;
\r
135 if (r!= null && r.getData() != null && r.getData().getBody().length > 0) {
\r
136 tfile.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.WriteOnly));
\r
137 QByteArray binData = new QByteArray(r.getData().getBody());
\r
138 tfile.write(binData);
\r
140 enmedia.setAttribute("src", QUrl.fromLocalFile(tfile.fileName()).toString());
\r
141 enmedia.setAttribute("en-tag", "en-media");
\r
142 enmedia.setNodeValue("");
\r
143 enmedia.setAttribute("guid", r.getGuid());
\r
144 enmedia.setTagName("img");
\r
147 enmedia.setAttribute("src", QUrl.fromLocalFile(tfile.fileName()).toString());
\r
148 enmedia.setAttribute("en-tag", "en-media");
\r
149 enmedia.setAttribute("onContextMenu", "window.jambi.imageContextMenu('" +tfile.fileName() +"');");
\r
150 enmedia.setNodeValue("");
\r
151 enmedia.setAttribute("guid", resGuid);
\r
152 enmedia.setTagName("img");
\r
154 logger.log(logger.HIGH, "Leaving NeverNote.modifyImageTags");
\r
158 // Modify tags from Evernote specific things to XHTML tags.
\r
159 private QDomDocument modifyTags(QDomDocument doc) {
\r
160 logger.log(logger.HIGH, "Entering NeverNote.modifyTags");
\r
161 if (tempFiles == null)
\r
162 tempFiles = new ArrayList<QTemporaryFile>();
\r
164 QDomElement docElem = doc.documentElement();
\r
166 // Modify en-media tags
\r
167 QDomNodeList anchors = docElem.elementsByTagName("en-media");
\r
168 int enMediaCount = anchors.length();
\r
169 for (int i=enMediaCount-1; i>=0; i--) {
\r
170 QDomElement enmedia = anchors.at(i).toElement();
\r
171 if (enmedia.hasAttribute("type")) {
\r
172 QDomAttr attr = enmedia.attributeNode("type");
\r
173 QDomAttr hash = enmedia.attributeNode("hash");
\r
174 String[] type = attr.nodeValue().split("/");
\r
175 String appl = type[1];
\r
177 if (type[0] != null) {
\r
178 if (type[0].equals("image")) {
\r
179 modifyImageTags(docElem, enmedia, hash);
\r
181 if (!type[0].equals("image")) {
\r
182 modifyApplicationTags(doc, docElem, enmedia, hash, appl);
\r
188 // Modify todo tags
\r
189 anchors = docElem.elementsByTagName("en-todo");
\r
190 int enTodoCount = anchors.length();
\r
191 for (int i=enTodoCount-1; i>=0; i--) {
\r
192 QDomElement enmedia = anchors.at(i).toElement();
\r
193 modifyTodoTags(enmedia);
\r
196 // Modify en-crypt tags
\r
197 anchors = docElem.elementsByTagName("en-crypt");
\r
198 int enCryptLen = anchors.length();
\r
199 for (int i=enCryptLen-1; i>=0; i--) {
\r
200 QDomElement enmedia = anchors.at(i).toElement();
\r
201 enmedia.setAttribute("contentEditable","false");
\r
202 enmedia.setAttribute("src", Global.getFileManager().getImageDirPath("encrypt.png"));
\r
203 enmedia.setAttribute("en-tag","en-crypt");
\r
204 enmedia.setAttribute("alt", enmedia.text());
\r
205 Global.cryptCounter++;
\r
206 enmedia.setAttribute("id", "crypt"+Global.cryptCounter.toString());
\r
207 String encryptedText = enmedia.text();
\r
209 // If the encryption string contains crlf at the end, remove them because they mess up the javascript.
\r
210 if (encryptedText.endsWith("\n"))
\r
211 encryptedText = encryptedText.substring(0,encryptedText.length()-1);
\r
212 if (encryptedText.endsWith("\r"))
\r
213 encryptedText = encryptedText.substring(0,encryptedText.length()-1);
\r
215 // Add the commands
\r
216 String hint = enmedia.attribute("hint");
\r
217 hint = hint.replace("'","'");
\r
218 enmedia.setAttribute("onClick", "window.jambi.decryptText('crypt"+Global.cryptCounter.toString()+"', '"+encryptedText+"', '"+hint+"');");
\r
219 enmedia.setAttribute("onMouseOver", "style.cursor='hand'");
\r
220 enmedia.setTagName("img");
\r
221 enmedia.removeChild(enmedia.firstChild()); // Remove the actual encrypted text
\r
225 // Modify link tags
\r
226 anchors = docElem.elementsByTagName("a");
\r
227 enCryptLen = anchors.length();
\r
228 for (int i=0; i<anchors.length(); i++) {
\r
229 QDomElement element = anchors.at(i).toElement();
\r
230 element.setAttribute("title", element.attribute("href"));
\r
233 logger.log(logger.HIGH, "Leaving NeverNote.modifyTags");
\r
238 // Modify the en-media tag into an attachment
\r
239 private void modifyApplicationTags(QDomDocument doc, QDomElement docElem, QDomElement enmedia, QDomAttr hash, String appl) {
\r
240 logger.log(logger.HIGH, "Entering NeverNote.modifyApplicationTags");
\r
241 if (appl.equalsIgnoreCase("vnd.evernote.ink"))
\r
243 String resGuid = conn.getNoteTable().noteResourceTable.getNoteResourceGuidByHashHex(currentNote.getGuid(), hash.value());
\r
244 Resource r = conn.getNoteTable().noteResourceTable.getNoteResource(resGuid, false);
\r
245 if (r == null || r.getData() == null)
\r
246 resourceError = true;
\r
248 if (r.getData()!=null) {
\r
249 // Did we get a generic applicaiton? Then look at the file name to
\r
250 // try and find a good application type for the icon
\r
251 if (appl.equalsIgnoreCase("octet-stream")) {
\r
252 if (r.getAttributes() != null && r.getAttributes().getFileName() != null) {
\r
253 String fn = r.getAttributes().getFileName();
\r
254 int pos = fn.lastIndexOf(".");
\r
256 appl = fn.substring(pos+1);
\r
261 String fileDetails = null;
\r
262 if (r.getAttributes() != null && r.getAttributes().getFileName() != null && !r.getAttributes().getFileName().equals(""))
\r
263 fileDetails = r.getAttributes().getFileName();
\r
264 String contextFileName;
\r
265 FileManager fileManager = Global.getFileManager();
\r
266 if (fileDetails != null && !fileDetails.equals("")) {
\r
267 if (!noteHistory) {
\r
268 enmedia.setAttribute("href", "nnres://" +r.getGuid()
\r
269 +Global.attachmentNameDelimeter +fileDetails);
\r
270 contextFileName = fileManager.getResDirPath(r.getGuid()
\r
271 +Global.attachmentNameDelimeter + fileDetails);
\r
273 enmedia.setAttribute("href", "nnres://" +r.getGuid() + currentNote.getUpdateSequenceNum()
\r
274 +Global.attachmentNameDelimeter +fileDetails);
\r
275 contextFileName = fileManager.getResDirPath(r.getGuid() + currentNote.getUpdateSequenceNum()
\r
276 +Global.attachmentNameDelimeter + fileDetails);
\r
279 if (!noteHistory) {
\r
280 enmedia.setAttribute("href", "nnres://" +r.getGuid() +currentNote.getUpdateSequenceNum()
\r
281 +Global.attachmentNameDelimeter +appl);
\r
282 contextFileName = fileManager.getResDirPath(r.getGuid() +currentNote.getUpdateSequenceNum()
\r
283 +Global.attachmentNameDelimeter + appl);
\r
285 enmedia.setAttribute("href", "nnres://" +r.getGuid()
\r
286 +Global.attachmentNameDelimeter +appl);
\r
287 contextFileName = fileManager.getResDirPath(r.getGuid()
\r
288 +Global.attachmentNameDelimeter + appl);
\r
291 contextFileName = contextFileName.replace("\\", "/");
\r
292 enmedia.setAttribute("onContextMenu", "window.jambi.resourceContextMenu('" +contextFileName +"');");
\r
293 if (fileDetails == null || fileDetails.equals(""))
\r
295 enmedia.setAttribute("en-tag", "en-media");
\r
296 enmedia.setAttribute("guid", r.getGuid());
\r
297 enmedia.setTagName("a");
\r
298 QDomElement newText = doc.createElement("img");
\r
299 boolean goodPreview = false;
\r
300 String filePath = "";
\r
301 if (appl.equalsIgnoreCase("pdf") && pdfPreview) {
\r
303 Resource res = conn.getNoteTable().noteResourceTable.getNoteResource(r.getGuid(), true);
\r
304 if (res.getAttributes() != null &&
\r
305 res.getAttributes().getFileName() != null &&
\r
306 !res.getAttributes().getFileName().trim().equals(""))
\r
307 fileName = res.getGuid()+Global.attachmentNameDelimeter+res.getAttributes().getFileName();
\r
309 fileName = res.getGuid()+".pdf";
\r
310 QFile file = new QFile(fileManager.getResDirPath(fileName));
\r
311 QFile.OpenMode mode = new QFile.OpenMode();
\r
312 mode.set(QFile.OpenModeFlag.WriteOnly);
\r
314 QDataStream out = new QDataStream(file);
\r
315 Resource resBinary = conn.getNoteTable().noteResourceTable.getNoteResource(res.getGuid(), true);
\r
316 QByteArray binData = new QByteArray(resBinary.getData().getBody());
\r
318 out.writeBytes(binData.toByteArray());
\r
320 PDFPreview pdfPreview = new PDFPreview();
\r
321 goodPreview = pdfPreview.setupPreview(file.fileName(), appl,0);
\r
323 QDomElement span = doc.createElement("span");
\r
324 QDomElement table = doc.createElement("table");
\r
325 span.setAttribute("pdfNavigationTable", "true");
\r
326 QDomElement tr = doc.createElement("tr");
\r
327 QDomElement td = doc.createElement("td");
\r
328 QDomElement left = doc.createElement("img");
\r
329 left.setAttribute("onMouseDown", "window.jambi.nextPage('" +file.fileName() +"')");
\r
330 left.setAttribute("onMouseDown", "window.jambi.nextPage('" +file.fileName() +"')");
\r
331 left.setAttribute("onMouseOver", "style.cursor='hand'");
\r
332 QDomElement right = doc.createElement("img");
\r
333 right.setAttribute("onMouseDown", "window.jambi.nextPage('" +file.fileName() +"')");
\r
334 left.setAttribute("onMouseDown", "window.jambi.previousPage('" +file.fileName() +"')");
\r
335 // NFC TODO: should these be file:// URLs?
\r
336 left.setAttribute("src", Global.getFileManager().getImageDirPath("small_left.png"));
\r
337 right.setAttribute("src", Global.getFileManager().getImageDirPath("small_right.png"));
\r
338 right.setAttribute("onMouseOver", "style.cursor='hand'");
\r
340 table.appendChild(tr);
\r
341 tr.appendChild(td);
\r
342 td.appendChild(left);
\r
343 td.appendChild(right);
\r
344 span.appendChild(table);
\r
345 enmedia.parentNode().insertBefore(span, enmedia);
\r
347 filePath = fileName+".png";
\r
349 String icon = findIcon(appl);
\r
350 if (icon.equals("attachment.png"))
\r
351 icon = findIcon(fileDetails.substring(fileDetails.indexOf(".")+1));
\r
352 // NFC TODO: should this be a 'file://' URL?
\r
353 newText.setAttribute("src", Global.getFileManager().getImageDirPath(icon));
\r
355 // NFC TODO: should this be a 'file://' URL?
\r
356 newText.setAttribute("src", fileManager.getResDirPath(filePath));
\r
357 newText.setAttribute("style", "border-style:solid; border-color:green; padding:0.5mm 0.5mm 0.5mm 0.5mm;");
\r
359 newText.setAttribute("title", fileDetails);
\r
360 enmedia.removeChild(enmedia.firstChild());
\r
362 enmedia.appendChild(newText);
\r
365 logger.log(logger.HIGH, "Leaving NeverNote.modifyApplicationTags");
\r
367 // Modify the en-to tag into an input field
\r
368 private void modifyTodoTags(QDomElement todo) {
\r
369 logger.log(logger.HIGH, "Entering NeverNote.modifyTodoTags");
\r
370 todo.setAttribute("type", "checkbox");
\r
371 String checked = todo.attribute("checked");
\r
372 todo.removeAttribute("checked");
\r
373 if (checked.equalsIgnoreCase("true"))
\r
374 todo.setAttribute("checked", "");
\r
376 todo.setAttribute("unchecked","");
\r
377 todo.setAttribute("value", checked);
\r
378 todo.setAttribute("onClick", "value=checked;window.jambi.contentChanged(); ");
\r
379 todo.setTagName("input");
\r
380 logger.log(logger.HIGH, "Leaving NeverNote.modifyTodoTags");
\r
385 // Modify any cached todo tags that may have changed
\r
386 public String modifyCachedTodoTags(String note) {
\r
387 logger.log(logger.HIGH, "Entering NeverNote.modifyCachedTodoTags");
\r
388 StringBuffer html = new StringBuffer(note);
\r
389 for (int i=html.indexOf("<input", 0); i>-1; i=html.indexOf("<input", i)) {
\r
390 int endPos =html.indexOf(">",i+1);
\r
391 String input = html.substring(i,endPos);
\r
392 if (input.indexOf("value=\"true\"") > 0)
\r
393 input = input.replace(" unchecked=\"\"", " checked=\"\"");
\r
395 input = input.replace(" checked=\"\"", " unchecked=\"\"");
\r
396 html.replace(i, endPos, input);
\r
399 logger.log(logger.HIGH, "Leaving NeverNote.modifyCachedTodoTags");
\r
400 return html.toString();
\r
406 // Scan and do hilighting of words
\r
407 public QDomDocument addHilight(QDomDocument doc) {
\r
408 // EnSearch e = listManager.getEnSearch();
\r
409 if (enSearch.hilightWords == null || enSearch.hilightWords.size() == 0)
\r
411 XMLInsertHilight hilight = new XMLInsertHilight(doc, enSearch.hilightWords);
\r
412 return hilight.getDoc();
\r
416 // find the appropriate icon for an attachment
\r
417 private String findIcon(String appl) {
\r
418 logger.log(logger.HIGH, "Entering NeverNote.findIcon");
\r
419 appl = appl.toLowerCase();
\r
420 String relativePath = appl + ".png";
\r
421 File f = Global.getFileManager().getImageDirFile(relativePath);
\r
423 return relativePath;
\r
426 return appl+".png";
\r
427 logger.log(logger.HIGH, "Leaving NeverNote.findIcon");
\r
428 return "attachment.png";
\r