OSDN Git Service

Added LaTex image insert & edit
[neighbornote/NeighborNote.git] / src / cx / fbn / nevernote / gui / BrowserWindow.java
index 6f3280a..0aab4b9 100644 (file)
@@ -55,6 +55,7 @@ import com.swabunga.spell.event.SpellCheckListener;
 import com.swabunga.spell.event.SpellChecker;\r
 import com.swabunga.spell.event.StringWordTokenizer;\r
 import com.trolltech.qt.core.QByteArray;\r
+import com.trolltech.qt.core.QCoreApplication;\r
 import com.trolltech.qt.core.QDataStream;\r
 import com.trolltech.qt.core.QDateTime;\r
 import com.trolltech.qt.core.QEvent;\r
@@ -65,6 +66,7 @@ import com.trolltech.qt.core.QIODevice;
 import com.trolltech.qt.core.QMimeData;\r
 import com.trolltech.qt.core.QTextCodec;\r
 import com.trolltech.qt.core.QUrl;\r
+import com.trolltech.qt.core.Qt;\r
 import com.trolltech.qt.core.Qt.Key;\r
 import com.trolltech.qt.core.Qt.KeyboardModifier;\r
 import com.trolltech.qt.core.Qt.KeyboardModifiers;\r
@@ -72,6 +74,7 @@ import com.trolltech.qt.gui.QAction;
 import com.trolltech.qt.gui.QApplication;\r
 import com.trolltech.qt.gui.QCalendarWidget;\r
 import com.trolltech.qt.gui.QClipboard;\r
+import com.trolltech.qt.gui.QClipboard.Mode;\r
 import com.trolltech.qt.gui.QColor;\r
 import com.trolltech.qt.gui.QComboBox;\r
 import com.trolltech.qt.gui.QDateEdit;\r
@@ -92,6 +95,8 @@ import com.trolltech.qt.gui.QLineEdit;
 import com.trolltech.qt.gui.QListWidgetItem;\r
 import com.trolltech.qt.gui.QMatrix;\r
 import com.trolltech.qt.gui.QMessageBox;\r
+import com.trolltech.qt.gui.QPalette;\r
+import com.trolltech.qt.gui.QPalette.ColorRole;\r
 import com.trolltech.qt.gui.QPushButton;\r
 import com.trolltech.qt.gui.QShortcut;\r
 import com.trolltech.qt.gui.QTimeEdit;\r
@@ -99,6 +104,9 @@ import com.trolltech.qt.gui.QToolButton;
 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
@@ -109,11 +117,13 @@ import cx.fbn.nevernote.Global;
 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
 import cx.fbn.nevernote.dialog.TagAssign;\r
 import cx.fbn.nevernote.evernote.EnCrypt;\r
+import cx.fbn.nevernote.filters.FilterEditorTags;\r
 import cx.fbn.nevernote.signals.NoteResourceSignal;\r
 import cx.fbn.nevernote.signals.NoteSignal;\r
 import cx.fbn.nevernote.sql.DatabaseConnection;\r
