OSDN Git Service

2bed87fd8f0ec9d186960f6c4c04507c0aefd4f4
[neighbornote/NeighborNote.git] / src / cx / fbn / nevernote / gui / BrowserWindow.java
1 /*
2  * This file is part of NixNote/NeighborNote 
3  * Copyright 2009 Randy Baumgarte
4  * Copyright 2013 Yuki Takahashi
5  * 
6  * This file may be licensed under the terms of of the
7  * GNU General Public License Version 2 (the ``GPL'').
8  *
9  * Software distributed under the License is distributed
10  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
11  * express or implied. See the GPL for the specific language
12  * governing rights and limitations.
13  *
14  * You should have received a copy of the GPL along with this
15  * program. If not, go to http://www.gnu.org/licenses/gpl.html
16  * or write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  */
20
21 package cx.fbn.nevernote.gui;
22
23 import java.io.File;
24 import java.io.FileNotFoundException;
25 import java.io.IOException;
26 import java.net.FileNameMap;
27 import java.net.URI;
28 import java.net.URLConnection;
29 import java.security.MessageDigest;
30 import java.security.NoSuchAlgorithmException;
31 import java.text.SimpleDateFormat;
32 import java.util.ArrayList;
33 import java.util.Calendar;
34 import java.util.Collections;
35 import java.util.Date;
36 import java.util.GregorianCalendar;
37 import java.util.HashMap;
38 import java.util.List;
39 import java.util.Locale;
40 import java.util.StringTokenizer;
41
42 import org.apache.commons.lang3.StringEscapeUtils;
43 import org.apache.commons.lang3.StringUtils;
44
45 import com.evernote.edam.limits.Constants;
46 import com.evernote.edam.type.Data;
47 import com.evernote.edam.type.Note;
48 import com.evernote.edam.type.Notebook;
49 import com.evernote.edam.type.Resource;
50 import com.evernote.edam.type.ResourceAttributes;
51 import com.evernote.edam.type.Tag;
52 import com.evernote.edam.type.User;
53 import com.swabunga.spell.engine.Configuration;
54 import com.swabunga.spell.engine.SpellDictionary;
55 import com.swabunga.spell.engine.SpellDictionaryHashMap;
56 import com.swabunga.spell.engine.Word;
57 import com.swabunga.spell.event.SpellCheckEvent;
58 import com.swabunga.spell.event.SpellCheckListener;
59 import com.swabunga.spell.event.SpellChecker;
60 import com.swabunga.spell.event.StringWordTokenizer;
61 import com.trolltech.qt.core.QByteArray;
62 import com.trolltech.qt.core.QCoreApplication;
63 import com.trolltech.qt.core.QDataStream;
64 import com.trolltech.qt.core.QDateTime;
65 import com.trolltech.qt.core.QEvent;
66 import com.trolltech.qt.core.QEvent.Type;
67 import com.trolltech.qt.core.QFile;
68 import com.trolltech.qt.core.QFileSystemWatcher;
69 import com.trolltech.qt.core.QIODevice;
70 import com.trolltech.qt.core.QMimeData;
71 import com.trolltech.qt.core.QSize;
72 import com.trolltech.qt.core.QTextCodec;
73 import com.trolltech.qt.core.QTimer;
74 import com.trolltech.qt.core.QUrl;
75 import com.trolltech.qt.core.Qt;
76 import com.trolltech.qt.core.Qt.Key;
77 import com.trolltech.qt.core.Qt.KeyboardModifier;
78 import com.trolltech.qt.core.Qt.KeyboardModifiers;
79 import com.trolltech.qt.gui.QAction;
80 import com.trolltech.qt.gui.QApplication;
81 import com.trolltech.qt.gui.QCalendarWidget;
82 import com.trolltech.qt.gui.QClipboard;
83 import com.trolltech.qt.gui.QClipboard.Mode;
84 import com.trolltech.qt.gui.QColor;
85 import com.trolltech.qt.gui.QComboBox;
86 import com.trolltech.qt.gui.QDateEdit;
87 import com.trolltech.qt.gui.QDesktopServices;
88 import com.trolltech.qt.gui.QFileDialog;
89 import com.trolltech.qt.gui.QFileDialog.AcceptMode;
90 import com.trolltech.qt.gui.QFileDialog.FileMode;
91 import com.trolltech.qt.gui.QFont;
92 import com.trolltech.qt.gui.QFontDatabase;
93 import com.trolltech.qt.gui.QFormLayout;
94 import com.trolltech.qt.gui.QGridLayout;
95 import com.trolltech.qt.gui.QHBoxLayout;
96 import com.trolltech.qt.gui.QIcon;
97 import com.trolltech.qt.gui.QImage;
98 import com.trolltech.qt.gui.QKeyEvent;
99 import com.trolltech.qt.gui.QKeySequence;
100 import com.trolltech.qt.gui.QLabel;
101 import com.trolltech.qt.gui.QLineEdit;
102 import com.trolltech.qt.gui.QListWidgetItem;
103 import com.trolltech.qt.gui.QMatrix;
104 import com.trolltech.qt.gui.QMessageBox;
105 import com.trolltech.qt.gui.QPalette;
106 import com.trolltech.qt.gui.QPalette.ColorRole;
107 import com.trolltech.qt.gui.QPushButton;
108 import com.trolltech.qt.gui.QShortcut;
109 import com.trolltech.qt.gui.QSizePolicy;
110 import com.trolltech.qt.gui.QSplitter;
111 import com.trolltech.qt.gui.QTextEdit;
112 import com.trolltech.qt.gui.QTextEdit.LineWrapMode;
113 import com.trolltech.qt.gui.QTimeEdit;
114 import com.trolltech.qt.gui.QToolButton;
115 import com.trolltech.qt.gui.QToolButton.ToolButtonPopupMode;
116 import com.trolltech.qt.gui.QVBoxLayout;
117 import com.trolltech.qt.gui.QWidget;
118 import com.trolltech.qt.network.QNetworkAccessManager;
119 import com.trolltech.qt.network.QNetworkReply;
120 import com.trolltech.qt.network.QNetworkReply.NetworkError;
121 import com.trolltech.qt.network.QNetworkRequest;
122 import com.trolltech.qt.webkit.QWebPage;
123 import com.trolltech.qt.webkit.QWebPage.WebAction;
124 import com.trolltech.qt.webkit.QWebSettings;
125 import com.trolltech.qt.webkit.QWebView;
126
127 import cx.fbn.nevernote.Global;
128 import cx.fbn.nevernote.clipboard.ClipBoardObserver;
129 import cx.fbn.nevernote.dialog.EnCryptDialog;
130 import cx.fbn.nevernote.dialog.EnDecryptDialog;
131 import cx.fbn.nevernote.dialog.GeoDialog;
132 import cx.fbn.nevernote.dialog.InsertLatexImage;
133 import cx.fbn.nevernote.dialog.InsertLinkDialog;
134 import cx.fbn.nevernote.dialog.NoteQuickLinkDialog;
135 import cx.fbn.nevernote.dialog.SpellCheck;
136 import cx.fbn.nevernote.dialog.TableDialog;
137 import cx.fbn.nevernote.dialog.TagAssign;
138 import cx.fbn.nevernote.evernote.EnCrypt;
139 import cx.fbn.nevernote.filters.FilterEditorTags;
140 import cx.fbn.nevernote.signals.NoteResourceSignal;
141 import cx.fbn.nevernote.signals.NoteSignal;
142 import cx.fbn.nevernote.sql.DatabaseConnection;
143 import cx.fbn.nevernote.utilities.ApplicationLogger;
144 import cx.fbn.nevernote.utilities.FileUtils;
145 import cx.fbn.nevernote.utilities.Pair;
146 import cx.fbn.nevernote.xml.HtmlTagModifier;
147
148 public class BrowserWindow extends QWidget {
149
150         public final QLineEdit titleLabel;
151         private final QLineEdit urlText;
152         private final QLabel authorLabel;
153         private final QLineEdit authorText;
154         private final QComboBox geoBox;
155         public final TagLineEdit tagEdit;
156         public final QLabel tagLabel;
157         private final QPushButton urlLabel;
158         private final QLabel alteredLabel;
159         private final QDateEdit alteredDate;
160         private final QTimeEdit alteredTime;
161         private final QDateEdit createdDate;
162         private final QTimeEdit createdTime;
163         private final QLabel subjectLabel;
164         private final QDateEdit subjectDate;
165         private final QTimeEdit subjectTime;
166         public final QComboBox notebookBox;
167         private final QLabel notebookLabel;
168         private final QLabel createdLabel;
169         public final QComboBox fontSize;
170         public final QAction    fontSizeAction;
171         private boolean extendedOn;
172         public boolean buttonsVisible;
173         private final String iconPath;
174         public final ContentView browser;
175         private final QTextEdit sourceEdit;
176         private String sourceEditHeader;
177         Highlighter syntaxHighlighter;
178         private List<Tag> allTags;
179         private List<String> currentTags;
180         public NoteSignal noteSignal;
181         public Signal2<String,String> evernoteLinkClicked;
182         private List<Notebook> notebookList;
183         private Note currentNote;
184         private String saveNoteTitle;
185         private String saveTagList;
186         private boolean insideList;
187         private final DatabaseConnection conn;
188         private final QCalendarWidget createdCalendarWidget;
189         private final QCalendarWidget alteredCalendarWidget;
190         private final QCalendarWidget subjectCalendarWidget;
191
192         public final QPushButton undoButton;
193         public final QAction    undoAction;
194         public final QPushButton redoButton;
195         public final QAction    redoAction;
196         public final QPushButton cutButton;
197         public final QAction    cutAction;
198         public final QPushButton copyButton;
199         public final QAction    copyAction;
200         public final QPushButton pasteButton;
201         public final QAction    pasteAction;
202         public final QPushButton boldButton;
203         public final QAction    boldAction;
204         public final QPushButton underlineButton;
205         public final QAction    underlineAction;
206         public final QPushButton italicButton;
207         public final QAction    italicAction;
208         public final Signal0 focusLost;
209         public final NoteResourceSignal resourceSignal;
210
211         public QPushButton rightAlignButton;
212         public final QAction    rightAlignAction;
213         public QPushButton leftAlignButton;
214         public final QAction    leftAlignAction;
215         public QPushButton centerAlignButton;
216         public final QAction    centerAlignAction;
217
218         public final QPushButton strikethroughButton;
219         public final QAction    strikethroughAction;
220         public final QPushButton hlineButton;
221         public final QAction    hlineAction;
222         public final QPushButton indentButton;
223         public final QAction    indentAction;
224         public final QPushButton outdentButton;
225         public final QAction    outdentAction;
226         public final QPushButton bulletListButton;
227         public final QAction    bulletListAction;
228         public final QPushButton numberListButton;
229         public final QAction    numberListAction;
230         public final QPushButton spellCheckButton;
231         public final QAction    spellCheckAction;
232         public final QPushButton todoButton;
233         public final QAction    todoAction;
234
235         public final QShortcut focusTitleShortcut;
236         public final QShortcut focusTagShortcut;
237         public final QShortcut focusNoteShortcut;
238         public final QShortcut focusUrlShortcut;
239         public final QShortcut focusAuthorShortcut;
240         
241         public EditorButtonBar buttonLayout;
242         public final QComboBox fontList;
243         public final QAction    fontListAction;
244         public final QToolButton fontColor;
245         public final QAction    fontColorAction;
246         private final ColorMenu fontColorMenu;
247         public final QToolButton fontHilight;
248         public final QAction    fontHilightAction;
249         private final ColorMenu fontHilightColorMenu;
250         public final QFileSystemWatcher fileWatcher;
251         public int cursorPosition;
252         private boolean forceTextPaste;
253         private String selectedFile;
254         private String currentHyperlink;
255         public boolean keepPDFNavigationHidden;
256         private final ApplicationLogger logger;
257         SpellDictionary dictionary;
258     SpellDictionary userDictionary;
259     SpellChecker spellChecker;
260     SuggestionListener spellListener;
261         private final HashMap<String,Integer> previewPageList;  
262         boolean insertHyperlink;
263         boolean insideTable;
264         boolean insideEncryption;
265         public Signal1<BrowserWindow> blockApplication;
266         public Signal0 unblockApplication;
267         public boolean awaitingHttpResponse;
268         public long     unblockTime;
269         private final QTimer setSourceTimer;
270         String latexGuid;  // This is set if we are editing an existing LaTeX formula.  Useful to track guid.
271
272         private final ClipBoardObserver cbObserver;
273         
274         public static class SuggestionListener implements SpellCheckListener {
275                 public boolean abortSpellCheck = false;
276                 public boolean errorsFound = false;
277                 private final SpellCheck                spellCheckDialog;
278                 
279                 
280                 private final BrowserWindow parent;
281                 public SuggestionListener(BrowserWindow parent, SpellChecker checker) {
282                         this.parent = parent;
283                         spellCheckDialog = new SpellCheck(checker);
284                 }
285                 public void spellingError(SpellCheckEvent event) {
286                         errorsFound = true;
287                         spellCheckDialog.setWord(event.getInvalidWord());
288
289                     @SuppressWarnings("unchecked")
290                         List<Word> suggestions = event.getSuggestions();
291                     spellCheckDialog.clearSuggestions();
292                     if (!suggestions.isEmpty()) {
293 //                     spellCheckDialog.setCurrentSuggestion(suggestions.get(0).getWord());
294                        for (int i=0; i<suggestions.size(); i++) {
295                           spellCheckDialog.addSuggestion(suggestions.get(i).getWord());
296                        }
297                        spellCheckDialog.setSelectedSuggestion(0);
298                     }
299                     spellCheckDialog.exec();
300                     if (spellCheckDialog.cancelPressed()) {
301                         abortSpellCheck = true;
302                         event.cancel();
303                         return;
304                     }
305                     if (spellCheckDialog.replacePressed()) {
306                         QClipboard clipboard = QApplication.clipboard();
307                         clipboard.setText(spellCheckDialog.getReplacementWord()); 
308                         parent.pasteClicked();
309                     }
310                     event.cancel();
311                  }
312         }
313
314         
315         // 引数にcbObserverを追加
316         public BrowserWindow(DatabaseConnection c, ClipBoardObserver cbObserver) {
317                 logger = new ApplicationLogger("browser.log");
318                 logger.log(logger.HIGH, "Setting up browser");
319                 iconPath = new String("classpath:cx/fbn/nevernote/icons/");
320                 forceTextPaste = false;
321                 insertHyperlink = true;
322                 insideTable = false;
323                 insideEncryption = false;
324                 
325                 fileWatcher = new QFileSystemWatcher();
326 //              fileWatcher.fileChanged.connect(this, "fileChanged(String)");
327                 noteSignal = new NoteSignal();
328                 titleLabel = new QLineEdit();
329                 evernoteLinkClicked = new Signal2<String,String>();
330                 titleLabel.setMaxLength(Constants.EDAM_NOTE_TITLE_LEN_MAX);
331                 urlText = new QLineEdit();
332                 authorText = new QLineEdit();
333                 geoBox = new QComboBox();
334                 urlLabel = new QPushButton();
335                 urlLabel.clicked.connect(this, "sourceUrlClicked()");
336                 authorLabel = new QLabel();
337                 conn = c;
338                 
339                 this.cbObserver = cbObserver;
340                 
341                 focusLost = new Signal0();
342
343                 tagEdit = new TagLineEdit(allTags);
344                 tagLabel = new QLabel(tr("Tags:"));
345                 tagEdit.focusLost.connect(this, "modifyTagsTyping()");
346
347                 createdCalendarWidget = new QCalendarWidget();
348                 createdDate = new QDateEdit();
349                 createdDate.setDisplayFormat(Global.getDateFormat());
350                 createdDate.setCalendarPopup(true);
351                 createdDate.setCalendarWidget(createdCalendarWidget);
352                 createdTime = new QTimeEdit();
353                 createdDate.dateChanged.connect(this, "createdChanged()");
354                 createdTime.timeChanged.connect(this, "createdChanged()");
355
356                 alteredCalendarWidget = new QCalendarWidget();
357                 alteredDate = new QDateEdit();
358                 alteredDate.setDisplayFormat(Global.getDateFormat());
359                 alteredDate.setCalendarPopup(true);
360                 alteredDate.setCalendarWidget(alteredCalendarWidget);
361                 alteredTime = new QTimeEdit();
362                 alteredLabel = new QLabel(tr("Altered:"));
363                 alteredDate.dateChanged.connect(this, "alteredChanged()");
364                 alteredTime.timeChanged.connect(this, "alteredChanged()");
365
366                 subjectCalendarWidget = new QCalendarWidget();
367                 subjectDate = new QDateEdit();
368                 subjectDate.setDisplayFormat(Global.getDateFormat());
369                 subjectDate.setCalendarPopup(true);
370                 subjectDate.setCalendarWidget(subjectCalendarWidget);
371                 subjectTime = new QTimeEdit();
372                 subjectLabel = new QLabel(tr("Subject Date:"));
373                 subjectDate.dateChanged.connect(this, "subjectDateTimeChanged()");
374                 subjectTime.timeChanged.connect(this, "subjectDateTimeChanged()");
375                 authorText.textChanged.connect(this, "authorChanged()");
376                 urlText.textChanged.connect(this, "sourceUrlChanged()");
377
378                 notebookBox = new QComboBox();
379                 notebookLabel = new QLabel(tr("Notebook"));
380                 createdLabel = new QLabel(tr("Created:"));
381                 // selectedText = new String();
382
383                 urlLabel.setVisible(false);
384                 urlText.setVisible(false);
385                 authorLabel.setVisible(false);
386                 
387                 geoBox.setVisible(false);
388                 geoBox.addItem(new QIcon(iconPath+"globe.png"), "");
389                 geoBox.addItem(new String(tr("Set")));
390                 geoBox.addItem(new String(tr("Clear")));
391                 geoBox.addItem(new String(tr("View On Map")));
392                 geoBox.activated.connect(this, "geoBoxChanged()");
393                 
394                 authorText.setVisible(false);
395                 createdDate.setVisible(false);
396                 alteredLabel.setVisible(false);
397                 //notebookBox.setVisible(false);
398                 notebookLabel.setVisible(false);
399                 createdLabel.setVisible(false);
400                 createdTime.setVisible(false);
401                 alteredDate.setVisible(false);
402                 alteredTime.setVisible(false);
403                 subjectLabel.setVisible(false);
404                 subjectDate.setVisible(false);
405                 subjectTime.setVisible(false);
406                 extendedOn = false;
407                 buttonsVisible = true;
408                 setAcceptDrops(true);
409
410                 browser = new ContentView(this);
411                                 
412                 browser.page().setLinkDelegationPolicy(
413                                 QWebPage.LinkDelegationPolicy.DelegateAllLinks);
414                 browser.linkClicked.connect(this, "linkClicked(QUrl)");
415                 currentHyperlink = "";
416                 
417                 //Setup the source editor
418                 sourceEdit = new QTextEdit(this);
419                 sourceEdit.setVisible(false);
420                 sourceEdit.setTabChangesFocus(true);
421                 sourceEdit.setLineWrapMode(LineWrapMode.NoWrap);
422                 QFont font = new QFont();
423                 font.setFamily("Courier");
424                 font.setFixedPitch(true);
425                 font.setPointSize(10);
426                 sourceEdit.setFont(font);
427                 syntaxHighlighter = new Highlighter(sourceEdit.document());
428                 sourceEdit.textChanged.connect(this, "sourceEdited()");
429
430                 QVBoxLayout v = new QVBoxLayout();
431                 QFormLayout notebookLayout = new QFormLayout();
432                 QGridLayout dateLayout = new QGridLayout();
433                 titleLabel.setReadOnly(false);
434                 titleLabel.editingFinished.connect(this, "titleEdited()");
435                 browser.page().contentsChanged.connect(this, "contentChanged()");
436                 browser.page().selectionChanged.connect(this, "selectionChanged()");
437                 browser.page().mainFrame().javaScriptWindowObjectCleared.connect(this,
438                                 "exposeToJavascript()");
439
440                 notebookBox.activated.connect(this, "notebookChanged()");
441                 resourceSignal = new NoteResourceSignal();
442                 
443                 QHBoxLayout tagLayout = new QHBoxLayout();
444                 v.addWidget(titleLabel, 0);
445                 notebookLayout.addRow(notebookLabel, notebookBox);
446                 tagLayout.addLayout(notebookLayout, 0);
447                 tagLayout.stretch(4);
448                 tagLayout.addWidget(tagLabel, 0);
449                 tagLayout.addWidget(tagEdit, 1);
450                 v.addLayout(tagLayout);
451
452                 QHBoxLayout urlLayout = new QHBoxLayout();
453                 urlLayout.addWidget(urlLabel, 0);
454                 urlLayout.addWidget(urlText, 0);
455                 v.addLayout(urlLayout);
456
457                 QHBoxLayout authorLayout = new QHBoxLayout();
458                 authorLayout.addWidget(authorLabel, 0);
459                 authorLayout.addWidget(authorText, 0);
460                 authorLayout.addWidget(geoBox);
461                 v.addLayout(authorLayout);
462
463                 dateLayout.addWidget(createdLabel, 0, 0);
464                 dateLayout.addWidget(createdDate, 0, 1);
465                 dateLayout.addWidget(createdTime, 0, 2);
466                 dateLayout.setColumnStretch(9, 100);
467                 dateLayout.addWidget(alteredLabel, 0, 3);
468                 dateLayout.addWidget(alteredDate, 0, 4);
469                 dateLayout.addWidget(alteredTime, 0, 5);
470                 dateLayout.addWidget(subjectLabel, 0, 6);
471                 dateLayout.addWidget(subjectDate, 0, 7);
472                 dateLayout.addWidget(subjectTime, 0, 8);
473                 v.addLayout(dateLayout, 0);
474
475                 undoButton = newEditorButton("undo", tr("Undo Change"));
476                 redoButton = newEditorButton("redo", tr("Redo Change"));
477                 cutButton = newEditorButton("cut", tr("Cut"));
478                 copyButton = newEditorButton("copy", tr("Copy"));
479                 pasteButton = newEditorButton("paste", tr("Paste"));
480                 boldButton = newEditorButton("bold", tr("Bold"));
481                 underlineButton = newEditorButton("underline", tr("Underline"));
482                 italicButton = newEditorButton("italic", tr("Italic"));
483
484                 rightAlignButton = newEditorButton("justifyRight", tr("Right Align"));
485                 leftAlignButton = newEditorButton("justifyLeft", tr("Left Align"));
486                 centerAlignButton = newEditorButton("justifyCenter", tr("Center Align"));
487
488                 strikethroughButton = newEditorButton("strikethrough", tr("Strikethrough"));
489                 hlineButton = newEditorButton("hline", tr("Insert Horizontal Line"));
490                 indentButton = newEditorButton("indent", tr("Shift Right"));
491                 outdentButton = newEditorButton("outdent", tr("Shift Left"));
492                 bulletListButton = newEditorButton("bulletList", tr("Bullet List"));
493                 numberListButton = newEditorButton("numberList", tr("Number List"));
494                 spellCheckButton = newEditorButton("spellCheck", tr("Spell Check"));
495                 todoButton = newEditorButton("todo", tr("To-do"));
496
497                 
498                 buttonLayout = new EditorButtonBar();
499                 v.addWidget(buttonLayout);
500                 
501                 undoAction = buttonLayout.addWidget(undoButton);
502                 buttonLayout.toggleUndoVisible.triggered.connect(this, "toggleUndoVisible(Boolean)");
503                 redoAction = buttonLayout.addWidget(redoButton);
504                 buttonLayout.toggleRedoVisible.triggered.connect(this, "toggleRedoVisible(Boolean)");
505                 
506                 buttonLayout.addWidget(newSeparator());
507                 cutAction = buttonLayout.addWidget(cutButton);
508                 buttonLayout.toggleCutVisible.triggered.connect(this, "toggleCutVisible(Boolean)");
509                 copyAction = buttonLayout.addWidget(copyButton);
510                 buttonLayout.toggleCopyVisible.triggered.connect(this, "toggleCopyVisible(Boolean)");
511                 pasteAction = buttonLayout.addWidget(pasteButton);
512                 buttonLayout.togglePasteVisible.triggered.connect(this, "togglePasteVisible(Boolean)");
513
514                 buttonLayout.addWidget(newSeparator());
515                 boldAction = buttonLayout.addWidget(boldButton);
516                 buttonLayout.toggleBoldVisible.triggered.connect(this, "toggleBoldVisible(Boolean)");
517                 italicAction = buttonLayout.addWidget(italicButton);
518                 buttonLayout.toggleItalicVisible.triggered.connect(this, "toggleItalicVisible(Boolean)");
519                 underlineAction = buttonLayout.addWidget(underlineButton);
520                 buttonLayout.toggleUnderlineVisible.triggered.connect(this, "toggleUnderlineVisible(Boolean)");
521                 strikethroughAction = buttonLayout.addWidget(strikethroughButton);
522                 buttonLayout.toggleStrikethroughVisible.triggered.connect(this, "toggleStrikethroughVisible(Boolean)");
523
524                 
525                 buttonLayout.addWidget(newSeparator());
526                 leftAlignAction = buttonLayout.addWidget(leftAlignButton);
527                 buttonLayout.toggleLeftAlignVisible.triggered.connect(this, "toggleLeftAlignVisible(Boolean)");
528                 centerAlignAction = buttonLayout.addWidget(centerAlignButton);
529                 buttonLayout.toggleCenterAlignVisible.triggered.connect(this, "toggleCenterAlignVisible(Boolean)");
530                 rightAlignAction = buttonLayout.addWidget(rightAlignButton);
531                 buttonLayout.toggleRightAlignVisible.triggered.connect(this, "toggleRightAlignVisible(Boolean)");
532
533                 buttonLayout.addWidget(newSeparator());
534                 hlineAction = buttonLayout.addWidget(hlineButton);
535                 buttonLayout.toggleHLineVisible.triggered.connect(this, "toggleHLineVisible(Boolean)");
536
537                 indentAction = buttonLayout.addWidget(indentButton);
538                 buttonLayout.toggleIndentVisible.triggered.connect(this, "toggleIndentVisible(Boolean)");
539                 outdentAction = buttonLayout.addWidget(outdentButton);
540                 buttonLayout.toggleOutdentVisible.triggered.connect(this, "toggleOutdentVisible(Boolean)");
541                 bulletListAction = buttonLayout.addWidget(bulletListButton);
542                 buttonLayout.toggleBulletListVisible.triggered.connect(this, "toggleBulletListVisible(Boolean)");
543                 numberListAction = buttonLayout.addWidget(numberListButton);
544                 buttonLayout.toggleNumberListVisible.triggered.connect(this, "toggleNumberListVisible(Boolean)");
545
546                 // Setup the font & font size combo boxes
547                 buttonLayout.addWidget(newSeparator());
548                 fontList = new QComboBox();
549                 fontSize = new QComboBox();
550                 fontList.setMaximumWidth(130);
551                 fontSize.setMaximumWidth(45);
552                 fontSize.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed);
553                 fontList.setToolTip("Font");
554                 fontSize.setToolTip("Font Size");
555                 fontList.setStyleSheet("QComboBox {padding: 3px;} ");
556                 fontSize.setStyleSheet("QComboBox {padding: 3px;} ");
557                 fontList.activated.connect(this, "fontChanged(String)");
558                 fontSize.activated.connect(this, "fontSizeChanged(String)");
559                 fontListAction = buttonLayout.addWidget(fontList);
560                 buttonLayout.toggleFontVisible.triggered.connect(this, "toggleFontListVisible(Boolean)");
561                 fontSizeAction = buttonLayout.addWidget(fontSize);
562                 buttonLayout.toggleFontSizeVisible.triggered.connect(this, "toggleFontSizeVisible(Boolean)");
563                 QFontDatabase fonts = new QFontDatabase();
564                 List<String> fontFamilies = fonts.families();
565                 for (int i = 0; i < fontFamilies.size(); i++) {
566                         fontList.addItem(fontFamilies.get(i));
567                         if (i == 0) {
568                                 loadFontSize(fontFamilies.get(i));
569                         }
570                 }
571
572 //              buttonLayout.addWidget(newSeparator(), 0);
573                 fontColor = newToolButton("fontColor", tr("Font Color"));
574                 fontColorMenu = new ColorMenu(this);
575                 fontColor.setMenu(fontColorMenu.getMenu());
576                 fontColor.setPopupMode(ToolButtonPopupMode.MenuButtonPopup);
577                 fontColor.setAutoRaise(false);
578                 fontColorMenu.getMenu().triggered.connect(this, "fontColorClicked()");
579                 fontColorAction = buttonLayout.addWidget(fontColor);
580                 buttonLayout.toggleFontColorVisible.triggered.connect(this, "toggleFontColorVisible(Boolean)");
581                 fontHilight = newToolButton("fontHilight", tr("Font Hilight Color"));
582                 fontHilight.setPopupMode(ToolButtonPopupMode.MenuButtonPopup);
583                 fontHilight.setAutoRaise(false);
584                 fontHilightColorMenu = new ColorMenu(this);
585                 fontHilightColorMenu.setDefault(QColor.yellow);
586                 fontHilight.setMenu(fontHilightColorMenu.getMenu());
587                 fontHilightColorMenu.getMenu().triggered.connect(this, "fontHilightClicked()");
588                 fontHilightAction = buttonLayout.addWidget(fontHilight);
589                 fontHilightColorMenu.setDefault(QColor.yellow);
590                 buttonLayout.toggleFontHilight.triggered.connect(this, "toggleFontHilightVisible(Boolean)");
591                 
592                 spellCheckAction = buttonLayout.addWidget(spellCheckButton);
593                 buttonLayout.toggleNumberListVisible.triggered.connect(this, "spellCheckClicked()");
594                 buttonLayout.toggleSpellCheck.triggered.connect(this, "toggleSpellCheckVisible(Boolean)");
595                 
596                 todoAction = buttonLayout.addWidget(todoButton);
597                 buttonLayout.toggleNumberListVisible.triggered.connect(this, "todoClicked()");
598                 buttonLayout.toggleTodo.triggered.connect(this, "toggleTodoVisible(Boolean)");
599
600                 // Setup the source browser);
601
602 //              buttonLayout.addWidget(new QLabel(), 1);
603                 QSplitter editSplitter = new QSplitter(this);
604                 editSplitter.addWidget(browser);
605                 editSplitter.setOrientation(Qt.Orientation.Vertical);
606                 editSplitter.addWidget(sourceEdit);
607
608                 
609
610 //              v.addWidget(browser, 1);
611 //              v.addWidget(sourceEdit);
612                 v.addWidget(editSplitter);
613                 setLayout(v);
614
615                 browser.downloadAttachmentRequested.connect(this,
616                                 "downloadAttachment(QNetworkRequest)");
617                 browser.downloadImageRequested.connect(this,
618                                 "downloadImage(QNetworkRequest)");
619                 setTabOrder(notebookBox, tagEdit);
620                 setTabOrder(tagEdit, browser);
621                 
622                 focusNoteShortcut = new QShortcut(this);
623                 setupShortcut(focusNoteShortcut, "Focus_Note");
624                 focusNoteShortcut.activated.connect(this, "focusNote()");
625                 focusTitleShortcut = new QShortcut(this);
626                 setupShortcut(focusTitleShortcut, "Focus_Title");
627                 focusTitleShortcut.activated.connect(this, "focusTitle()");
628                 focusTagShortcut = new QShortcut(this);
629                 setupShortcut(focusTagShortcut, "Focus_Tag");
630                 focusTagShortcut.activated.connect(this, "focusTag()");
631                 focusAuthorShortcut = new QShortcut(this);
632                 setupShortcut(focusAuthorShortcut, "Focus_Author");
633                 focusAuthorShortcut.activated.connect(this, "focusAuthor()");
634                 focusUrlShortcut = new QShortcut(this);
635                 setupShortcut(focusUrlShortcut, "Focus_Url");
636                 focusUrlShortcut.activated.connect(this, "focusUrl()");
637                 
638                 browser.page().mainFrame().setTextSizeMultiplier(Global.getTextSizeMultiplier());
639                 browser.page().mainFrame().setZoomFactor(Global.getZoomFactor());
640                 
641                 previewPageList = new HashMap<String,Integer>();
642                 
643                 browser.page().microFocusChanged.connect(this, "microFocusChanged()");
644                 
645                 //Setup colors
646                 
647                 QPalette pal = new QPalette();
648                 pal.setColor(ColorRole.Text, QColor.black);
649                 titleLabel.setPalette(pal);
650                 authorText.setPalette(pal);
651                 authorLabel.setPalette(pal);
652                 urlLabel.setPalette(pal);
653                 urlText.setPalette(pal);
654                 createdDate.setPalette(pal);
655                 createdTime.setPalette(pal);
656                 alteredDate.setPalette(pal);
657                 alteredTime.setPalette(pal);
658                 subjectDate.setPalette(pal);
659                 subjectTime.setPalette(pal);
660                 tagEdit.setPalette(pal);
661                 notebookBox.setPalette(pal);
662                 
663                 blockApplication = new Signal1<BrowserWindow>();
664                 unblockApplication = new Signal0();
665                 
666                 setSourceTimer = new QTimer();
667                 setSourceTimer.timeout.connect(this, "setSource()");
668                 
669                 logger.log(logger.HIGH, "Browser setup complete");
670         }
671
672         
673         
674         private void setupShortcut(QShortcut action, String text) {
675                 if (!Global.shortcutKeys.containsAction(text))
676                         return;
677                 action.setKey(new QKeySequence(Global.shortcutKeys.getShortcut(text)));
678         }
679         
680         
681
682         
683         // Getter for the QWebView
684         public QWebView getBrowser() {
685                 return browser;
686         }
687
688         // Block signals while loading data or things are flagged as dirty by
689         // mistake
690         public void loadingData(boolean val) {
691                 logger.log(logger.EXTREME, "Entering BrowserWindow.loadingData() " +val);
692                 notebookBox.blockSignals(val);
693                 browser.page().blockSignals(val);
694                 browser.page().mainFrame().blockSignals(val);
695                 titleLabel.blockSignals(val);
696                 alteredDate.blockSignals(val);
697                 alteredTime.blockSignals(val);
698                 createdTime.blockSignals(val);
699                 createdDate.blockSignals(val);
700                 subjectDate.blockSignals(val);
701                 subjectTime.blockSignals(val);
702                 urlText.blockSignals(val);
703                 authorText.blockSignals(val);
704                 if (!val)
705                         exposeToJavascript();
706                 logger.log(logger.EXTREME, "Exiting BrowserWindow.loadingData() " +val);
707         }
708
709         // Enable/disable
710         public void setReadOnly(boolean v) {
711                 setEnabled(true);
712                 titleLabel.setEnabled(!v);
713                 notebookBox.setEnabled(!v);
714                 tagEdit.setEnabled(!v);
715                 authorLabel.setEnabled(!v);
716                 geoBox.setEnabled(!v);
717                 urlText.setEnabled(!v);
718                 createdDate.setEnabled(!v);
719                 subjectDate.setEnabled(!v);
720                 alteredDate.setEnabled(!v);
721                 authorText.setEnabled(!v);
722                 createdTime.setEnabled(!v);
723                 alteredTime.setEnabled(!v);
724                 subjectTime.setEnabled(!v);
725                 getBrowser().setEnabled(true);
726                 getBrowser().page().setContentEditable(!v);
727 //              getBrowser().setEnabled(!v);
728         }
729         
730         // expose this class to Javascript on the web page
731         private void exposeToJavascript() {
732                 browser.page().mainFrame().addToJavaScriptWindowObject("jambi", this);
733         }
734
735         // Custom event queue
736         @Override
737         public boolean event(QEvent e) {
738                 if (e.type().equals(QEvent.Type.FocusOut)) {
739                         logger.log(logger.EXTREME, "Focus lost");
740                         focusLost.emit();
741                 }
742                 return super.event(e);
743         }
744
745         // clear out browser
746         public void clear() {
747                 logger.log(logger.EXTREME, "Entering BrowserWindow.clear()");
748                 setNote(null);
749                 setContent(new QByteArray());
750                 tagEdit.setText("");
751                 tagEdit.tagCompleter.reset();
752                 urlLabel.setText(tr("Source URL:"));
753                 titleLabel.setText("");
754                 logger.log(logger.EXTREME, "Exiting BrowserWindow.clear()");
755         }
756
757         public void setContent(QByteArray data) {
758                 sourceEdit.blockSignals(true);
759                 browser.setContent(data);
760                 setSource();
761         }
762         // get/set current note
763         public void setNote(Note n) {
764                 currentNote = n;
765                 if (n == null)
766                         n = new Note();
767                 saveNoteTitle = n.getTitle();
768
769         }
770
771         public Note getNote() {
772                 return currentNote;
773         }
774
775         // New Editor Button
776         private QPushButton newEditorButton(String name, String toolTip) {
777                 QPushButton button = new QPushButton();
778 //              QIcon icon = new QIcon(iconPath + name + ".gif");
779                 QIcon icon = new QIcon(iconPath + name + ".png");
780                 button.setIcon(icon);
781                 button.setIconSize(new QSize(16, 16));
782                 
783                 button.setToolTip(toolTip);
784                 button.clicked.connect(this, name + "Clicked()");
785                 button.setStyleSheet("QPushButton {padding: 3px;} ");
786                 return button;
787         }
788         // New Editor Button
789         private QToolButton newToolButton(String name, String toolTip) {
790                 QToolButton button = new QToolButton();
791 //              QIcon icon = new QIcon(iconPath + name + ".gif");
792                 QIcon icon = new QIcon(iconPath + name + ".png");
793                 button.setIcon(icon);
794                 button.setIconSize(new QSize(16, 16));
795                 
796                 button.setToolTip(toolTip);
797                 button.clicked.connect(this, name + "Clicked()");
798                 button.setStyleSheet("QToolButton {padding: 3px;} ");
799                 button.setMaximumHeight(22);
800                 return button;
801         }
802
803         // New Separator
804         private QLabel newSeparator() {
805                 return new QLabel("");
806         }
807
808         // Set the title in the window
809         public void setTitle(String t) {
810                 titleLabel.setText(t);
811                 saveNoteTitle = t;
812                 checkNoteTitle();
813         }
814
815         // Return the current text title
816         public String getTitle() {
817                 return titleLabel.text();
818         }
819
820         // Set the tag name string
821         public void setTag(String t) {
822                 saveTagList = t;
823                 tagEdit.setText(t);
824                 tagEdit.tagCompleter.reset();
825         }
826
827         // Set the source URL
828         public void setUrl(String t) {
829                 urlLabel.setText(tr("Source URL:\t"));
830                 urlText.setText(t);
831         }
832
833         // The user want's to launch a web browser on the source of the URL
834         public void sourceUrlClicked() {
835                 // Make sure we have a valid URL
836                 if (urlText.text().trim().equals(""))
837                         return;
838                 
839                 String url = urlText.text();
840                 if (!url.toLowerCase().startsWith(tr("http://")))
841                         url = tr("http://") +url;
842                 
843         if (!QDesktopServices.openUrl(new QUrl(url))) {
844                 logger.log(logger.LOW, "Error opening file :" +url);
845         }
846         }
847         
848         public void setAuthor(String t) {
849                 authorLabel.setText(tr("Author:\t"));
850                 authorText.setText(t);
851         }
852
853         // Set the creation date
854         public void setCreation(long date) {
855                 QDateTime dt = new QDateTime();
856                 dt.setTime_t((int) (date / 1000));
857                 createdDate.setDateTime(dt);
858                 createdTime.setDateTime(dt);
859                 createdDate.setDisplayFormat(Global.getDateFormat());
860                 createdTime.setDisplayFormat(Global.getTimeFormat());
861         }
862
863         // Set the creation date
864         public void setAltered(long date) {
865                 QDateTime dt = new QDateTime();
866                 dt.setTime_t((int) (date / 1000));
867                 alteredDate.setDateTime(dt);
868                 alteredTime.setDateTime(dt);
869                 alteredDate.setDisplayFormat(Global.getDateFormat());
870                 alteredTime.setDisplayFormat(Global.getTimeFormat());
871         }
872
873         // Set the subject date
874         public void setSubjectDate(long date) {
875                 QDateTime dt = new QDateTime();
876                 dt.setTime_t((int) (date / 1000));
877                 subjectDate.setDateTime(dt);
878                 subjectTime.setDateTime(dt);
879                 subjectDate.setDisplayFormat(Global.getDateFormat());
880                 subjectTime.setDisplayFormat(Global.getTimeFormat());
881         }
882
883         // Toggle the extended attribute information
884         public void toggleInformation() {
885                 if (extendedOn) {
886                         extendedOn = false;
887                 } else {
888                         extendedOn = true;
889                 }
890                 urlLabel.setVisible(extendedOn);
891                 urlText.setVisible(extendedOn);
892                 authorText.setVisible(extendedOn);
893                 geoBox.setVisible(extendedOn);
894                 authorLabel.setVisible(extendedOn);
895                 createdDate.setVisible(extendedOn);
896                 createdTime.setVisible(extendedOn);
897                 createdLabel.setVisible(extendedOn);
898                 alteredLabel.setVisible(extendedOn);
899                 alteredDate.setVisible(extendedOn);
900                 alteredTime.setVisible(extendedOn);
901                 //notebookBox.setVisible(extendedOn);
902                 notebookLabel.setVisible(extendedOn);
903                 subjectLabel.setVisible(extendedOn);
904                 subjectDate.setVisible(extendedOn);
905                 subjectTime.setVisible(extendedOn);
906         }
907
908         public void hideButtons() {
909
910                 undoButton.parentWidget().setVisible(false);
911                 buttonsVisible = false;
912         }
913
914
915         // Is the extended view on?
916         public boolean isExtended() {
917                 return extendedOn;
918         }
919
920         // Listener for when a link is clicked
921         @SuppressWarnings("unused")
922         private void openFile() {
923                 logger.log(logger.EXTREME, "Starting openFile()");
924                 File fileHandle = new File(selectedFile);
925                 URI fileURL = fileHandle.toURI();
926                 String localURL = fileURL.toString();
927                 QUrl url = new QUrl(localURL);
928                 QFile file = new QFile(selectedFile);
929                 
930                 logger.log(logger.EXTREME, "Adding to fileWatcher:"+file.fileName());
931                 fileWatcher.addPath(file.fileName());
932         
933         if (!QDesktopServices.openUrl(url)) {
934                 logger.log(logger.LOW, "Error opening file :" +url);
935         }
936         }
937         
938         
939         // Listener for when a link is clicked
940         @SuppressWarnings("unused")
941         private void linkClicked(QUrl url) {
942                 logger.log(logger.EXTREME, "URL Clicked: " +url.toString());
943                 if (url.toString().startsWith("latex:")) {
944                         int position = url.toString().lastIndexOf(".");
945                         String guid = url.toString().substring(0,position);
946                         position = guid.lastIndexOf("/");
947                         guid = guid.substring(position+1);
948                         editLatex(guid);
949                         return;
950                 }
951                 if (url.toString().startsWith("evernote:/view/")) {
952                         StringTokenizer tokens = new StringTokenizer(url.toString().replace("evernote:/view/", ""), "/");
953                         tokens.nextToken();
954                         tokens.nextToken();
955                         String sid = tokens.nextToken();
956                         String lid = tokens.nextToken();
957                         
958                         // Emit that we want to switch to a new note
959                         evernoteLinkClicked.emit(sid, lid);
960
961                         return;
962                 }
963                 if (url.toString().startsWith("nnres://")) {
964                         logger.log(logger.EXTREME, "URL is NN resource");
965                         if (url.toString().endsWith("/vnd.evernote.ink")) {
966                                 logger.log(logger.EXTREME, "Unable to open ink note");
967                                 QMessageBox.information(this, tr("Unable Open"), tr("This is an ink note.\n"+
968                                         "Ink notes are not supported since Evernote has not\n published any specifications on them\n" +
969                                         "and I'm too lazy to figure them out by myself."));
970                                 return;
971                         }
972                         String fullName = url.toString().substring(8);
973                         int index = fullName.indexOf(".");
974                         String guid = "";
975                         String type = "";
976                         if (index >-1) {
977                                 type = fullName.substring(index+1);
978                                 guid = fullName.substring(0,index);
979                         }
980                         index = guid.indexOf(Global.attachmentNameDelimeter);
981                         if (index > -1) {
982                                 guid = guid.substring(0,index);
983                         }
984                         List<Resource> resList = currentNote.getResources();
985                         Resource res = null;
986                         for (int i=0; i<resList.size(); i++) {
987                                 if (resList.get(i).getGuid().equals(guid)) {
988                                         res = resList.get(i);
989                                         i=resList.size();
990                                 }
991                         }
992                         if (res == null) {
993                                 String resGuid = Global.resourceMap.get(guid);
994                                 if (resGuid != null) 
995                                         res = conn.getNoteTable().noteResourceTable.getNoteResource(resGuid, true);
996                         }
997                         if (res != null) {
998                                 String fileName;
999                                 if (res.getAttributes() != null && 
1000                                                 res.getAttributes().getFileName() != null && 
1001                                                 !res.getAttributes().getFileName().trim().equals(""))
1002                                         fileName = res.getGuid()+Global.attachmentNameDelimeter+res.getAttributes().getFileName();
1003                                 else
1004                                         fileName = res.getGuid()+"."+type;
1005                                 QFile file = new QFile(Global.getFileManager().getResDirPath(fileName));
1006                         QFile.OpenMode mode = new QFile.OpenMode();
1007                         mode.set(QFile.OpenModeFlag.WriteOnly);
1008                         boolean openResult = file.open(mode);
1009                                 logger.log(logger.EXTREME, "File opened:" +openResult);
1010                         QDataStream out = new QDataStream(file);
1011                         Resource resBinary = conn.getNoteTable().noteResourceTable.getNoteResource(res.getGuid(), true);
1012                                 QByteArray binData = new QByteArray(resBinary.getData().getBody());
1013                                 resBinary = null;
1014                                 logger.log(logger.EXTREME, "Writing resource");
1015                         out.writeBytes(binData.toByteArray());
1016                         file.close();
1017                                 
1018                         String whichOS = System.getProperty("os.name");
1019                                 if (whichOS.contains("Windows")) 
1020                                 url.setUrl("file:///"+file.fileName());
1021                         else
1022                                 url.setUrl("file://"+file.fileName());
1023                  //       fileWatcher.removePath(file.fileName());
1024                                 logger.log(logger.EXTREME, "Adding file watcher " +file.fileName());
1025                                 fileWatcher.addPath(file.fileName());
1026                         
1027                         // If we can't open it, then prompt the user to save it.
1028                         if (!QDesktopServices.openUrl(url)) {
1029                                         logger.log(logger.EXTREME, "We can't handle this.  Where do we put it?");
1030                                 QFileDialog dialog = new QFileDialog();
1031                                 dialog.show();
1032                                 if (dialog.exec()!=0) {
1033                                         List<String> fileNames = dialog.selectedFiles(); //gets all selected filenames
1034                                         if (fileNames.size() == 0) 
1035                                                 return;
1036                                         String sf = fileNames.get(0);
1037                                         QFile saveFile = new QFile(sf);
1038                                         mode.set(QFile.OpenModeFlag.WriteOnly);
1039                                         saveFile.open(mode);
1040                                         QDataStream saveOut = new QDataStream(saveFile);
1041                                         saveOut.writeBytes(binData.toByteArray());
1042                                         saveFile.close();
1043                                         return;
1044                                 }
1045                                 }
1046                         }
1047                         return;
1048                 }
1049                 logger.log(logger.EXTREME, "Launching URL");
1050                 QDesktopServices.openUrl(url);
1051         }
1052
1053         // Listener for when BOLD is clicked
1054         @SuppressWarnings("unused")
1055         private void undoClicked() {
1056                 browser.page().triggerAction(WebAction.Undo);
1057                 browser.setFocus();
1058         }
1059
1060         // Listener for when BOLD is clicked
1061         @SuppressWarnings("unused")
1062         private void redoClicked() {
1063                 browser.page().triggerAction(WebAction.Redo);
1064                 browser.setFocus();
1065         }
1066
1067         // Listener for when BOLD is clicked
1068         @SuppressWarnings("unused")
1069         private void boldClicked() {
1070                 browser.page().triggerAction(WebAction.ToggleBold);
1071                 microFocusChanged();
1072                 browser.setFocus();
1073         }
1074
1075         // Listener for when Italics is clicked
1076         @SuppressWarnings("unused")
1077         private void italicClicked() {
1078                 browser.page().triggerAction(WebAction.ToggleItalic);
1079                 microFocusChanged();
1080                 browser.setFocus();
1081         }
1082
1083         // Listener for when UNDERLINE is clicked
1084         @SuppressWarnings("unused")
1085         private void underlineClicked() {
1086                 browser.page().triggerAction(WebAction.ToggleUnderline);
1087                 microFocusChanged();
1088                 browser.setFocus();
1089         }
1090
1091         // Listener for when Strikethrough is clicked
1092         @SuppressWarnings("unused")
1093         private void strikethroughClicked() {
1094                 browser.page().mainFrame().evaluateJavaScript(
1095                                 "document.execCommand('strikeThrough', false, '');");
1096                 browser.setFocus();
1097         }
1098
1099         // Listener for when cut is clicked
1100         @SuppressWarnings("unused")
1101         private void cutClicked() {
1102                 cbObserver.setCopySourceGuid(currentNote.getGuid(), browser.page().selectedText());
1103                 
1104                 browser.page().triggerAction(WebAction.Cut);
1105                 browser.setFocus();
1106         }
1107
1108         // Listener when COPY is clicked
1109         @SuppressWarnings("unused")
1110         private void copyClicked() {
1111                 cbObserver.setCopySourceGuid(currentNote.getGuid(), browser.page().selectedText());
1112                 
1113                 browser.page().triggerAction(WebAction.Copy);
1114                 browser.setFocus();
1115         }
1116
1117         // Listener when PASTE is clicked
1118         public void pasteClicked() {
1119                 logger.log(logger.EXTREME, "Paste Clicked");
1120                 if (forceTextPaste) {
1121                         pasteWithoutFormattingClicked();
1122                         return;
1123                 }
1124                 
1125                 // コピー&ペーストの操作履歴をデータベースに登録
1126                 String srcGuid = cbObserver.getSourceGuid();
1127                 String dstGuid = currentNote.getGuid();
1128                 if(srcGuid != null && dstGuid != null){
1129                         if(!srcGuid.equals(dstGuid)){
1130                                 conn.getHistoryTable().addHistory("copy & paste", srcGuid, dstGuid);
1131                         }
1132                 }
1133                 
1134                 QClipboard clipboard = QApplication.clipboard();
1135                 QMimeData mime = clipboard.mimeData();
1136
1137                 if (mime.hasImage()) {
1138                         logger.log(logger.EXTREME, "Image paste found");
1139                         browser.setFocus();
1140                         insertImage(mime);
1141                         browser.setFocus();
1142                         return;
1143                 }
1144
1145                 if (mime.hasUrls()) {
1146                         logger.log(logger.EXTREME, "URL paste found");
1147                         if (mime.text().startsWith("evernote:")) {
1148                                 handleNoteLink(mime);
1149                         } else {
1150                                 handleUrls(mime);
1151                                 browser.setFocus();
1152                         }
1153                         return;
1154                 }
1155                 
1156                 String text = mime.html();
1157                 if (text.contains("en-tag") && mime.hasHtml()) {
1158                         logger.log(logger.EXTREME, "Intra-note paste found");
1159                         text = fixInternotePaste(text);
1160                         mime.setHtml(text);
1161                         clipboard.setMimeData(mime);
1162                 }
1163
1164                 logger.log(logger.EXTREME, "Final paste choice encountered");
1165                 browser.page().triggerAction(WebAction.Paste);
1166                 browser.setFocus();
1167
1168         }
1169
1170         // Paste text without formatting
1171         private void pasteWithoutFormattingClicked() {
1172                 logger.log(logger.EXTREME, "Paste without format clipped");
1173                 QClipboard clipboard = QApplication.clipboard();
1174                 QMimeData mime = clipboard.mimeData();
1175                 if (!mime.hasText())
1176                         return;
1177                 
1178                 // コピー&ペーストの操作履歴をデータベースに登録
1179                 String srcGuid = cbObserver.getSourceGuid();
1180                 String dstGuid = currentNote.getGuid();
1181                 if(srcGuid != null && dstGuid != null){
1182                         if(!srcGuid.equals(dstGuid)){
1183                                 conn.getHistoryTable().addHistory("copy & paste", srcGuid, dstGuid);
1184                         }
1185                 }
1186                 
1187                 String text = mime.text();
1188                 clipboard.clear();
1189                 clipboard.setText(text, Mode.Clipboard);
1190                 browser.page().triggerAction(WebAction.Paste);
1191
1192                 // This is done because pasting into an encryption block
1193                 // can cause multiple cells (which can't happen).  It 
1194                 // just goes through the table, extracts the data, & 
1195                 // puts it back as one table cell.
1196                 if (insideEncryption) {
1197                         String js = new String( "function fixEncryption() { "
1198                                         +"   var selObj = window.getSelection();"
1199                                         +"   var selRange = selObj.getRangeAt(0);"
1200                                         +"   var workingNode = window.getSelection().anchorNode;"
1201                                         +"   while(workingNode != null && workingNode.nodeName.toLowerCase() != 'table') { " 
1202                                         +"           workingNode = workingNode.parentNode;"
1203                                         +"   } "
1204                                         +"   workingNode.innerHTML = window.jambi.fixEncryptionPaste(workingNode.innerHTML);"
1205                                         +"} fixEncryption();");
1206                         browser.page().mainFrame().evaluateJavaScript(js);
1207                 }
1208         }
1209         
1210         // This basically removes all the table tags and returns just the contents.
1211         // This is called by JavaScript to fix encryption pastes.
1212         public String fixEncryptionPaste(String data) {
1213                 data = data.replace("<tbody>", "");
1214                 data = data.replace("</tbody>", "");
1215                 data = data.replace("<tr>", "");
1216                 data = data.replace("</tr>", "");
1217                 data = data.replace("<td>", "");
1218                 data = data.replace("</td>", "<br>");
1219                 data = data.replace("<br><br>", "<br>");
1220
1221                 return "<tbody><tr><td>"+data+"</td></tr></tbody>";
1222         }
1223         
1224         // insert date/time
1225         @SuppressWarnings("unused")
1226         private void insertDateTime() {
1227                 String fmt = Global.getDateFormat() + " " + Global.getTimeFormat();
1228                 String dateTimeFormat = new String(fmt);
1229                 SimpleDateFormat simple = new SimpleDateFormat(dateTimeFormat);
1230                 Calendar cal = Calendar.getInstance();
1231                 
1232                 browser.page().mainFrame().evaluateJavaScript(
1233                         "document.execCommand('insertHtml', false, '"+simple.format(cal.getTime())+"');");
1234                 
1235                 browser.setFocus();
1236
1237         }
1238
1239         // Listener when Left is clicked
1240         @SuppressWarnings("unused")
1241         private void justifyLeftClicked() {
1242                 browser.page().mainFrame().evaluateJavaScript(
1243                                 "document.execCommand('JustifyLeft', false, '');");
1244                 browser.setFocus();
1245         }
1246
1247         // Listener when Center is clicked
1248         @SuppressWarnings("unused")
1249         private void justifyCenterClicked() {
1250                 browser.page().mainFrame().evaluateJavaScript(
1251                                 "document.execCommand('JustifyCenter', false, '');");
1252                 browser.setFocus();
1253         }
1254
1255         // Listener when Left is clicked
1256         @SuppressWarnings("unused")
1257         private void justifyRightClicked() {
1258                 browser.page().mainFrame().evaluateJavaScript(
1259                                 "document.execCommand('JustifyRight', false, '');");
1260                 browser.setFocus();
1261         }
1262
1263         // Listener when HLINE is clicked
1264         @SuppressWarnings("unused")
1265         private void hlineClicked() {
1266                 browser.page().mainFrame().evaluateJavaScript(
1267                                 "document.execCommand('insertHorizontalRule', false, '');");
1268                 browser.setFocus();
1269         }
1270
1271         // Listener when outdent is clicked
1272         private void outdentClicked() {
1273                 browser.page().mainFrame().evaluateJavaScript(
1274                                 "document.execCommand('outdent', false, '');");
1275                 browser.setFocus();
1276         }
1277
1278         // Listener when a bullet list is clicked
1279         @SuppressWarnings("unused")
1280         private void bulletListClicked() {
1281                 browser.page().mainFrame().evaluateJavaScript(
1282                                 "document.execCommand('InsertUnorderedList', false, '');");
1283                 browser.setFocus();
1284         }
1285
1286         // Listener when a bullet list is clicked
1287         @SuppressWarnings("unused")
1288         private void numberListClicked() {
1289                 browser.page().mainFrame().evaluateJavaScript(
1290                                 "document.execCommand('InsertOrderedList', false, '');");
1291                 browser.setFocus();
1292         }
1293
1294         // Listener when indent is clicked
1295         private void indentClicked() {
1296                 browser.page().mainFrame().evaluateJavaScript(
1297                                 "document.execCommand('indent', false, '');");
1298                 browser.setFocus();
1299         }
1300
1301         // Listener when the font name is changed
1302         @SuppressWarnings("unused")
1303         private void fontChanged(String font) {
1304                 browser.page().mainFrame().evaluateJavaScript(
1305                                 "document.execCommand('fontName',false,'" + font + "');");
1306                 browser.setFocus();
1307         }
1308
1309         // Listener when a font size is changed
1310         @SuppressWarnings("unused")
1311         private void fontSizeChanged(String font) {
1312                 String text = browser.selectedText();
1313                 if (text.trim().equalsIgnoreCase(""))
1314                         return;
1315
1316                 String selectedText = browser.selectedText();
1317                 String url = "<span style=\"font-size:" +font +"pt; \">"+selectedText +"</a>";
1318                 String script = "document.execCommand('insertHtml', false, '"+url+"');";
1319                 browser.page().mainFrame().evaluateJavaScript(script);
1320 /*              browser.page().mainFrame().evaluateJavaScript(
1321                                 "document.execCommand('fontSize',false,'"
1322                                                 + font + "');");
1323 */
1324                 browser.setFocus();
1325         }
1326
1327         // Load the font combo box based upon the font selected
1328         private void loadFontSize(String name) {        
1329                 QFontDatabase db = new QFontDatabase(); 
1330                 fontSize.clear();
1331                 List<Integer> points = db.pointSizes(name); 
1332                 for (int i=0; i<points.size(); i++) { 
1333                         fontSize.addItem(points.get(i).toString()); 
1334                 }
1335                 /*
1336                 fontSize.addItem("x-small");
1337                 fontSize.addItem("small");
1338                 fontSize.addItem("medium");
1339                 fontSize.addItem("large");
1340                 fontSize.addItem("x-large");
1341                 fontSize.addItem("xx-large");
1342                 fontSize.addItem("xxx-large");
1343                 */
1344         }
1345
1346         // Listener when a font size is changed
1347         @SuppressWarnings("unused")
1348         private void fontColorClicked() {
1349 //              QColorDialog dialog = new QColorDialog();
1350 //              QColor color = QColorDialog.getColor();
1351                 QColor color = fontColorMenu.getColor();
1352                 if (color.isValid())
1353                         browser.page().mainFrame().evaluateJavaScript(
1354                                         "document.execCommand('foreColor',false,'" + color.name()
1355                                                         + "');");
1356                 browser.setFocus();
1357         }
1358
1359         // Listener for when a background color change is requested
1360         @SuppressWarnings("unused")
1361         private void fontHilightClicked() {
1362 //              QColorDialog dialog = new QColorDialog();
1363 //              QColor color = QColorDialog.getColor();
1364                 QColor color = fontHilightColorMenu.getColor();
1365                 if (color.isValid())
1366                         browser.page().mainFrame().evaluateJavaScript(
1367                                         "document.execCommand('backColor',false,'" + color.name()
1368                                                         + "');");
1369                 browser.setFocus();
1370         }
1371         
1372         // Listener for when a background color change is requested
1373         @SuppressWarnings("unused")
1374         private void superscriptClicked() {
1375                 browser.page().mainFrame().evaluateJavaScript(
1376                                         "document.execCommand('superscript');");
1377                 browser.setFocus();
1378         }
1379         
1380         // Listener for when a background color change is requested
1381         @SuppressWarnings("unused")
1382         private void subscriptClicked() {
1383                 browser.page().mainFrame().evaluateJavaScript(
1384                                         "document.execCommand('subscript');");
1385                 browser.setFocus();
1386         }
1387         // Insert a to-do checkbox
1388         @SuppressWarnings("unused")
1389         private void todoClicked() {
1390                 FileNameMap fileNameMap = URLConnection.getFileNameMap();
1391                 String script_start = new String(
1392                                 "document.execCommand('insertHtml', false, '");
1393                 String script_end = new String("');");
1394                 String todo = new String(
1395                                 "<input TYPE=\"CHECKBOX\" value=\"false\" " +
1396                                 "onMouseOver=\"style.cursor=\\'hand\\'\" " +
1397                                 "onClick=\"value=checked; window.jambi.contentChanged(); \" />");
1398                 browser.page().mainFrame().evaluateJavaScript(
1399                                 script_start + todo + script_end);
1400                 browser.setFocus();
1401         }
1402
1403         // Encrypt the selected text
1404         @SuppressWarnings("unused")
1405         private void encryptText() {
1406                 String text = browser.selectedText();
1407                 if (text.trim().equalsIgnoreCase(""))
1408                         return;
1409                 text = new String(text.replaceAll("\n", "<br/>"));
1410
1411                 EnCryptDialog dialog = new EnCryptDialog();
1412                 dialog.exec();
1413                 if (!dialog.okPressed()) {
1414                         return;
1415                 }
1416
1417                 EnCrypt crypt = new EnCrypt();
1418                 String encrypted = crypt.encrypt(text, dialog.getPassword().trim(), 64);
1419                 String decrypted = crypt.decrypt(encrypted, dialog.getPassword().trim(), 64);
1420
1421                 if (encrypted.trim().equals("")) {
1422                         QMessageBox.information(this, tr("Error"), tr("Error Encrypting String"));
1423                         return;
1424                 }
1425                 StringBuffer buffer = new StringBuffer(encrypted.length() + 100);
1426                 buffer.append("<img en-tag=\"en-crypt\" cipher=\"RC2\" hint=\""
1427                                 + dialog.getHint().replace("'","\\'") + "\" length=\"64\" ");
1428                 buffer.append("contentEditable=\"false\" alt=\"");
1429                 buffer.append(encrypted);
1430                 buffer.append("\" src=\"").append(FileUtils.toForwardSlashedPath(Global.getFileManager().getImageDirPath("encrypt.png") +"\""));
1431                 Global.cryptCounter++;
1432                 buffer.append(" id=\"crypt"+Global.cryptCounter.toString() +"\"");
1433                 buffer.append(" onMouseOver=\"style.cursor=\\'hand\\'\"");
1434                 buffer.append(" onClick=\"window.jambi.decryptText(\\'crypt"+Global.cryptCounter.toString() 
1435                                 +"\\', \\'"+encrypted+"\\', \\'"+dialog.getHint().replace("'", "\\&amp;apos;")+"\\');\"");
1436                 buffer.append("style=\"display:block\" />");
1437
1438                 String script_start = new String(
1439                                 "document.execCommand('insertHtml', false, '");
1440                 String script_end = new String("');");
1441                 browser.page().mainFrame().evaluateJavaScript(
1442                                 script_start + buffer.toString() + script_end);
1443         }
1444
1445
1446         // Insert a Quick hyperlink
1447         public void insertQuickLink() {
1448                 logger.log(logger.EXTREME, "Inserting link");
1449                 String text = browser.selectedText();
1450                 if (text.trim().equalsIgnoreCase(""))
1451                         return;
1452                 
1453                 NoteQuickLinkDialog dialog = new NoteQuickLinkDialog(logger, conn, text, cbObserver);
1454
1455                 if (dialog.getResults().size() == 0) {
1456                         QMessageBox.critical(null, tr("No Matches Found") ,tr("No matching notes found."));
1457                         return;
1458                 }
1459                 if (dialog.getResults().size() > 1) {
1460                         dialog.exec();
1461                         if (!dialog.okPressed) {
1462                                 logger.log(logger.EXTREME, "Insert link canceled");
1463                                 return;
1464                         }
1465                 }
1466
1467                 User user = Global.getUserInformation();
1468                 String dUrl = new String("evernote:///view/") + new String(user.getId() + "/" +user.getShardId() +"/"
1469                                 +dialog.getSelectedNote()+"/"+dialog.getSelectedNote() +"/ " +"style=\"color:#69aa35\"");
1470                 
1471                 String url = "<a title=\"" +dUrl
1472                                 +"\" href=" +dUrl 
1473                                 +" >"+text +"</a>";
1474                 String script = "document.execCommand('insertHtml', false, '"+url+"');";
1475                 browser.page().mainFrame().evaluateJavaScript(script);  
1476                 contentChanged();
1477         }
1478
1479         // Insert a hyperlink
1480         public void insertLink() {
1481                 logger.log(logger.EXTREME, "Inserting link");
1482                 String text = browser.selectedText();
1483                 if (text.trim().equalsIgnoreCase(""))
1484                         return;
1485
1486                 InsertLinkDialog dialog = new InsertLinkDialog(insertHyperlink);
1487                 if (currentHyperlink != null && currentHyperlink != "") {
1488                         dialog.setUrl(currentHyperlink);
1489                 }
1490                 dialog.exec();
1491                 if (!dialog.okPressed()) {
1492                         logger.log(logger.EXTREME, "Insert link canceled");
1493                         return;
1494                 }
1495                 
1496                 // Take care of inserting new links
1497                 if (insertHyperlink) {
1498                         String selectedText = browser.selectedText();
1499                         if (dialog.getUrl().trim().equals(""))
1500                                 return;
1501                         logger.log(logger.EXTREME, "Inserting link on text "+selectedText);
1502                         logger.log(logger.EXTREME, "URL Link " +dialog.getUrl().trim());
1503                         String dUrl = StringUtils.replace(dialog.getUrl().trim(), "'", "\\'");
1504                         String url = "<a href=\"" +dUrl
1505                                         +"\" title=" +dUrl 
1506                                         +" >"+selectedText +"</a>";
1507                         String script = "document.execCommand('insertHtml', false, '"+url+"');";
1508                         browser.page().mainFrame().evaluateJavaScript(script);
1509                         return;
1510                 }
1511                 
1512                 // Edit existing links
1513                 String js = new String( "function getCursorPos() {"
1514                                 +"var cursorPos;"
1515                                 +"if (window.getSelection) {"
1516                                 +"   var selObj = window.getSelection();"
1517                                 +"   var selRange = selObj.getRangeAt(0);"
1518                                 +"   var workingNode = window.getSelection().anchorNode.parentNode;"
1519                                 +"   while(workingNode != null) { " 
1520                                 +"      if (workingNode.nodeName.toLowerCase()=='a') workingNode.setAttribute('href','" +dialog.getUrl() +"');"
1521                                 +"      workingNode = workingNode.parentNode;"
1522                                 +"   }"
1523                                 +"}"
1524                                 +"} getCursorPos();");
1525                         browser.page().mainFrame().evaluateJavaScript(js);
1526                 
1527                 if (!dialog.getUrl().trim().equals("")) {
1528                         contentChanged();
1529                         return;
1530                 }
1531                 
1532                 // Remove URL
1533                 js = new String( "function getCursorPos() {"
1534                                 +"var cursorPos;"
1535                                 +"if (window.getSelection) {"
1536                                 +"   var selObj = window.getSelection();"
1537                                 +"   var selRange = selObj.getRangeAt(0);"
1538                                 +"   var workingNode = window.getSelection().anchorNode.parentNode;"
1539                                 +"   while(workingNode != null) { " 
1540                                 +"      if (workingNode.nodeName.toLowerCase()=='a') { "
1541                                 +"         workingNode.removeAttribute('href');"
1542                                 +"         workingNode.removeAttribute('title');"
1543                                 +"         var text = document.createTextNode(workingNode.innerText);"
1544                                 +"         workingNode.parentNode.insertBefore(text, workingNode);"
1545                                 +"         workingNode.parentNode.removeChild(workingNode);"
1546                                 +"      }"
1547                                 +"      workingNode = workingNode.parentNode;"
1548                                 +"   }"
1549                                 +"}"
1550                                 +"} getCursorPos();");
1551                         browser.page().mainFrame().evaluateJavaScript(js);
1552                         
1553                         contentChanged();
1554
1555                 
1556         }
1557         
1558         
1559         // Insert a hyperlink
1560         public void insertLatex() {
1561                 editLatex(null);
1562         }
1563         public void editLatex(String guid) {
1564                 logger.log(logger.EXTREME, "Inserting latex");
1565                 String text = browser.selectedText();
1566                 if (text.trim().equalsIgnoreCase("\n") || text.trim().equalsIgnoreCase("")) {
1567                         InsertLatexImage dialog = new InsertLatexImage();
1568                         if (guid != null) {
1569                                 String formula = conn.getNoteTable().noteResourceTable.getNoteSourceUrl(guid).replace("http://latex.codecogs.com/gif.latex?", "");
1570                                 dialog.setFormula(formula);
1571                         }
1572                         dialog.exec();
1573                         if (!dialog.okPressed()) {
1574                                 logger.log(logger.EXTREME, "Edit LaTex canceled");
1575                                 return;
1576                         }
1577                         text = dialog.getFormula().trim();
1578                 }
1579                 blockApplication.emit(this);
1580                 logger.log(logger.EXTREME, "Inserting LaTeX formula:" +text);
1581                 latexGuid = guid;
1582                 text = StringUtils.replace(text, "'", "\\'");
1583                 String url = "http://latex.codecogs.com/gif.latex?" +text;
1584                 logger.log(logger.EXTREME, "Sending request to codecogs --> " + url);
1585                 QNetworkAccessManager manager = new QNetworkAccessManager(this);
1586                 manager.finished.connect(this, "insertLatexImageReady(QNetworkReply)");
1587                 unblockTime = new GregorianCalendar().getTimeInMillis()+5000;
1588                 awaitingHttpResponse = true;
1589                 manager.get(new QNetworkRequest(new QUrl(url)));
1590         }
1591         
1592         public void insertLatexImageReady(QNetworkReply reply) {
1593                 logger.log(logger.EXTREME, "Response received from CodeCogs");
1594                 if (reply.error() != NetworkError.NoError) 
1595                         return;
1596
1597                 unblockTime = -1;
1598                 if (!awaitingHttpResponse)
1599                         return;
1600                 
1601                 awaitingHttpResponse = false;
1602                 QUrl replyUrl = reply.url();            
1603                 QByteArray image = reply.readAll();
1604                 reply.close();
1605                 logger.log(logger.EXTREME, "New image size: " +image.size());
1606
1607                 Resource newRes = null;
1608                 QFile tfile;
1609                 String path;
1610                 if (latexGuid == null) {
1611                         logger.log(logger.EXTREME, "Creating temporary gif");                   
1612                         path = Global.getFileManager().getResDirPath("latex-temp.gif");
1613                         tfile = new QFile(path);
1614                         tfile.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.WriteOnly));
1615                         logger.log(logger.EXTREME, "File Open: " +tfile.errorString());
1616                         tfile.write(image);
1617                         logger.log(logger.EXTREME, "Bytes writtes: "+tfile.size());
1618                         tfile.close();
1619                         logger.log(logger.EXTREME, "Creating resource");
1620                         int sequence = 0;
1621                         if (currentNote.getResources() != null || currentNote.getResources().size() > 0)
1622                                 sequence = currentNote.getResources().size();
1623                         newRes = createResource(path,sequence ,"image/gif", false);
1624                         QImage pix = new QImage();
1625                         pix.loadFromData(image);
1626                         newRes.setHeight(new Integer(pix.height()).shortValue());
1627                         newRes.setWidth(new Integer(pix.width()).shortValue());
1628                         logger.log(logger.EXTREME, "Renaming temporary file to " +newRes.getGuid()+".gif");
1629                         path = Global.getFileManager().getResDirPath(newRes.getGuid()+".gif");
1630                         tfile.rename(path);
1631                 } else {
1632                         newRes = conn.getNoteTable().noteResourceTable.getNoteResource(latexGuid, false);
1633                         path = Global.getFileManager().getResDirPath(newRes.getGuid()+".gif");
1634                         tfile = new QFile(path);
1635                         tfile.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.WriteOnly));
1636                         tfile.write(image);
1637                         tfile.close();
1638                         newRes.getData().setBody(image.toByteArray());
1639                         // Calculate the new hash value
1640                 MessageDigest md;
1641
1642                 logger.log(logger.EXTREME, "Generating MD5");
1643                 try {
1644                                 md = MessageDigest.getInstance("MD5");
1645                         md.update(image.toByteArray());
1646                         byte[] hash = md.digest();
1647                         newRes.getData().setBodyHash(hash);
1648                         } catch (NoSuchAlgorithmException e) {
1649                                 e.printStackTrace();
1650                         }
1651                         QImage pix = new QImage();
1652                         pix.loadFromData(image);
1653                         newRes.setHeight(new Integer(pix.height()).shortValue());
1654                         newRes.setWidth(new Integer(pix.width()).shortValue());
1655                         conn.getNoteTable().noteResourceTable.updateNoteResource(newRes, true);
1656                 }
1657
1658                 logger.log(logger.EXTREME, "Setting source: " +replyUrl.toString());
1659                 newRes.getAttributes().setSourceURL(replyUrl.toString());
1660                 conn.getNoteTable().noteResourceTable.updateNoteSourceUrl(newRes.getGuid(), replyUrl.toString(), true);
1661                 
1662                 for(int i=0; i<currentNote.getResourcesSize(); i++) {
1663                         if (currentNote.getResources().get(i).getGuid().equals(newRes.getGuid())) {
1664                                 currentNote.getResources().remove(i);
1665                                 i=currentNote.getResourcesSize();
1666                         }
1667                 }
1668                 currentNote.getResources().add(newRes);
1669                 
1670
1671                 // do the actual insert into the note.  We only do this on new formulas.  
1672                 if (latexGuid == null) {
1673                         StringBuffer buffer = new StringBuffer(100);
1674                         String formula = replyUrl.toString().toLowerCase().replace("http://latex.codecogs.com/gif.latex?", "");
1675                         buffer.append("<a href=\"latex://"+path.replace("\\", "/")+"\" title=\""+formula+"\""
1676                                         +"><img src=\"");
1677                         buffer.append(path.replace("\\", "/"));
1678                         buffer.append("\" en-tag=\"en-latex\" type=\"image/gif\""
1679                                 +" hash=\""+Global.byteArrayToHexString(newRes.getData().getBodyHash()) +"\""
1680                                 +" guid=\"" +newRes.getGuid() +"\""
1681                                 + " /></a>");
1682                 
1683                         String script_start = new String("document.execCommand('insertHTML', false, '");
1684                         String script_end = new String("');");
1685                         browser.page().mainFrame().evaluateJavaScript(
1686                                         script_start + buffer + script_end);
1687                 } else {
1688                         HtmlTagModifier modifier = new HtmlTagModifier(getContent());
1689                         modifier.modifyLatexTagHash(newRes);
1690                         String newContent = modifier.getHtml();
1691                         setContent(new QByteArray(newContent));
1692                 }
1693
1694                 logger.log(logger.EXTREME, "New HTML set\n" +browser.page().currentFrame().toHtml());
1695                 QWebSettings.setMaximumPagesInCache(0);
1696                 QWebSettings.setObjectCacheCapacities(0, 0, 0);
1697                 
1698                 browser.page().mainFrame().setHtml(browser.page().mainFrame().toHtml());
1699                 browser.reload();
1700                 contentChanged();
1701 //              resourceSignal.contentChanged.emit(path);
1702                 unblockTime = -1;
1703         unblockApplication.emit();
1704                 return;
1705                 
1706         }
1707
1708         
1709         
1710         // Insert a table
1711         public void insertTable() {
1712                 TableDialog dialog = new TableDialog();
1713                 dialog.exec();
1714                 if (!dialog.okPressed()) {
1715                         return;
1716                 }
1717                 
1718                 int cols = dialog.getCols();
1719                 int rows = dialog.getRows();
1720                 int width = dialog.getWidth();
1721                 boolean percent = dialog.isPercent();
1722                 
1723                 String newHTML = "<table border=\"1\" width=\"" +new Integer(width).toString();
1724                 if (percent)
1725                         newHTML = newHTML +"%";
1726                 newHTML = newHTML + "\"><tbody>";
1727
1728                 for (int i=0; i<rows; i++) {
1729                         newHTML = newHTML +"<tr>";
1730                         for (int j=0; j<cols; j++) {
1731                                 newHTML = newHTML +"<td>&nbsp;</td>";
1732                         }
1733                         newHTML = newHTML +"</tr>";
1734                 }
1735                 newHTML = newHTML+"</tbody></table>";   
1736         
1737                 String script = "document.execCommand('insertHtml', false, '"+newHTML+"');";
1738                 browser.page().mainFrame().evaluateJavaScript(script);
1739         }
1740         
1741         
1742         // Text content changed
1743         @SuppressWarnings("unused")
1744         private void selectionChanged() {
1745                 browser.encryptAction.setEnabled(true);
1746                 browser.insertLinkAction.setEnabled(true);
1747                 browser.insertQuickLinkAction.setEnabled(true);
1748                 String scriptStart = "var selection_text = (window.getSelection()).toString();"
1749                                 + "var range = (window.getSelection()).getRangeAt(0);"
1750                                 + "var parent_html = range.commonAncestorContainer.innerHTML;"
1751                                 + "if (parent_html == undefined) {window.jambi.saveSelectedText(selection_text); return;}"
1752                                 + "var first_text = range.startContainer.nodeValue.substr(range.startOffset);"
1753                                 + "var last_text = (range.endContainer.nodeValue).substring(0,range.endOffset);"
1754                                 + "var start = parent_html.indexOf(first_text);"
1755                                 + "var end = parent_html.indexOf(last_text,start+1)+last_text.length;"
1756                                 + "var value = parent_html.substring(start,end);"
1757                                 + "window.jambi.saveSelectedText(value);" ;
1758                 browser.page().mainFrame().evaluateJavaScript(scriptStart);
1759
1760         }
1761
1762         public void saveSelectedText(String text) {
1763                 boolean enabled = true;
1764                 if (text.trim().length() == 0)
1765                         enabled=false;
1766                 if (text.indexOf("en-tag=\"en-crypt\"") >= 0)
1767                         enabled=false;
1768                 if (text.indexOf("<img en-tag=\"en-media\"") >= 0)
1769                         enabled=false;
1770                 if (text.indexOf("<a en-tag=\"en-media\"") >= 0)
1771                         enabled=false;
1772                 if (text.indexOf("<input ") >= 0)
1773                         enabled=false;
1774                 
1775                 browser.encryptAction.setEnabled(enabled);
1776                 browser.insertLinkAction.setEnabled(enabled);
1777                 browser.insertQuickLinkAction.setEnabled(enabled);
1778 //              selectedText = text;
1779         }
1780
1781         // Decrypt clicked text
1782         public void decryptText(String id, String text, String hint) {
1783                 EnCrypt crypt = new EnCrypt();
1784                 String plainText = null;
1785                 Calendar currentTime = new GregorianCalendar();
1786                 Long l = new Long(currentTime.getTimeInMillis());
1787                 String slot = new String(Long.toString(l));
1788                 
1789                 // First, try to decrypt with any keys we already have
1790                 for (int i=0; i<Global.passwordRemember.size(); i++) {
1791                         plainText = crypt.decrypt(text, Global.passwordRemember.get(i).getFirst(), 64);
1792                         if (plainText != null) {
1793                                 slot = new String(Long.toString(l));
1794                                 Global.passwordSafe.put(slot, Global.passwordRemember.get(i));
1795                                 removeEncryption(id, plainText, false, slot);   
1796                                 return;
1797                         }
1798                 }
1799                 
1800                 
1801                 EnDecryptDialog dialog = new EnDecryptDialog();
1802                 dialog.setHint(hint);
1803                 while (plainText == null || !dialog.okPressed()) {
1804                         dialog.exec();
1805                         if (!dialog.okPressed()) {
1806                                 return;
1807                         }
1808                         plainText = crypt.decrypt(text, dialog.getPassword().trim(), 64);
1809                         if (plainText == null) {
1810                                 QMessageBox.warning(this, tr("Incorrect Password"), tr("The password entered is not correct"));
1811                         }
1812                 }
1813                 Pair<String,String> passwordPair = new Pair<String,String>();
1814                 passwordPair.setFirst(dialog.getPassword());
1815                 passwordPair.setSecond(dialog.getHint());
1816                 Global.passwordSafe.put(slot, passwordPair);
1817 //              removeEncryption(id, plainText.replaceAll("\n", "<br/>"), dialog.permanentlyDecrypt(), slot);
1818                 removeEncryption(id, plainText, dialog.permanentlyDecrypt(), slot);
1819                 if (dialog.rememberPassword()) {
1820                         Pair<String, String> pair = new Pair<String,String>();
1821                         pair.setFirst(dialog.getPassword());
1822                         pair.setSecond(dialog.getHint());
1823                         Global.passwordRemember.add(pair);
1824                 }
1825
1826         }
1827
1828         // Get the editor tag line
1829         public TagLineEdit getTagLine() {
1830                 return tagEdit;
1831         }
1832
1833         // Modify a note's tags
1834         @SuppressWarnings("unused")
1835         private void modifyTags() {
1836                 TagAssign tagWindow = new TagAssign(allTags, currentTags, !conn.getNotebookTable().isLinked(currentNote.getNotebookGuid()));
1837                 tagWindow.exec();
1838                 if (tagWindow.okClicked()) {
1839                         currentTags.clear();
1840                         StringBuffer tagDisplay = new StringBuffer();
1841
1842                         List<QListWidgetItem> newTags = tagWindow.getTagList()
1843                                         .selectedItems();
1844                         for (int i = 0; i < newTags.size(); i++) {
1845                                 currentTags.add(newTags.get(i).text());
1846                                 tagDisplay.append(newTags.get(i).text());
1847                                 if (i < newTags.size() - 1) {
1848                                         tagDisplay.append(Global.tagDelimeter + " ");
1849                                 }
1850                         }
1851                         tagEdit.setText(tagDisplay.toString());
1852                         noteSignal.tagsChanged.emit(currentNote.getGuid(), currentTags);
1853                 }
1854         }
1855
1856         // Tag line has been modified by typing text
1857         @SuppressWarnings("unused")
1858         private void modifyTagsTyping() {
1859                 String completionText = "";
1860                 if (tagEdit.currentCompleterSelection != null && !tagEdit.currentCompleterSelection.equals("")) {
1861                         completionText = tagEdit.currentCompleterSelection;
1862                         tagEdit.currentCompleterSelection = "";
1863                 }
1864                 
1865                 if (tagEdit.text().equalsIgnoreCase(saveTagList))
1866                         return;
1867
1868                 if (saveTagList == null) {
1869                         return;
1870                 }
1871                 
1872                 // We know something has changed...
1873                 String oldTagArray[] = saveTagList.split(Global.tagDelimeter);
1874                 String newTagArray[];
1875                 if (!completionText.equals("")) {
1876                         String before = tagEdit.text().substring(0,tagEdit.cursorPosition());
1877                         int lastDelimiter = before.lastIndexOf(Global.tagDelimeter);
1878                         if (lastDelimiter > 0)
1879                                 before = before.substring(0,before.lastIndexOf(Global.tagDelimeter));
1880                         else 
1881                                 before = "";
1882                         String after = tagEdit.text().substring(tagEdit.cursorPosition());
1883                         newTagArray = (before+Global.tagDelimeter+completionText+Global.tagDelimeter+after).split(Global.tagDelimeter);
1884                 }
1885                 else {
1886                         newTagArray = tagEdit.text().split(Global.tagDelimeter);
1887                 }
1888                 
1889                 // Remove any traling or leading blanks
1890                 for (int i=0; i<newTagArray.length; i++)
1891                         newTagArray[i] = newTagArray[i].trim().replaceAll("^\\s+", "");;
1892                 
1893                 // Remove any potential duplicates from the new list
1894                 for (int i=0; i<newTagArray.length; i++) {
1895                         boolean foundOnce = false;
1896                         for (int j=0; j<newTagArray.length; j++) {
1897                                 if (newTagArray[j].equalsIgnoreCase(newTagArray[i])) {
1898                                         if (!foundOnce) {
1899                                                 foundOnce = true;
1900                                         } else
1901                                                 newTagArray[j] = "";
1902                                 }
1903                         }
1904                 }
1905
1906                 List<String> newTagList = new ArrayList<String>();
1907                 List<String> oldTagList = new ArrayList<String>();
1908
1909                 for (int i = 0; i < oldTagArray.length; i++)
1910                         if (!oldTagArray[i].trim().equals(""))
1911                                 oldTagList.add(oldTagArray[i]);
1912                 for (int i = 0; i < newTagArray.length; i++)
1913                         if (!newTagArray[i].trim().equals(""))
1914                                 newTagList.add(newTagArray[i]);
1915
1916                 if (conn.getNotebookTable().isLinked(currentNote.getNotebookGuid())) {
1917                         for (int i=newTagList.size()-1; i>=0; i--) {
1918                                 boolean found = false;
1919                                 for (int j=0; j<allTags.size(); j++) {
1920                                         if (allTags.get(j).getName().equalsIgnoreCase(newTagList.get(i))) {
1921                                                 found = true;
1922                                                 j=allTags.size();
1923                                         }
1924                                 }
1925                                 if (!found)
1926                                         newTagList.remove(i);
1927                         }
1928                 }
1929
1930                 // Let's cleanup the appearance of the tag list
1931                 Collections.sort(newTagList);
1932                 String newDisplay = "";
1933                 for (int i=0; i<newTagList.size(); i++) {
1934                         newDisplay = newDisplay+newTagList.get(i);
1935                         if (i<newTagList.size()-1)
1936                                 newDisplay = newDisplay+Global.tagDelimeter +" ";
1937                 }
1938                 tagEdit.blockSignals(true);
1939                 tagEdit.setText(newDisplay);
1940                 tagEdit.blockSignals(false);
1941                 
1942                 // We now have lists of the new & old. Remove duplicates. If all
1943                 // are removed from both then nothing has really changed
1944                 for (int i = newTagList.size() - 1; i >= 0; i--) {
1945                         String nTag = newTagList.get(i);
1946                         for (int j = oldTagList.size() - 1; j >= 0; j--) {
1947                                 String oTag = oldTagList.get(j);
1948                                 if (oTag.equalsIgnoreCase(nTag)) {
1949                                         oldTagList.remove(j);
1950                                         newTagList.remove(i);
1951                                         j = -1;
1952                                 }
1953                         }
1954                 }
1955
1956                 if (oldTagList.size() != 0 || newTagList.size() != 0) {
1957                         currentTags.clear();
1958                         newTagArray = tagEdit.text().split(Global.tagDelimeter);
1959                         for (int i = 0; i < newTagArray.length; i++)
1960                                 if (!newTagArray[i].trim().equals(""))
1961                                         currentTags.add(newTagArray[i].trim());
1962
1963                         noteSignal.tagsChanged.emit(currentNote.getGuid(), currentTags);
1964                 }
1965                 
1966         }
1967
1968         // Tab button was pressed
1969         public void tabPressed() {
1970                 if (insideEncryption)
1971                         return;
1972                 if (!insideList && !insideTable) {
1973                         String script_start = new String(
1974                         "document.execCommand('insertHtml', false, '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;');");
1975                         browser.page().mainFrame().evaluateJavaScript(script_start);
1976                         return;
1977                 }
1978                 if (insideList) {
1979                         indentClicked();
1980                 }
1981                 if (insideTable) {
1982                         String js = new String( "function getCursorPosition() { "
1983                                         +"   var selObj = window.getSelection();"
1984                                         +"   var selRange = selObj.getRangeAt(0);"
1985                                         +"   var workingNode = window.getSelection().anchorNode;"
1986                                         +"   var rowCount = 0;"
1987                                         +"   var colCount = 0;"
1988                                         +"   while(workingNode != null && workingNode.nodeName.toLowerCase() != 'table') { " 
1989                                         +"      if (workingNode.nodeName.toLowerCase()=='tr') {"
1990                                         +"         rowCount = rowCount+1;"
1991                                         +"      }"
1992                                         +"      if (workingNode.nodeName.toLowerCase() == 'td') {"
1993                                         +"         colCount = colCount+1;"
1994                                         +"      }"
1995                                         +"      if (workingNode.previousSibling != null)"
1996                                         +"          workingNode = workingNode.previousSibling;"
1997                                         +"      else "
1998                                         +"           workingNode = workingNode.parentNode;"
1999                                         +"   }"
2000                                         +"   var nodes = workingNode.getElementsByTagName('tr');"
2001                                         +"   var tableRows = nodes.length;"
2002                                         +"   nodes = nodes[0].getElementsByTagName('td');"
2003                                         +"   var tableColumns = nodes.length;"
2004                                         +"   window.jambi.setTableCursorPositionTab(rowCount, colCount, tableRows, tableColumns);"
2005                                         +"} getCursorPosition();");
2006                         browser.page().mainFrame().evaluateJavaScript(js);
2007                 }
2008         }
2009         
2010         // If a user presses tab from within a table
2011         public void setTableCursorPositionTab(int currentRow, int currentCol, int tableRows, int tableColumns) {
2012                 if (tableRows == currentRow && currentCol == tableColumns) {
2013                         insertTableRow();
2014                 }
2015                 KeyboardModifiers modifiers = new KeyboardModifiers(KeyboardModifier.NoModifier);
2016                 QKeyEvent right = new QKeyEvent(Type.KeyPress, Qt.Key.Key_Right.value(), modifiers);
2017                 QKeyEvent end = new QKeyEvent(Type.KeyPress, Qt.Key.Key_End.value(), modifiers);
2018                 QKeyEvent end2 = new QKeyEvent(Type.KeyPress, Qt.Key.Key_End.value(), modifiers);
2019                 getBrowser().focusWidget();
2020                 QCoreApplication.postEvent(getBrowser(), end);
2021                 QCoreApplication.postEvent(getBrowser(), right);
2022                 QCoreApplication.postEvent(getBrowser(), end2);
2023         }
2024                 
2025         public void backtabPressed() {
2026                 if (insideEncryption) 
2027                         return;
2028                 if (insideList)
2029                         outdentClicked();
2030                 if (insideTable) {
2031                         String js = new String( "function getCursorPosition() { "
2032                                         +"   var selObj = window.getSelection();"
2033                                         +"   var selRange = selObj.getRangeAt(0);"
2034                                         +"   var workingNode = window.getSelection().anchorNode;"
2035                                         +"   var rowCount = 0;"
2036                                         +"   var colCount = 0;"
2037                                         +"   while(workingNode != null && workingNode.nodeName.toLowerCase() != 'table') { " 
2038                                         +"      if (workingNode.nodeName.toLowerCase()=='tr') {"
2039                                         +"         rowCount = rowCount+1;"
2040                                         +"      }"
2041                                         +"      if (workingNode.nodeName.toLowerCase() == 'td') {"
2042                                         +"         colCount = colCount+1;"
2043                                         +"      }"
2044                                         +"      if (workingNode.previousSibling != null)"
2045                                         +"          workingNode = workingNode.previousSibling;"
2046                                         +"      else "
2047                                         +"           workingNode = workingNode.parentNode;"
2048                                         +"   }"
2049                                         +"   var nodes = workingNode.getElementsByTagName('tr');"
2050                                         +"   var tableRows = nodes.length;"
2051                                         +"   nodes = nodes[0].getElementsByTagName('td');"
2052                                         +"   var tableColumns = nodes.length;"
2053                                         +"   window.jambi.setTableCursorPositionBackTab(rowCount, colCount, tableRows, tableColumns);"
2054                                         +"} getCursorPosition();");
2055                         browser.page().mainFrame().evaluateJavaScript(js);
2056                         
2057                 }
2058         }
2059         
2060         // If a user presses backtab from within a table
2061         public void setTableCursorPositionBackTab(int currentRow, int currentCol, int tableRows, int tableColumns) {
2062                 if (currentRow  == 1 && currentCol == 1) {
2063                         return;
2064                 }
2065                 KeyboardModifiers modifiers = new KeyboardModifiers(KeyboardModifier.NoModifier);
2066                 QKeyEvent left = new QKeyEvent(Type.KeyPress, Qt.Key.Key_Left.value(), modifiers);
2067                 QKeyEvent home = new QKeyEvent(Type.KeyPress, Qt.Key.Key_Home.value(), modifiers);
2068                 getBrowser().focusWidget();
2069                 QCoreApplication.postEvent(getBrowser(), home);
2070                 QCoreApplication.postEvent(getBrowser(), left);
2071         }
2072         
2073         
2074         public void setInsideList() {
2075                 insideList = true;
2076         }
2077         
2078         // The title has been edited
2079         @SuppressWarnings("unused")
2080         private void titleEdited() {
2081                 // If we don't have a good note, or if the current title
2082                 // matches the old title then we don't need to do anything
2083                 if (currentNote == null)
2084                         return;
2085                 if (currentNote.getTitle().trim().equals(titleLabel.text().trim()))
2086                         return;
2087                 
2088                 // If we have a real change, we need to save it.
2089                 String text = titleLabel.text().trim();
2090                 if (text.equals("")) 
2091                         text = tr("Untitled Note");
2092                 noteSignal.titleChanged.emit(currentNote.getGuid(), text);
2093                 currentNote.setTitle(text);
2094                 saveNoteTitle = text;
2095                 checkNoteTitle();
2096         }
2097
2098         // Set the list of note tags
2099         public void setAllTags(List<Tag> l) {
2100                 allTags = l;
2101                 tagEdit.setTagList(l);
2102         }
2103
2104         // Setter for the current tags
2105         public void setCurrentTags(List<String> s) {
2106                 currentTags = s;
2107         }
2108
2109         // Save the list of notebooks
2110         public void setNotebookList(List<Notebook> n) {
2111                 notebookList = n;
2112                 loadNotebookList();
2113         }
2114
2115         // Load the notebook list and select the current notebook
2116         private void loadNotebookList() {
2117                 if (notebookBox.count() != 0)
2118                         notebookBox.clear();
2119                 if (notebookList == null)
2120                         return;
2121
2122                 for (int i = 0; i < notebookList.size(); i++) {
2123                         notebookBox.addItem(notebookList.get(i).getName());
2124                         if (currentNote != null) {
2125                                 if (currentNote.getNotebookGuid().equals(
2126                                                 notebookList.get(i).getGuid())) {
2127                                         notebookBox.setCurrentIndex(i);
2128                                 }
2129                         }
2130                 }
2131         }
2132         
2133         
2134         // Set the notebook for a note
2135         public void setNotebook(String notebook) {
2136                 currentNote.setNotebookGuid(notebook);
2137                 loadNotebookList();
2138         }
2139
2140         // Get the contents of the editor
2141         public String getContent() {
2142                 return browser.page().currentFrame().toHtml();
2143         }
2144
2145         // The note contents have changed
2146         public void contentChanged() {
2147                 String content = getContent();
2148                 
2149                 // This puts in a 1/2 second delay
2150                 // before updating the source editor.
2151                 // It improves response when someone is doing
2152                 // frequent updates on a large note.
2153                 // If the source editor isn't visible, then there
2154                 // is no point to doing any of this.
2155                 if (sourceEdit.isVisible()) {
2156                         setSourceTimer.stop();
2157                         setSourceTimer.setInterval(500);
2158                         setSourceTimer.setSingleShot(true);
2159                         setSourceTimer.start();
2160                 }
2161                 
2162                 checkNoteTitle();
2163                 noteSignal.noteChanged.emit(currentNote.getGuid(), content); 
2164         }
2165
2166         // The notebook selection has changed
2167         @SuppressWarnings("unused")
2168         private void notebookChanged() {
2169                 boolean changed = false;
2170                 String n = notebookBox.currentText();
2171                 for (int i = 0; i < notebookList.size(); i++) {
2172                         if (n.equals(notebookList.get(i).getName())) {
2173                                 if (!notebookList.get(i).getGuid().equals(currentNote.getNotebookGuid())) {
2174                                         String guid = conn.getNotebookTable().findNotebookByName(n);
2175                                         if (conn.getNotebookTable().isLinked(guid)) {
2176                                                 tagEdit.setText("");
2177                                                 noteSignal.tagsChanged.emit(currentNote.getGuid(), new ArrayList<String>());
2178                                                 FilterEditorTags t = new FilterEditorTags(conn, logger);
2179                                                 setAllTags(t.getValidTags(currentNote));
2180                                         }
2181                                         currentNote.setNotebookGuid(notebookList.get(i).getGuid());
2182                                         changed = true;
2183                                 }
2184                                 i = notebookList.size();
2185                         }
2186                 }
2187                 
2188                 // If the notebook changed, signal the update
2189                 if (changed)
2190                         noteSignal.notebookChanged.emit(currentNote.getGuid(), currentNote
2191                                         .getNotebookGuid());
2192         }
2193
2194         // Check the note title
2195         private void checkNoteTitle() {
2196                 String text = browser.page().currentFrame().toPlainText();
2197                 if (saveNoteTitle == null)
2198                         saveNoteTitle = new String();
2199                 text = text.trim();
2200                 if (!saveNoteTitle.trim().equals("") && !saveNoteTitle.trim().equals("Untitled Note"))
2201                         text = saveNoteTitle.trim();
2202                 int newLine = text.indexOf("\n");
2203                 if (newLine > 0)
2204                         text = text.substring(0,newLine);
2205                 if (saveNoteTitle.trim().equals("") || saveNoteTitle.trim().equals("Untitled Note")) {
2206                         if (text.trim().equals(""))
2207                                 text = tr("Untitled Note");
2208                                 titleLabel.setText(text);
2209                 } else {
2210                         if (text.length() > Constants.EDAM_NOTE_TITLE_LEN_MAX)
2211                                 titleLabel.setText(text.substring(0, Constants.EDAM_NOTE_TITLE_LEN_MAX));
2212                         else {
2213                                 titleLabel.blockSignals(true);
2214                                 if (text.trim().equals(""))
2215                                         titleLabel.setText(tr("Untitled Note"));
2216                                 else
2217                                         titleLabel.setText(text);
2218                                 titleLabel.blockSignals(false);
2219                         }
2220                 }
2221                 if (currentNote != null && titleLabel != null && !currentNote.getTitle().equals(text))
2222                         noteSignal.titleChanged.emit(currentNote.getGuid(), text);
2223         }
2224
2225         // Return the note contents so we can email them
2226         public String getContentsToEmail() {
2227                 return browser.page().currentFrame().toPlainText().trim();
2228                 /*
2229                  * int body = browser.page().currentFrame().toHtml().indexOf("<body>");
2230                  * String temp = browser.page().currentFrame().toHtml(); if (body == -1)
2231                  * temp = "<html><body><b>Test</b></body></html>"; else temp =
2232                  * "<html>"+temp.substring(body); return temp; // return
2233                  * urlEncode(browser.page().currentFrame().toHtml());
2234                  */
2235         }
2236
2237         // Insert an image into the editor
2238         private void insertImage(QMimeData mime) {
2239                 logger.log(logger.EXTREME, "Entering insertImage");
2240                 QImage img = (QImage) mime.imageData();
2241                 String script_start = new String(
2242                                 "document.execCommand('insertHTML', false, '");
2243                 String script_end = new String("');");
2244
2245                 long now = new Date().getTime();
2246                 String path = Global.getFileManager().getResDirPath(
2247                                 (new Long(now).toString()) + ".jpg");
2248
2249                 // This block is just a hack to make sure we wait at least 1ms so we
2250                 // don't
2251                 // have collisions on image names
2252                 long i = new Date().getTime();
2253                 while (now == i)
2254                         i = new Date().getTime();
2255
2256                 // Open the file & write the data
2257                 QFile tfile = new QFile(path);
2258                 tfile.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.WriteOnly));
2259                 if (!img.save(tfile)) {
2260                         tfile.close();
2261                         return;
2262                 }
2263                 tfile.close();
2264                 
2265                 Resource newRes = createResource(QUrl.fromLocalFile(path).toString(), 0, "image/jpeg", false);
2266                 if (newRes == null)
2267                         return;
2268                 currentNote.getResources().add(newRes);
2269
2270                 // do the actual insert into the note
2271                 StringBuffer buffer = new StringBuffer(100);
2272                 buffer.append("<img src=\"");
2273                 buffer.append(tfile.fileName());
2274                 buffer.append("\" en-tag=en-media type=\"image/jpeg\""
2275                                 +" hash=\""+Global.byteArrayToHexString(newRes.getData().getBodyHash()) +"\""
2276                                 +" guid=\"" +newRes.getGuid() +"\""
2277                                 +" onContextMenu=\"window.jambi.imageContextMenu(&amp." +tfile.fileName() +"&amp.);\""
2278                                 + " />");
2279                 
2280                 browser.page().mainFrame().evaluateJavaScript(
2281                                 script_start + buffer + script_end);
2282
2283                 return;
2284         }
2285
2286         // Handle pasting of a note-to-note link
2287         private void handleNoteLink(QMimeData mime) {
2288                 for (int i=0; i<mime.urls().size(); i++) {
2289                         StringTokenizer tokens = new StringTokenizer(mime.urls().get(i).toString().replace("evernote:///view/", ""), "/");
2290                         tokens.nextToken();
2291                         tokens.nextToken();
2292                         String sid = tokens.nextToken();
2293                         String lid = tokens.nextToken();
2294                         
2295                         if (!sid.equals(currentNote.getGuid()) && !lid.equals(currentNote.getGuid())) {
2296                                 
2297                                 Note note = conn.getNoteTable().getNote(sid, false, false, false, false, false);
2298                                 if (note == null)
2299                                         note = conn.getNoteTable().getNote(lid, false, false, false, false, false);
2300                 
2301                                 if (note == null)
2302                                         return;
2303
2304                                 // If we've gotten this far, we have a bunch of values.  We need to build the link.
2305                                 StringBuffer url = new StringBuffer(100);
2306                                 String script_start = new String(
2307                                         "document.execCommand('insertHtml', false, '");
2308                                 String script_end = new String("');");
2309         
2310                                 url.append("<a href=\""+mime.urls().get(i).toString() +"\" style=\"color:#69aa35\">");
2311                                 url.append(note.getTitle());
2312                                 url.append("</a>");
2313                                 if (mime.urls().size() > 1)
2314                                         url.append("&nbsp;");
2315                                 browser.page().mainFrame().evaluateJavaScript(
2316                                                 script_start + url + script_end);
2317                         }
2318                 }
2319         }
2320         
2321         // Handle URLs that are trying to be pasted
2322         public void handleUrls(QMimeData mime) {
2323                 logger.log(logger.EXTREME, "Starting handleUrls");
2324                 FileNameMap fileNameMap = URLConnection.getFileNameMap();
2325
2326                 List<QUrl> urlList = mime.urls();
2327                 String url = new String();
2328                 String script_start = new String(
2329                                 "document.execCommand('createLink', false, '");
2330                 String script_end = new String("');");
2331
2332                 for (int i = 0; i < urlList.size(); i++) {
2333                         url = urlList.get(i).toString();
2334                         // Find out what type of file we have
2335                         String mimeType = fileNameMap.getContentTypeFor(url);
2336
2337                         // If null returned, we need to guess at the file type
2338                         if (mimeType == null)
2339                                 mimeType = "application/"
2340                                                 + url.substring(url.lastIndexOf(".") + 1);
2341
2342                         // Check if we have an image or some other type of file
2343                         if (url.substring(0, 5).equalsIgnoreCase("file:")
2344                                         && mimeType.substring(0, 5).equalsIgnoreCase("image")) {
2345                                 handleLocalImageURLPaste(mime, mimeType);
2346                                 return;
2347                         }
2348
2349                         boolean smallEnough = checkFileAttachmentSize(url);
2350                         if (smallEnough 
2351                                         && url.substring(0, 5).equalsIgnoreCase("file:")
2352                                         && !mimeType.substring(0, 5).equalsIgnoreCase("image")) {
2353                                 handleLocalAttachment(mime, mimeType);
2354                                 return;
2355                         }
2356                         browser.page().mainFrame().evaluateJavaScript(
2357                                         script_start + url + script_end);
2358                 }
2359                 return;
2360         }
2361
2362         // If a URL being pasted is an image URL, then attach the image
2363         private void handleLocalImageURLPaste(QMimeData mime, String mimeType) {
2364                 List<QUrl> urlList = mime.urls();
2365                 String url = new String();
2366                 String script_start_image = new String(
2367                                 "document.execCommand('insertHtml', false, '");
2368                 String script_end = new String("');");
2369                 StringBuffer buffer;
2370
2371                 // Copy the image over into the resource directory and create a new resource 
2372                 // record for each url pasted
2373                 for (int i = 0; i < urlList.size(); i++) {
2374                         url = urlList.get(i).toString();
2375
2376                         Resource newRes = createResource(url, i, mimeType, false);
2377                         if (newRes == null)
2378                                 return;
2379                         currentNote.getResources().add(newRes);
2380                         buffer = new StringBuffer(100);
2381                         
2382                         // Open the file & write the data
2383                         String fileName = Global.getFileManager().getResDirPath(newRes.getGuid());
2384                         QFile tfile = new QFile(fileName);
2385                         tfile.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.WriteOnly));
2386                         tfile.write(newRes.getData().getBody());
2387                         tfile.close();
2388                         buffer.append(script_start_image);
2389                         buffer.append("<img src=\"" + FileUtils.toForwardSlashedPath(fileName));
2390 //                      if (mimeType.equalsIgnoreCase("image/jpg"))
2391 //                              mimeType = "image/jpeg";
2392                         buffer.append("\" en-tag=\"en-media\" type=\"" + mimeType +"\""
2393                                         +" hash=\""+Global.byteArrayToHexString(newRes.getData().getBodyHash()) +"\""
2394                                         +" guid=\"" +newRes.getGuid() +"\""
2395                                         +" onContextMenu=\"window.jambi.imageContextMenu(&apos;" +tfile.fileName() +"&apos;);\""
2396                                         + " />");
2397                         buffer.append(script_end);
2398                         browser.page().mainFrame().evaluateJavaScript(buffer.toString());
2399                 }
2400                 return;
2401         }
2402         
2403
2404         // If a URL being pasted is a local file URL, then attach the file
2405         private void handleLocalAttachment(QMimeData mime, String mimeType) {
2406                 logger.log(logger.EXTREME, "Attaching local file");
2407                 List<QUrl> urlList = mime.urls();
2408                 String script_start = new String(
2409                                 "document.execCommand('insertHtml', false, '");
2410                 String script_end = new String("');");
2411                 StringBuffer buffer;
2412
2413                         String[] type = mimeType.split("/");
2414                         String icon = findIcon(type[1]);
2415                         if (icon.equals("attachment.png"))
2416                                 icon = findIcon(type[0]);
2417                         buffer = new StringBuffer(100);
2418
2419                 for (int i = 0; i < urlList.size(); i++) {
2420                         String url = urlList.get(i).toString();
2421
2422                         // Start building the HTML
2423                         if (icon.equals("attachment.png"))
2424                                 icon = findIcon(url.substring(url.lastIndexOf(".")+1));
2425                         String imageURL = FileUtils.toFileURLString(Global.getFileManager().getImageDirFile(icon));
2426
2427                         logger.log(logger.EXTREME, "Creating resource ");
2428                         Resource newRes = createResource(url, i, mimeType, true);
2429                         if (newRes == null)
2430                                 return;
2431                         logger.log(logger.EXTREME, "New resource size: " +newRes.getData().getSize());
2432                         currentNote.getResources().add(newRes);
2433                         
2434                         String fileName = newRes.getGuid() + Global.attachmentNameDelimeter+newRes.getAttributes().getFileName();
2435                         // If we have a PDF, we need to setup the preview.
2436                         if (icon.equalsIgnoreCase("pdf.png") && Global.pdfPreview()) {
2437                                 logger.log(logger.EXTREME, "Setting up PDF preview");
2438                                 if (newRes.getAttributes() != null && 
2439                                                 newRes.getAttributes().getFileName() != null && 
2440                                                 !newRes.getAttributes().getFileName().trim().equals(""))
2441                                         fileName = newRes.getGuid()+Global.attachmentNameDelimeter+
2442                                                 newRes.getAttributes().getFileName();
2443                                 else
2444                                         fileName = newRes.getGuid()+".pdf";
2445                                 QFile file = new QFile(Global.getFileManager().getResDirPath(fileName));
2446                         QFile.OpenMode mode = new QFile.OpenMode();
2447                         mode.set(QFile.OpenModeFlag.WriteOnly);
2448                         file.open(mode);
2449                         QDataStream out = new QDataStream(file);
2450 //                      Resource resBinary = conn.getNoteTable().noteResourceTable.getNoteResource(newRes.getGuid(), true);
2451                                 QByteArray binData = new QByteArray(newRes.getData().getBody());
2452 //                              resBinary = null;
2453                         out.writeBytes(binData.toByteArray());
2454                         file.close();
2455
2456                                 PDFPreview pdfPreview = new PDFPreview();
2457                                 if (pdfPreview.setupPreview(Global.getFileManager().getResDirPath(fileName), "pdf",0)) {
2458                                 imageURL = file.fileName() + ".png";
2459                                 }
2460                         }
2461                         
2462                         logger.log(logger.EXTREME, "Generating link tags");
2463                         buffer.delete(0, buffer.length());
2464                         buffer.append("<a en-tag=\"en-media\" guid=\"" +newRes.getGuid()+"\" ");
2465                         buffer.append(" onContextMenu=\"window.jambi.imageContextMenu(&apos;")
2466                       .append(Global.getFileManager().getResDirPath(fileName))
2467                       .append("&apos;);\" ");                   
2468                         buffer.append("type=\"" + mimeType + "\" href=\"nnres://" + fileName +"\" hash=\""+Global.byteArrayToHexString(newRes.getData().getBodyHash()) +"\" >");
2469                         buffer.append("<img src=\"" + imageURL + "\" title=\"" +newRes.getAttributes().getFileName());
2470                         buffer.append("\"></img>");
2471                         buffer.append("</a>");
2472                         browser.page().mainFrame().evaluateJavaScript(
2473                                         script_start + buffer.toString() + script_end);
2474                 }
2475                 return;
2476         }
2477
2478         private Resource createResource(String url, int sequence, String mime, boolean attachment) {
2479                 logger.log(logger.EXTREME, "Inside create resource");
2480                 QFile resourceFile; 
2481                 //These two lines are added to handle odd characters in the name like #.  Without it
2482                 // toLocalFile() chokes and returns the wrong name.
2483                 logger.log(logger.EXTREME, "File URL:" +url);
2484                 String whichOS = System.getProperty("os.name");
2485                 if (whichOS.contains("Windows")) 
2486                         url = url.replace("file:///", "");
2487                 else
2488                         url = url.replace("file://", "");
2489                 String urlTest = new QUrl(url).toLocalFile();
2490                 logger.log(logger.EXTREME, "File URL toLocalFile():" +urlTest);
2491                 urlTest = url;
2492                 if (!urlTest.equals(""))
2493                         url = urlTest;
2494 //              url = url.replace("/", File.separator);
2495                 logger.log(logger.EXTREME, "Reading from file to create resource:" +url);
2496                 resourceFile = new QFile(url); 
2497         resourceFile.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.ReadOnly));
2498         logger.log(logger.EXTREME, "Error opening file "+url.toString()  +": "+resourceFile.errorString());
2499         byte[] fileData = resourceFile.readAll().toByteArray();
2500         resourceFile.close();
2501         logger.log(logger.EXTREME, "File Length: " +fileData.length);
2502         if (fileData.length == 0)
2503                 return null;
2504         MessageDigest md;
2505         try {
2506                 logger.log(logger.EXTREME, "Generating MD5");
2507                 md = MessageDigest.getInstance("MD5");
2508                 md.update(fileData);
2509                 byte[] hash = md.digest();
2510   
2511                 Resource r = new Resource();
2512                 Calendar time = new GregorianCalendar();
2513                 long prevTime = time.getTimeInMillis();
2514                 while (prevTime == time.getTimeInMillis()) {
2515                         time = new GregorianCalendar();
2516                 }
2517                 r.setGuid(time.getTimeInMillis()+new Integer(sequence).toString());
2518                 r.setNoteGuid(currentNote.getGuid());
2519                 r.setMime(mime);
2520                 r.setActive(true);
2521                 r.setUpdateSequenceNum(0);
2522                 r.setWidth((short) 0);
2523                 r.setHeight((short) 0);
2524                 r.setDuration((short) 0);
2525                                 
2526                 Data d = new Data();
2527                 d.setBody(fileData);
2528                 d.setBodyIsSet(true);
2529                 d.setBodyHash(hash);
2530                 d.setBodyHashIsSet(true);
2531                 r.setData(d);
2532                 d.setSize(fileData.length);
2533                 
2534                 int fileNamePos = url.lastIndexOf(File.separator);
2535                 if (fileNamePos == -1)
2536                         fileNamePos = url.lastIndexOf("/");
2537                         String fileName = url.substring(fileNamePos+1);
2538                 ResourceAttributes a = new ResourceAttributes();
2539                 a.setAltitude(0);
2540                 a.setAltitudeIsSet(false);
2541                 a.setLongitude(0);
2542                 a.setLongitudeIsSet(false);
2543                 a.setLatitude(0);
2544                 a.setLatitudeIsSet(false);
2545                 a.setCameraMake("");
2546                 a.setCameraMakeIsSet(false);
2547                 a.setCameraModel("");
2548                 a.setCameraModelIsSet(false);
2549                 a.setAttachment(attachment);
2550                 a.setAttachmentIsSet(true);
2551                 a.setClientWillIndex(false);
2552                 a.setClientWillIndexIsSet(true);
2553                 a.setRecoType("");
2554                 a.setRecoTypeIsSet(false);
2555                 a.setSourceURL(url);
2556                 a.setSourceURLIsSet(true);
2557                 a.setTimestamp(0);
2558                 a.setTimestampIsSet(false);
2559                 a.setFileName(fileName);
2560                 a.setFileNameIsSet(true);
2561                 r.setAttributes(a);
2562                 
2563                 conn.getNoteTable().noteResourceTable.saveNoteResource(r, true);
2564                 logger.log(logger.EXTREME, "Resource created");
2565                 return r;
2566         } catch (NoSuchAlgorithmException e1) {
2567                 e1.printStackTrace();
2568                 }
2569         return null;
2570         }
2571         
2572
2573     // find the appropriate icon for an attachment
2574     private String findIcon(String appl) {
2575         appl = appl.toLowerCase();
2576         File f = Global.getFileManager().getImageDirFile(appl + ".png");
2577         if (f.exists())
2578                 return appl+".png";
2579         return "attachment.png";
2580     }
2581
2582
2583
2584         // Check the file attachment to be sure it isn't over 25 mb
2585         private boolean checkFileAttachmentSize(String url) {
2586                 String fileName = url.substring(8);
2587                 QFile resourceFile = new QFile(fileName);
2588                 resourceFile.open(new QIODevice.OpenMode(
2589                                 QIODevice.OpenModeFlag.ReadOnly));
2590                 long size = resourceFile.size();
2591                 resourceFile.close();
2592                 size = size / 1024 / 1024;
2593                 if (size < 50 && Global.isPremium())
2594                         return true;
2595                 if (size < 25)
2596                         return true;
2597
2598                 String error = tr("A file attachment may not exceed 25MB.");
2599                 QMessageBox.information(this, tr("Attachment Size"), error);
2600                 return false;
2601         }
2602
2603
2604         @SuppressWarnings("unused")
2605         private void createdChanged() {
2606                 QDateTime dt = new QDateTime();
2607                 dt.setDate(createdDate.date());
2608                 dt.setTime(createdTime.time());
2609                 noteSignal.createdDateChanged.emit(currentNote.getGuid(), dt);
2610
2611         }
2612
2613         @SuppressWarnings("unused")
2614         private void alteredChanged() {
2615                 QDateTime dt = new QDateTime();
2616                 dt.setDate(alteredDate.date());
2617                 dt.setTime(alteredTime.time());
2618                 noteSignal.alteredDateChanged.emit(currentNote.getGuid(), dt);
2619         }
2620
2621         @SuppressWarnings("unused")
2622         private void subjectDateTimeChanged() {
2623                 QDateTime dt = new QDateTime();
2624                 dt.setDate(subjectDate.date());
2625                 dt.setTime(subjectTime.time());
2626                 noteSignal.subjectDateChanged.emit(currentNote.getGuid(), dt);
2627
2628         }
2629
2630         @SuppressWarnings("unused")
2631         private void sourceUrlChanged() {
2632                 noteSignal.sourceUrlChanged.emit(currentNote.getGuid(), urlText.text());
2633         }
2634
2635         @SuppressWarnings("unused")
2636         private void authorChanged() {
2637                 noteSignal.authorChanged.emit(currentNote.getGuid(), authorText.text());
2638         }
2639         
2640         @SuppressWarnings("unused")
2641         private void geoBoxChanged() {
2642                 int index = geoBox.currentIndex();
2643                 geoBox.setCurrentIndex(0);
2644                 if (index == 1) {
2645                         GeoDialog box = new GeoDialog();
2646                         box.setLongitude(currentNote.getAttributes().getLongitude());
2647                         box.setLatitude(currentNote.getAttributes().getLatitude());
2648                         box.setAltitude(currentNote.getAttributes().getAltitude());
2649                         box.exec();
2650                         if (!box.okPressed())
2651                                 return;
2652                         double alt = box.getAltitude();
2653                         double lat = box.getLatitude();
2654                         double lon = box.getLongitude();
2655                         if (alt != currentNote.getAttributes().getAltitude() ||
2656                                 lon != currentNote.getAttributes().getLongitude() ||
2657                                 lat != currentNote.getAttributes().getLatitude()) {
2658                                         noteSignal.geoChanged.emit(currentNote.getGuid(), lon, lat, alt);
2659                                         currentNote.getAttributes().setAltitude(alt);
2660                                         currentNote.getAttributes().setLongitude(lon);
2661                                         currentNote.getAttributes().setLatitude(lat);
2662                         }
2663                 }
2664                 
2665                 if (index == 2) {
2666                         noteSignal.geoChanged.emit(currentNote.getGuid(), 0.0, 0.0, 0.0);
2667                         currentNote.getAttributes().setAltitude(0.0);
2668                         currentNote.getAttributes().setLongitude(0.0);
2669                         currentNote.getAttributes().setLatitude(0.0);
2670                 }
2671                 
2672                 if (index == 3 || index == 0) {
2673                         QDesktopServices.openUrl(new QUrl("http://maps.google.com/maps?z=6&q="+currentNote.getAttributes().getLatitude() +"," +currentNote.getAttributes().getLongitude()));
2674                 }
2675         }
2676
2677         // ************************************************************
2678         // * User chose to save an attachment. Pares out the request *
2679         // * into a guid & file. Save the result. *
2680         // ************************************************************
2681         public void downloadAttachment(QNetworkRequest request) {
2682                 String guid;
2683                 QFileDialog fd = new QFileDialog(this);
2684                 fd.setFileMode(FileMode.AnyFile);
2685                 fd.setConfirmOverwrite(true);
2686                 fd.setWindowTitle(tr("Save File"));
2687                 fd.setAcceptMode(AcceptMode.AcceptSave);
2688                 fd.setDirectory(System.getProperty("user.home"));
2689                 String name = request.url().toString();
2690
2691                 int pos = name.lastIndexOf(Global.attachmentNameDelimeter);
2692                 if (pos > -1) {
2693                         guid = name.substring(0, pos).replace("nnres://", "");
2694                         name = name.substring(pos +Global.attachmentNameDelimeter.length());
2695                         fd.selectFile(name);
2696                         pos = name.lastIndexOf('.');
2697                         if (pos > -1) {
2698                                 String mimeType = "(*." + name.substring(pos + 1)
2699                                                 + ");; All Files (*)";
2700                                 fd.setFilter(tr(mimeType));
2701                         }
2702                 } else {
2703                         guid = name;
2704                 }
2705
2706                 // Strip URL prefix and base dir
2707                 guid = guid.replace("nnres://", "")
2708                         .replace(FileUtils.toForwardSlashedPath(Global.getFileManager().getResDirPath()), "");
2709                 guid = guid.replace("file://", "").replace("/", "")
2710                 .replace(FileUtils.toForwardSlashedPath(Global.getFileManager().getResDirPath()), "");
2711
2712                 pos = guid.lastIndexOf('.');
2713                 if (pos > 0)
2714                         guid = guid.substring(0,pos);
2715                 if (fd.exec() != 0 && fd.selectedFiles().size() > 0) {
2716                         name = name.replace('\\', '/');
2717                         Resource resBinary = conn.getNoteTable().noteResourceTable.getNoteResource(guid, true);
2718                         QFile saveFile = new QFile(fd.selectedFiles().get(0));
2719                         QFile.OpenMode mode = new QFile.OpenMode();
2720                         mode.set(QFile.OpenModeFlag.WriteOnly);
2721                         saveFile.open(mode);
2722                         QDataStream saveOut = new QDataStream(saveFile);
2723                         QByteArray binData = new QByteArray(resBinary.getData().getBody());
2724                         saveOut.writeBytes(binData.toByteArray());
2725                         saveFile.close();
2726
2727                 }
2728         }
2729
2730         
2731         // ************************************************************
2732         // * User chose to save an attachment. Pares out the request *
2733         // * into a guid & file. Save the result. --- DONE FROM downloadAttachment now!!!!!   
2734         // ************************************************************
2735         public void downloadImage(QNetworkRequest request) {
2736                 QFileDialog fd = new QFileDialog(this);
2737                 fd.setFileMode(FileMode.AnyFile);
2738                 fd.setConfirmOverwrite(true);
2739                 fd.setWindowTitle(tr("Save File"));
2740                 fd.setAcceptMode(AcceptMode.AcceptSave);
2741                 fd.setDirectory(System.getProperty("user.home"));
2742                 String name = request.url().toString();
2743                 name = name.replace("nnres://", "");
2744                 String dPath = FileUtils.toForwardSlashedPath(Global.getFileManager().getResDirPath());
2745                 name = name.replace(dPath, "");
2746                 int pos = name.lastIndexOf('.');
2747                 String guid = name;
2748                 if (pos > -1) {
2749                         String mimeType = "(*." + name.substring(pos + 1)
2750                         + ");; All Files (*)";
2751                                 fd.setFilter(tr(mimeType));
2752                         guid = guid.substring(0,pos);
2753                 }
2754                 pos = name.lastIndexOf(Global.attachmentNameDelimeter);
2755                 if (pos > -1) {
2756                         guid = name.substring(0, pos);
2757                         fd.selectFile(name.substring(pos+Global.attachmentNameDelimeter.length()));             
2758                 }
2759                 if (fd.exec() != 0 && fd.selectedFiles().size() > 0) {
2760                         Resource resBinary = conn.getNoteTable().noteResourceTable.getNoteResource(guid, true);
2761                         String fileName = fd.selectedFiles().get(0);
2762                         QFile saveFile = new QFile(fileName);
2763                         QFile.OpenMode mode = new QFile.OpenMode();
2764                         mode.set(QFile.OpenModeFlag.WriteOnly);
2765                         saveFile.open(mode);
2766                         QDataStream saveOut = new QDataStream(saveFile);
2767                         QByteArray binData = new QByteArray(resBinary.getData().getBody());
2768                         saveOut.writeBytes(binData.toByteArray());
2769                         saveFile.close();
2770                 }
2771         }
2772
2773         
2774         // *************************************************************
2775         // * decrypt any hidden text.  We could do an XML parse, but 
2776         // * it is quicker here just to scan for an <img tag & do the fix
2777         // * the manual way
2778         // *************************************************************
2779         private void removeEncryption(String id, String plainText, boolean permanent, String slot) {
2780                 if (!permanent) {
2781                         plainText = " <table class=\"en-crypt-temp\" slot=\""
2782                                         +slot 
2783                                         +"\""
2784                                         +"border=1 width=100%><tbody><tr><td>"
2785                                         +plainText+"</td></tr></tbody></table>";
2786                 }
2787                 
2788                 String html = browser.page().mainFrame().toHtml();
2789                 String text = html;
2790                 int imagePos = html.indexOf("<img");
2791                 int endPos;
2792                 for ( ;imagePos>0; ) {
2793                         // Find the end tag
2794                         endPos = text.indexOf(">", imagePos);
2795                         String tag = text.substring(imagePos-1,endPos);
2796                         if (tag.indexOf("id=\""+id+"\"") > -1) {
2797                                         text = text.substring(0,imagePos) +plainText+text.substring(endPos+1);  
2798                                         QTextCodec codec = QTextCodec.codecForName("UTF-8");
2799                                 QByteArray unicode =  codec.fromUnicode(text);
2800                                         setContent(unicode);
2801                                         if (permanent)
2802                                                 contentChanged();
2803                         }
2804                         imagePos = text.indexOf("<img", imagePos+1);
2805                 }
2806         }
2807         
2808         
2809         //****************************************************************
2810         //* Focus shortcuts
2811         //****************************************************************
2812         @SuppressWarnings("unused")
2813         private void focusTitle() {
2814                 titleLabel.setFocus();
2815         }
2816         @SuppressWarnings("unused")
2817         private void focusTag() {
2818                 tagEdit.setFocus();
2819         }
2820         @SuppressWarnings("unused")
2821         private void focusNote() {
2822                 browser.setFocus();
2823         }
2824         @SuppressWarnings("unused")
2825         private void focusAuthor() {
2826                 authorLabel.setFocus();
2827         }
2828         @SuppressWarnings("unused")
2829         private void focusUrl() {
2830                 urlLabel.setFocus();
2831         }
2832         
2833
2834         //*****************************************************************
2835         //* Set the document background color
2836         //*****************************************************************
2837         public void setBackgroundColor(String color) {
2838                 String js = "function changeBackground(color) {"
2839                         +"document.body.style.background = color;"
2840                         +"}" 
2841                         +"changeBackground('" +color+"');";
2842                 browser.page().mainFrame().evaluateJavaScript(js);
2843                 contentChanged();
2844         }
2845         
2846         
2847         //****************************************************************
2848         //* MicroFocus changed
2849         //****************************************************************
2850         private void microFocusChanged() {
2851                 boldButton.setDown(false);
2852                 italicButton.setDown(false);
2853                 underlineButton.setDown(false);
2854                 browser.openAction.setEnabled(false);
2855                 browser.downloadAttachment.setEnabled(false);
2856                 browser.downloadImage.setEnabled(false);
2857                 browser.rotateImageLeft.setEnabled(false);
2858                 browser.rotateImageRight.setEnabled(false);
2859                 browser.insertTableAction.setEnabled(true);
2860                 browser.deleteTableColumnAction.setEnabled(false);
2861                 browser.insertTableRowAction.setEnabled(false);
2862                 browser.insertTableColumnAction.setEnabled(false);
2863                 browser.deleteTableRowAction.setEnabled(false);
2864                 browser.insertLinkAction.setText(tr("Insert Hyperlink"));
2865                 insertHyperlink = true;
2866                 browser.insertQuickLinkAction.setEnabled(true);
2867                 currentHyperlink ="";
2868                 insideList = false;
2869                 insideTable = false;
2870                 insideEncryption = false;
2871                 forceTextPaste = false;
2872                 
2873                 String js = new String( "function getCursorPos() {"
2874                         +"var cursorPos;"
2875                         +"if (window.getSelection) {"
2876                         +"   var selObj = window.getSelection();"
2877                         +"   var selRange = selObj.getRangeAt(0);"
2878                         +"   var workingNode = window.getSelection().anchorNode.parentNode;"
2879                         +"   while(workingNode != null) { " 
2880 //                      +"      window.jambi.printNode(workingNode.nodeName);"
2881                         +"      if (workingNode.nodeName=='TABLE') { if (workingNode.getAttribute('class').toLowerCase() == 'en-crypt-temp') window.jambi.insideEncryption(); }"
2882                         +"      if (workingNode.nodeName=='B') window.jambi.boldActive();"
2883                         +"      if (workingNode.nodeName=='I') window.jambi.italicActive();"
2884                         +"      if (workingNode.nodeName=='U') window.jambi.underlineActive();"
2885                         +"      if (workingNode.nodeName=='UL') window.jambi.setInsideList();"
2886                         +"      if (workingNode.nodeName=='OL') window.jambi.setInsideList();"
2887                         +"      if (workingNode.nodeName=='LI') window.jambi.setInsideList();"
2888                         +"      if (workingNode.nodeName=='TBODY') window.jambi.setInsideTable();"
2889                         +"      if (workingNode.nodeName=='A') {for(var x = 0; x < workingNode.attributes.length; x++ ) {if (workingNode.attributes[x].nodeName.toLowerCase() == 'href') window.jambi.setInsideLink(workingNode.attributes[x].nodeValue);}}"
2890                         +"      if (workingNode.nodeName=='SPAN') {"
2891                         +"         if (workingNode.getAttribute('style') == 'text-decoration: underline;') window.jambi.underlineActive();"
2892                         +"      }"
2893                         +"      workingNode = workingNode.parentNode;"
2894                         +"   }"
2895                         +"}"
2896                         +"} getCursorPos();");
2897                 browser.page().mainFrame().evaluateJavaScript(js);
2898         }
2899         
2900         public void printNode(String n) {
2901                 System.out.println("Node Vaule: " +n);
2902         }
2903         
2904         public void insideEncryption() {
2905                 insideEncryption = true;
2906                 forceTextPaste();
2907         }
2908         
2909         //****************************************************************
2910         //* Insert a table row
2911         //****************************************************************
2912         public void insertTableRow() {
2913                 
2914                 String js = new String( "function insertTableRow() {"
2915                         +"   var selObj = window.getSelection();"
2916                         +"   var selRange = selObj.getRangeAt(0);"
2917                         +"   var workingNode = window.getSelection().anchorNode.parentNode;"
2918                         +"   var cellCount = 0;"
2919                         +"   while(workingNode != null) { " 
2920                         +"      if (workingNode.nodeName.toLowerCase()=='tr') {"
2921                         +"           row = document.createElement('TR');"
2922                         +"           var nodes = workingNode.getElementsByTagName('td');"
2923                         +"           for (j=0; j<nodes.length; j=j+1) {"
2924                         +"              cell = document.createElement('TD');"
2925                         +"              cell.innerHTML='&nbsp;';"
2926                         +"              row.appendChild(cell);"
2927                         +"           }"                 
2928                         +"           workingNode.parentNode.insertBefore(row,workingNode.nextSibling);"
2929                         +"           return;"
2930                         +"      }"
2931                         +"      workingNode = workingNode.parentNode;"
2932                         +"   }"
2933                         +"} insertTableRow();");
2934                 browser.page().mainFrame().evaluateJavaScript(js);
2935                 contentChanged();
2936         }
2937         
2938         public void insertTableColumn() {
2939                 String js = new String( "function insertTableColumn() {"
2940                                 +"   var selObj = window.getSelection();"
2941                                 +"   var selRange = selObj.getRangeAt(0);"
2942                                 +"   var workingNode = window.getSelection().anchorNode.parentNode;"
2943                                 +"   var current = 0;"
2944                                 +"   while (workingNode.nodeName.toLowerCase() != 'table' && workingNode != null) {"
2945                                 +"       if (workingNode.nodeName.toLowerCase() == 'td') {"
2946                                 +"          var td = workingNode;"
2947                                 +"          while (td.previousSibling != null) { " 
2948                                 +"             current = current+1; td = td.previousSibling;"
2949                                 +"          }"
2950                                 +"       }"
2951                                 +"       workingNode = workingNode.parentNode; "
2952                                 +"   }"
2953                                 +"   if (workingNode == null) return;"
2954                                 +"   for (var i=0; i<workingNode.rows.length; i++) { " 
2955                              &