@@ -156,7 +166,6 @@ public class BrowserWindow extends QWidget {
        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
@@ -231,8 +240,14 @@ public class BrowserWindow extends QWidget {
     SpellDictionary userDictionary;\r
     SpellChecker spellChecker;\r
     SuggestionListener spellListener;\r
-       private final HashMap<String,Integer> previewPageList; \r
-       \r
+       private final HashMap<String,Integer> previewPageList;  \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
                public boolean abortSpellCheck = false;\r
@@ -249,7 +264,8 @@ public class BrowserWindow extends QWidget {
                        errorsFound = true;\r
                        spellCheckDialog.setWord(event.getInvalidWord());\r
 \r
-                   List<Word> suggestions = event.getSuggestions();\r
+                   @SuppressWarnings("unchecked")\r
+                       List<Word> suggestions = event.getSuggestions();\r
                    spellCheckDialog.clearSuggestions();\r
                    if (!suggestions.isEmpty()) {\r
 //                    spellCheckDialog.setCurrentSuggestion(suggestions.get(0).getWord());\r
@@ -567,6 +583,27 @@ public class BrowserWindow extends QWidget {
                \r
                browser.page().microFocusChanged.connect(this, "microFocusChanged()");\r
                \r
+               //Setup colors\r
+               \r
+               QPalette pal = new QPalette();\r
+               pal.setColor(ColorRole.Text, QColor.black);\r
+               titleLabel.setPalette(pal);\r
+               authorText.setPalette(pal);\r
+               authorLabel.setPalette(pal);\r
+               urlLabel.setPalette(pal);\r
+               urlText.setPalette(pal);\r
+               createdDate.setPalette(pal);\r
+               createdTime.setPalette(pal);\r
+               alteredDate.setPalette(pal);\r
+               alteredTime.setPalette(pal);\r
+               subjectDate.setPalette(pal);\r
+               subjectTime.setPalette(pal);\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
@@ -619,6 +656,10 @@ public class BrowserWindow extends QWidget {
                createdDate.setEnabled(!v);\r
                subjectDate.setEnabled(!v);\r
                alteredDate.setEnabled(!v);\r
+               authorText.setEnabled(!v);\r
+               createdTime.setEnabled(!v);\r
+               alteredTime.setEnabled(!v);\r
+               subjectTime.setEnabled(!v);\r
                getBrowser().setEnabled(true);\r
        }\r
        \r
@@ -823,7 +864,15 @@ public class BrowserWindow extends QWidget {
        @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
@@ -1022,11 +1071,40 @@ public class BrowserWindow extends QWidget {
                if (!mime.hasText())\r
                        return;\r
                String text = mime.text();\r
-               clipboard.setText(text);\r
+               clipboard.clear();\r
+               clipboard.setText(text, Mode.Clipboard);\r
                browser.page().triggerAction(WebAction.Paste);\r
-               QApplication.clipboard().setMimeData(mime);\r
-               browser.setFocus();\r
 \r
+               // This is done because pasting into an encryption block\r
+               // can cause multiple cells (which can't happen).  It \r
+               // just goes through the table, extracts the data, & \r
+               // puts it back as one table cell.\r
+               if (insideEncryption) {\r
+                       String js = new String( "function fixEncryption() { "\r
+                                       +"   var selObj = window.getSelection();"\r
+                                       +"   var selRange = selObj.getRangeAt(0);"\r
+                                       +"   var workingNode = window.getSelection().anchorNode;"\r
+                                       +"   while(workingNode != null && workingNode.nodeName.toLowerCase() != 'table') { " \r
+                                       +"           workingNode = workingNode.parentNode;"\r
+                                       +"   } "\r
+                                       +"   workingNode.innerHTML = window.jambi.fixEncryptionPaste(workingNode.innerHTML);"\r
+                                       +"} fixEncryption();");\r
+                       browser.page().mainFrame().evaluateJavaScript(js);\r
+               }\r
+       }\r
+       \r
+       // This basically removes all the table tags and returns just the contents.\r
+       // This is called by JavaScript to fix encryption pastes.\r
+       public String fixEncryptionPaste(String data) {\r
+               data = data.replace("<tbody>", "");\r
+               data = data.replace("</tbody>", "");\r
+               data = data.replace("<tr>", "");\r
+               data = data.replace("</tr>", "");\r
+               data = data.replace("<td>", "");\r
+               data = data.replace("</td>", "<br>");\r
+               data = data.replace("<br><br>", "<br>");\r
+\r
+               return "<tbody><tr><td>"+data+"</td></tr></tbody>";\r
        }\r
        \r
        // insert date/time\r
@@ -1200,7 +1278,9 @@ public class BrowserWindow extends QWidget {
                                "document.execCommand('insertHtml', false, '");\r
                String script_end = new String("');");\r
                String todo = new String(\r
-                               "<input TYPE=\"CHECKBOX\" value=\"false\" onClick=\"value=checked; window.jambi.contentChanged(); \" />");\r
+                               "<input TYPE=\"CHECKBOX\" value=\"false\" " +\r
+                               "onMouseOver=\"style.cursor=\\'hand\\'\" " +\r
+                               "onClick=\"value=checked; window.jambi.contentChanged(); \" />");\r
                browser.page().mainFrame().evaluateJavaScript(\r
                                script_start + todo + script_end);\r
                browser.setFocus();\r
@@ -1256,7 +1336,7 @@ public class BrowserWindow extends QWidget {
                if (text.trim().equalsIgnoreCase(""))\r
                        return;\r
 \r
-               InsertLinkDialog dialog = new InsertLinkDialog();\r
+               InsertLinkDialog dialog = new InsertLinkDialog(insertHyperlink);\r
                if (currentHyperlink != null && currentHyperlink != "") {\r
                        dialog.setUrl(currentHyperlink);\r
                }\r
@@ -1265,8 +1345,12 @@ public class BrowserWindow extends QWidget {
                        logger.log(logger.EXTREME, "Insert link canceled");\r
                        return;\r
                }\r
-               if (browser.insertLinkAction.text().equalsIgnoreCase("Insert Hyperlink")) {\r
+               \r
+               // Take care of inserting new links\r
+               if (insertHyperlink) {\r
                        String selectedText = browser.selectedText();\r
+                       if (dialog.getUrl().trim().equals(""))\r
+                               return;\r
                        logger.log(logger.EXTREME, "Inserting link on text "+selectedText);\r
                        logger.log(logger.EXTREME, "URL Link " +dialog.getUrl().trim());\r
                        String dUrl = StringUtils.replace(dialog.getUrl().trim(), "'", "\\'");\r
@@ -1276,24 +1360,157 @@ public class BrowserWindow extends QWidget {
                        String script = "document.execCommand('insertHtml', false, '"+url+"');";\r
                        browser.page().mainFrame().evaluateJavaScript(script);\r
                        return;\r
+               }\r
+               \r
+               // Edit existing links\r
+               String js = new String( "function getCursorPos() {"\r
+                               +"var cursorPos;"\r
+                               +"if (window.getSelection) {"\r
+                               +"   var selObj = window.getSelection();"\r
+                               +"   var selRange = selObj.getRangeAt(0);"\r
+                               +"   var workingNode = window.getSelection().anchorNode.parentNode;"\r
+                               +"   while(workingNode != null) { " \r
+                               +"      if (workingNode.nodeName.toLowerCase()=='a') workingNode.setAttribute('href','" +dialog.getUrl() +"');"\r
+                               +"      workingNode = workingNode.parentNode;"\r
+                               +"   }"\r
+                               +"}"\r
+                               +"} getCursorPos();");\r
+                       browser.page().mainFrame().evaluateJavaScript(js);\r
+               \r
+               if (!dialog.getUrl().trim().equals("")) {\r
+                       contentChanged();\r
+                       return;\r
+               }\r
+               \r
+               // Remove URL\r
+               js = new String( "function getCursorPos() {"\r
+                               +"var cursorPos;"\r
+                               +"if (window.getSelection) {"\r
+                               +"   var selObj = window.getSelection();"\r
+                               +"   var selRange = selObj.getRangeAt(0);"\r
+                               +"   var workingNode = window.getSelection().anchorNode.parentNode;"\r
+                               +"   while(workingNode != null) { " \r
+                               +"      if (workingNode.nodeName.toLowerCase()=='a') { "\r
+                               +"         workingNode.removeAttribute('href');"\r
+                               +"         workingNode.removeAttribute('title');"\r
+                               +"         var text = document.createTextNode(workingNode.innerText);"\r
+                               +"         workingNode.parentNode.insertBefore(text, workingNode);"\r
+                               +"         workingNode.parentNode.removeChild(workingNode);"\r
+                               +"      }"\r
+                               +"      workingNode = workingNode.parentNode;"\r
+                               +"   }"\r
+                               +"}"\r
+                               +"} getCursorPos();");\r
+                       browser.page().mainFrame().evaluateJavaScript(js);\r
+                       \r
+                       contentChanged();\r
+\r
+               \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
-                       String js = new String( "function getCursorPos() {"\r
-                                       +"var cursorPos;"\r
-                                       +"if (window.getSelection) {"\r
-                                       +"   var selObj = window.getSelection();"\r
-                                       +"   var selRange = selObj.getRangeAt(0);"\r
-                                       +"   var workingNode = window.getSelection().anchorNode.parentNode;"\r
-                                       +"   while(workingNode != null) { " \r
-                                       +"      if (workingNode.nodeName.toLowerCase()=='a') workingNode.setAttribute('href','" +dialog.getUrl() +"');"\r
-                                       +"      workingNode = workingNode.parentNode;"\r
-                                       +"   }"\r
-                                       +"}"\r
-                                       +"} getCursorPos();");\r
-                               browser.page().mainFrame().evaluateJavaScript(js);\r
-                               contentChanged();\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
@@ -1377,7 +1594,6 @@ public class BrowserWindow extends QWidget {
                        plainText = crypt.decrypt(text, Global.passwordRemember.get(i).getFirst(), 64);\r
                        if (plainText != null) {\r
                                slot = new String(Long.toString(l));\r
-                               Pair<String,String> passwordPair = new Pair<String,String>();\r
                                Global.passwordSafe.put(slot, Global.passwordRemember.get(i));\r
                                removeEncryption(id, plainText, false, slot);   \r
                                return;\r
@@ -1420,7 +1636,7 @@ public class BrowserWindow extends QWidget {
        // Modify a note's tags\r
        @SuppressWarnings("unused")\r
        private void modifyTags() {\r
-               TagAssign tagWindow = new TagAssign(allTags, currentTags);\r
+               TagAssign tagWindow = new TagAssign(allTags, currentTags, !conn.getNotebookTable().isLinked(currentNote.getNotebookGuid()));\r
                tagWindow.exec();\r
                if (tagWindow.okClicked()) {\r
                        currentTags.clear();\r
@@ -1496,6 +1712,20 @@ public class BrowserWindow extends QWidget {
                        if (!newTagArray[i].trim().equals(""))\r
                                newTagList.add(newTagArray[i]);\r
 \r
+               if (conn.getNotebookTable().isLinked(currentNote.getNotebookGuid())) {\r
+                       for (int i=newTagList.size()-1; i>=0; i--) {\r
+                               boolean found = false;\r
+                               for (int j=0; j<allTags.size(); j++) {\r
+                                       if (allTags.get(j).getName().equalsIgnoreCase(newTagList.get(i))) {\r
+                                               found = true;\r
+                                               j=allTags.size();\r
+                                       }\r
+                               }\r
+                               if (!found)\r
+                                       newTagList.remove(i);\r
+                       }\r
+               }\r
+\r
                // Let's cleanup the appearance of the tag list\r
                Collections.sort(newTagList);\r
                String newDisplay = "";\r
@@ -1536,19 +1766,110 @@ public class BrowserWindow extends QWidget {
 \r
        // Tab button was pressed\r
        public void tabPressed() {\r
-               if (!insideList) {\r
+               if (insideEncryption)\r
+                       return;\r
+               if (!insideList && !insideTable) {\r
                        String script_start = new String(\r
                        "document.execCommand('insertHtml', false, '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;');");\r
                        browser.page().mainFrame().evaluateJavaScript(script_start);\r
-               } else \r
+                       return;\r
+               }\r
+               if (insideList) {\r
                        indentClicked();\r
+               }\r
+               if (insideTable) {\r
+                       String js = new String( "function getCursorPosition() { "\r
+                                       +"   var selObj = window.getSelection();"\r
+                                       +"   var selRange = selObj.getRangeAt(0);"\r
+                                       +"   var workingNode = window.getSelection().anchorNode;"\r
+                                       +"   var rowCount = 0;"\r
+                                       +"   var colCount = 0;"\r
+                                       +"   while(workingNode != null && workingNode.nodeName.toLowerCase() != 'table') { " \r
+                                       +"      if (workingNode.nodeName.toLowerCase()=='tr') {"\r
+                                       +"         rowCount = rowCount+1;"\r
+                                       +"      }"\r
+                                       +"      if (workingNode.nodeName.toLowerCase() == 'td') {"\r
+                                       +"         colCount = colCount+1;"\r
+                                       +"      }"\r
+                                       +"      if (workingNode.previousSibling != null)"\r
+                                       +"          workingNode = workingNode.previousSibling;"\r
+                                       +"      else "\r
+                                       +"           workingNode = workingNode.parentNode;"\r
+                                       +"   }"\r
+                                       +"   var nodes = workingNode.getElementsByTagName('tr');"\r
+                                       +"   var tableRows = nodes.length;"\r
+                                       +"   nodes = nodes[0].getElementsByTagName('td');"\r
+                                       +"   var tableColumns = nodes.length;"\r
+                                       +"   window.jambi.setTableCursorPositionTab(rowCount, colCount, tableRows, tableColumns);"\r
+                                       +"} getCursorPosition();");\r
+                       browser.page().mainFrame().evaluateJavaScript(js);\r
+               }\r
        }\r
        \r
+       // If a user presses tab from within a table\r
+       public void setTableCursorPositionTab(int currentRow, int currentCol, int tableRows, int tableColumns) {\r
+               if (tableRows == currentRow && currentCol == tableColumns) {\r
+                       insertTableRow();\r
+               }\r
+               KeyboardModifiers modifiers = new KeyboardModifiers(KeyboardModifier.NoModifier);\r
+               QKeyEvent right = new QKeyEvent(Type.KeyPress, Qt.Key.Key_Right.value(), modifiers);\r
+               QKeyEvent end = new QKeyEvent(Type.KeyPress, Qt.Key.Key_End.value(), modifiers);\r
+               QKeyEvent end2 = new QKeyEvent(Type.KeyPress, Qt.Key.Key_End.value(), modifiers);\r
+               getBrowser().focusWidget();\r
+               QCoreApplication.postEvent(getBrowser(), end);\r
+               QCoreApplication.postEvent(getBrowser(), right);\r
+               QCoreApplication.postEvent(getBrowser(), end2);\r
+       }\r
+               \r
        public void backtabPressed() {\r
+               if (insideEncryption) \r
+                       return;\r
                if (insideList)\r
                        outdentClicked();\r
+               if (insideTable) {\r
+                       String js = new String( "function getCursorPosition() { "\r
+                                       +"   var selObj = window.getSelection();"\r
+                                       +"   var selRange = selObj.getRangeAt(0);"\r
+                                       +"   var workingNode = window.getSelection().anchorNode;"\r
+                                       +"   var rowCount = 0;"\r
+                                       +"   var colCount = 0;"\r
+                                       +"   while(workingNode != null && workingNode.nodeName.toLowerCase() != 'table') { " \r
+                                       +"      if (workingNode.nodeName.toLowerCase()=='tr') {"\r
+                                       +"         rowCount = rowCount+1;"\r
+                                       +"      }"\r
+                                       +"      if (workingNode.nodeName.toLowerCase() == 'td') {"\r
+                                       +"         colCount = colCount+1;"\r
+                                       +"      }"\r
+                                       +"      if (workingNode.previousSibling != null)"\r
+                                       +"          workingNode = workingNode.previousSibling;"\r
+                                       +"      else "\r
+                                       +"           workingNode = workingNode.parentNode;"\r
+                                       +"   }"\r
+                                       +"   var nodes = workingNode.getElementsByTagName('tr');"\r
+                                       +"   var tableRows = nodes.length;"\r
+                                       +"   nodes = nodes[0].getElementsByTagName('td');"\r
+                                       +"   var tableColumns = nodes.length;"\r
+                                       +"   window.jambi.setTableCursorPositionBackTab(rowCount, colCount, tableRows, tableColumns);"\r
+                                       +"} getCursorPosition();");\r
+                       browser.page().mainFrame().evaluateJavaScript(js);\r
+                       \r
+               }\r
+       }\r
+       \r
+       // If a user presses backtab from within a table\r
+       public void setTableCursorPositionBackTab(int currentRow, int currentCol, int tableRows, int tableColumns) {\r
+               if (currentRow  == 1 && currentCol == 1) {\r
+                       return;\r
+               }\r
+               KeyboardModifiers modifiers = new KeyboardModifiers(KeyboardModifier.NoModifier);\r
+               QKeyEvent left = new QKeyEvent(Type.KeyPress, Qt.Key.Key_Left.value(), modifiers);\r
+               QKeyEvent home = new QKeyEvent(Type.KeyPress, Qt.Key.Key_Home.value(), modifiers);\r
+               getBrowser().focusWidget();\r
+               QCoreApplication.postEvent(getBrowser(), home);\r
+               QCoreApplication.postEvent(getBrowser(), left);\r
        }\r
        \r
+       \r
        public void setInsideList() {\r
                insideList = true;\r
        }\r
@@ -1632,6 +1953,13 @@ public class BrowserWindow extends QWidget {
                for (int i = 0; i < notebookList.size(); i++) {\r
                        if (n.equals(notebookList.get(i).getName())) {\r
                                if (!notebookList.get(i).getGuid().equals(currentNote.getNotebookGuid())) {\r
+                                       String guid = conn.getNotebookTable().findNotebookByName(n);\r
+                                       if (conn.getNotebookTable().isLinked(guid)) {\r
+                                               tagEdit.setText("");\r
+                                               noteSignal.tagsChanged.emit(currentNote.getGuid(), new ArrayList<String>());\r
+                                               FilterEditorTags t = new FilterEditorTags(conn, logger);\r
+                                               setAllTags(t.getValidTags(currentNote));\r
+                                       }\r
                                        currentNote.setNotebookGuid(notebookList.get(i).getGuid());\r
                                        changed = true;\r
                                }\r
@@ -1724,7 +2052,6 @@ public class BrowserWindow extends QWidget {
                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(&amp." +tfile.fileName() +"&amp.);\""\r
                                + " />");\r
                \r
@@ -2129,6 +2456,8 @@ public class BrowserWindow extends QWidget {
                // Strip URL prefix and base dir\r
                guid = guid.replace("nnres://", "")\r
                        .replace(FileUtils.toForwardSlashedPath(Global.getFileManager().getResDirPath()), "");\r
+               guid = guid.replace("file://", "").replace("/", "")\r
+               .replace(FileUtils.toForwardSlashedPath(Global.getFileManager().getResDirPath()), "");\r
 \r
                pos = guid.lastIndexOf('.');\r
                if (pos > 0)\r
@@ -2278,11 +2607,16 @@ public class BrowserWindow extends QWidget {
                browser.rotateImageLeft.setEnabled(false);\r
                browser.rotateImageRight.setEnabled(false);\r
                browser.insertTableAction.setEnabled(true);\r
+               browser.deleteTableColumnAction.setEnabled(false);\r
                browser.insertTableRowAction.setEnabled(false);\r
+               browser.insertTableColumnAction.setEnabled(false);\r
                browser.deleteTableRowAction.setEnabled(false);\r
                browser.insertLinkAction.setText(tr("Insert Hyperlink"));\r
+               insertHyperlink = true;\r
                currentHyperlink ="";\r
                insideList = false;\r
+               insideTable = false;\r
+               insideEncryption = false;\r
                forceTextPaste = false;\r
                \r
                String js = new String( "function getCursorPos() {"\r
@@ -2293,7 +2627,7 @@ public class BrowserWindow extends QWidget {
                        +"   var workingNode = window.getSelection().anchorNode.parentNode;"\r
                        +"   while(workingNode != null) { " \r
 //                     +"      window.jambi.printNode(workingNode.nodeName);"\r
-                       +"      if (workingNode.nodeName=='TABLE') { if (workingNode.getAttribute('class').toLowerCase() == 'en-crypt-temp') window.jambi.forceTextPaste(); }"\r
+                       +"      if (workingNode.nodeName=='TABLE') { if (workingNode.getAttribute('class').toLowerCase() == 'en-crypt-temp') window.jambi.insideEncryption(); }"\r
                        +"      if (workingNode.nodeName=='B') window.jambi.boldActive();"\r
                        +"      if (workingNode.nodeName=='I') window.jambi.italicActive();"\r
                        +"      if (workingNode.nodeName=='U') window.jambi.underlineActive();"\r
@@ -2316,6 +2650,10 @@ public class BrowserWindow extends QWidget {
                System.out.println("Node Vaule: " +n);\r
        }\r
        \r
+       public void insideEncryption() {\r
+               insideEncryption = true;\r
+               forceTextPaste();\r
+       }\r
        \r
        //****************************************************************\r
        //* Insert a table row\r
@@ -2345,8 +2683,34 @@ public class BrowserWindow extends QWidget {
                browser.page().mainFrame().evaluateJavaScript(js);\r
                contentChanged();\r
        }\r
+       \r
+       public void insertTableColumn() {\r
+               String js = new String( "function insertTableColumn() {"\r
+                               +"   var selObj = window.getSelection();"\r
+                               +"   var selRange = selObj.getRangeAt(0);"\r
+                               +"   var workingNode = window.getSelection().anchorNode.parentNode;"\r
+                               +"   var current = 0;"\r
+                               +"   while (workingNode.nodeName.toLowerCase() != 'table' && workingNode != null) {"\r
+                               +"       if (workingNode.nodeName.toLowerCase() == 'td') {"\r
+                               +"          var td = workingNode;"\r
+                               +"          while (td.previousSibling != null) { " \r
+                               +"             current = current+1; td = td.previousSibling;"\r
+                               +"          }"\r
+                               +"       }"\r
+                               +"       workingNode = workingNode.parentNode; "\r
+                               +"   }"\r
+                               +"   if (workingNode == null) return;"\r
+                               +"   for (var i=0; i<workingNode.rows.length; i++) { " \r
+                               +"      var cell = workingNode.rows[i].insertCell(current+1); "                 \r
+                               +"      cell.innerHTML = '&nbsp'; "\r
+                               +"   }"\r
+                               +"} insertTableColumn();");\r
+                       browser.page().mainFrame().evaluateJavaScript(js);\r
+                       contentChanged();\r
+       }\r
+       \r
        //****************************************************************\r
-       //* Insert a table row\r
+       //* Delete a table row\r
        //****************************************************************\r
        public void deleteTableRow() {\r
                \r
@@ -2366,16 +2730,46 @@ public class BrowserWindow extends QWidget {
                browser.page().mainFrame().evaluateJavaScript(js);\r
                contentChanged();\r
        }\r
+\r
+       public void deleteTableColumn() {\r
+               String js = new String( "function deleteTableColumn() {"\r
+                               +"   var selObj = window.getSelection();"\r
+                               +"   var selRange = selObj.getRangeAt(0);"\r
+                               +"   var workingNode = window.getSelection().anchorNode.parentNode;"\r
+                               +"   var current = 0;"\r
+                               +"   while (workingNode.nodeName.toLowerCase() != 'table' && workingNode != null) {"\r
+                               +"       if (workingNode.nodeName.toLowerCase() == 'td') {"\r
+                               +"          var td = workingNode;"\r
+                               +"          while (td.previousSibling != null) { " \r
+                               +"             current = current+1; td = td.previousSibling;"\r
+                               +"          }"\r
+                               +"       }"\r
+                               +"       workingNode = workingNode.parentNode; "\r
+                               +"   }"\r
+                               +"   if (workingNode == null) return;"\r
+                               +"   for (var i=0; i<workingNode.rows.length; i++) { " \r
+                               +"      workingNode.rows[i].deleteCell(current); "                      \r
+                               +"   }"\r
+                               +"} deleteTableColumn();");\r
+                       browser.page().mainFrame().evaluateJavaScript(js);\r
+                       contentChanged();\r
+       }\r
+       \r
+       \r
        public void setInsideTable() {\r
                browser.insertTableRowAction.setEnabled(true);\r
+               browser.insertTableColumnAction.setEnabled(true);\r
                browser.deleteTableRowAction.setEnabled(true);\r
+               browser.deleteTableColumnAction.setEnabled(true);\r
                browser.insertTableAction.setEnabled(false);\r
                browser.encryptAction.setEnabled(false);\r
+               insideTable = true;\r
        }\r
        \r
        public void setInsideLink(String link) {\r
                browser.insertLinkAction.setText(tr("Edit Hyperlink"));\r
                currentHyperlink = link;\r
+               insertHyperlink = false;\r
        }\r
        \r
        public void italicActive() {\r
@@ -2410,7 +2804,7 @@ public class BrowserWindow extends QWidget {
                browser.setHtml(browser.page().mainFrame().toHtml());\r
                browser.reload();\r
                contentChanged();\r
-//             resourceSignal.contentChanged.emit(selectedFile);\r
+               resourceSignal.contentChanged.emit(selectedFile);\r
 \r
        }\r
        public void rotateImageLeft() {\r
@@ -2422,15 +2816,21 @@ public class BrowserWindow extends QWidget {
                browser.setHtml(browser.page().mainFrame().toHtml());\r
                browser.reload();\r
                contentChanged();\r
-//             resourceSignal.contentChanged.emit(selectedFile);\r
+               resourceSignal.contentChanged.emit(selectedFile);\r
        }\r
        public void resourceContextMenu(String f) {\r
                browser.downloadAttachment.setEnabled(true);\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
@@ -2615,94 +3015,117 @@ public class BrowserWindow extends QWidget {
        }\r
 */\r
        \r
+       @SuppressWarnings("unused")\r
        private void toggleUndoVisible(Boolean toggle) {\r
                undoAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("undo", toggle);\r
        }\r
+       @SuppressWarnings("unused")\r
        private void toggleRedoVisible(Boolean toggle) {\r
                redoAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("redo", toggle);\r
        }\r
+       @SuppressWarnings("unused")\r
        private void toggleCutVisible(Boolean toggle) {\r
                cutAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("cut", toggle);\r
        }\r
+       @SuppressWarnings("unused")\r
        private void toggleCopyVisible(Boolean toggle) {\r
                copyAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("copy", toggle);\r
        }\r
+       @SuppressWarnings("unused")\r
        private void togglePasteVisible(Boolean toggle) {\r
                pasteAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("paste", toggle);\r
        }\r
+       @SuppressWarnings("unused")\r
        private void toggleBoldVisible(Boolean toggle) {\r
                boldAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("bold", toggle);\r
        }\r
+       @SuppressWarnings("unused")\r
        private void toggleItalicVisible(Boolean toggle) {\r
                italicAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("italic", toggle);\r
        }\r
+       @SuppressWarnings("unused")\r
        private void toggleUnderlineVisible(Boolean toggle) {\r
                underlineAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("underline", toggle);\r
        }\r
+       @SuppressWarnings("unused")\r
        private void toggleStrikethroughVisible(Boolean toggle) {\r
                strikethroughAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("strikethrough", toggle);\r
        }\r
+       @SuppressWarnings("unused")\r
        private void toggleLeftAlignVisible(Boolean toggle) {\r
                leftAlignAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("alignLeft", toggle);\r
        }\r
+       @SuppressWarnings("unused")\r
        private void toggleRightAlignVisible(Boolean toggle) {\r
                rightAlignAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("alignRight", toggle);\r
        }       \r
+       @SuppressWarnings("unused")\r
        private void toggleCenterAlignVisible(Boolean toggle) {\r
                centerAlignAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("alignCenter", toggle);\r
        }\r
+       @SuppressWarnings("unused")\r
        private void toggleHLineVisible(Boolean toggle) {\r
                hlineAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("hline", toggle);\r
        }\r
+       @SuppressWarnings("unused")\r
        private void toggleIndentVisible(Boolean toggle) {\r
                indentAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("indent", toggle);\r
        }\r
+       @SuppressWarnings("unused")\r
        private void toggleTodoVisible(Boolean toggle) {\r
                todoAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("todo", toggle);\r
        }\r
+       @SuppressWarnings("unused")\r
        private void toggleOutdentVisible(Boolean toggle) {\r
                outdentAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("outdent", toggle);\r
        }\r
+       @SuppressWarnings("unused")\r
        private void toggleBulletListVisible(Boolean toggle) {\r
                bulletListAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("bulletList", toggle);\r
        }\r
+       @SuppressWarnings("unused")\r
        private void toggleNumberListVisible(Boolean toggle) {\r
                numberListAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("numberList", toggle);\r
        }\r
+       @SuppressWarnings("unused")\r
        private void toggleFontListVisible(Boolean toggle) {\r
                fontListAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("font", toggle);\r
        }\r
+       @SuppressWarnings("unused")\r
        private void toggleFontColorVisible(Boolean toggle) {\r
                fontColorAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("fontColor", toggle);\r
        }\r
+       @SuppressWarnings("unused")\r
        private void toggleFontSizeVisible(Boolean toggle) {\r
                fontSizeAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("fontSize", toggle);\r
        }\r
+       @SuppressWarnings("unused")\r
        private void toggleFontHilightVisible(Boolean toggle) {\r
                fontHilightAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("fontHilight", toggle);\r
        }\r
+       @SuppressWarnings("unused")\r
        private void toggleSpellCheckVisible(Boolean toggle) {\r
                spellCheckAction.setVisible(toggle);\r
                Global.saveEditorButtonsVisible("spellCheck", toggle);\r
@@ -2748,6 +3171,7 @@ public class BrowserWindow extends QWidget {
        }\r
        \r
        // Invoke spell checker dialog\r
+       @SuppressWarnings("unused")\r
        private void spellCheckClicked() {\r
 \r
                if (spellChecker == null) {\r