OSDN Git Service

ショートカットキーによるコピー(カット)&ペースト操作も操作履歴として取得可能にした
[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         public void cutClicked() {
1101                 cbObserver.setCopySourceGuid(currentNote.getGuid(), browser.page().selectedText());
1102                 
1103                 browser.page().triggerAction(WebAction.Cut);
1104                 browser.setFocus();
1105         }
1106
1107         // Listener when COPY is clicked
1108         public void copyClicked() {
1109                 cbObserver.setCopySourceGuid(currentNote.getGuid(), browser.page().selectedText());
1110                 
1111                 browser.page().triggerAction(WebAction.Copy);
1112                 browser.setFocus();
1113         }
1114
1115         // Listener when PASTE is clicked
1116         public void pasteClicked() {
1117                 logger.log(logger.EXTREME, "Paste Clicked");
1118                 if (forceTextPaste) {
1119                         pasteWithoutFormattingClicked();
1120                         return;
1121                 }
1122                 
1123                 // コピー&ペーストの操作履歴をデータベースに登録
1124                 String srcGuid = cbObserver.getSourceGuid();
1125                 String dstGuid = currentNote.getGuid();
1126                 if(srcGuid != null && dstGuid != null){
1127                         if(!srcGuid.equals(dstGuid)){
1128                                 conn.getHistoryTable().addHistory("copy & paste", srcGuid, dstGuid);
1129                         }
1130                 }
1131                 
1132                 QClipboard clipboard = QApplication.clipboard();
1133                 QMimeData mime = clipboard.mimeData();
1134
1135                 if (mime.hasImage()) {
1136                         logger.log(logger.EXTREME, "Image paste found");
1137                         browser.setFocus();
1138                         insertImage(mime);
1139                         browser.setFocus();
1140                         return;
1141                 }
1142
1143                 if (mime.hasUrls()) {
1144                         logger.log(logger.EXTREME, "URL paste found");
1145                         if (mime.text().startsWith("evernote:")) {
1146                                 handleNoteLink(mime);
1147                         } else {
1148                                 handleUrls(mime);
1149                                 browser.setFocus();
1150                         }
1151                         return;
1152                 }
1153                 
1154                 String text = mime.html();
1155                 if (text.contains("en-tag") && mime.hasHtml()) {
1156                         logger.log(logger.EXTREME, "Intra-note paste found");
1157                         text = fixInternotePaste(text);
1158                         mime.setHtml(text);
1159                         clipboard.setMimeData(mime);
1160                 }
1161
1162                 logger.log(logger.EXTREME, "Final paste choice encountered");
1163                 browser.page().triggerAction(WebAction.Paste);
1164                 browser.setFocus();
1165
1166         }
1167
1168         // Paste text without formatting
1169         private void pasteWithoutFormattingClicked() {
1170                 logger.log(logger.EXTREME, "Paste without format clipped");
1171                 QClipboard clipboard = QApplication.clipboard();
1172                 QMimeData mime = clipboard.mimeData();
1173                 if (!mime.hasText())
1174                         return;
1175                 
1176                 // コピー&ペーストの操作履歴をデータベースに登録
1177                 String srcGuid = cbObserver.getSourceGuid();
1178                 String dstGuid = currentNote.getGuid();
1179                 if(srcGuid != null && dstGuid != null){
1180                         if(!srcGuid.equals(dstGuid)){
1181                                 conn.getHistoryTable().addHistory("copy & paste", srcGuid, dstGuid);
1182                         }
1183                 }
1184                 
1185                 String text = mime.text();
1186                 clipboard.clear();
1187                 clipboard.setText(text, Mode.Clipboard);
1188                 browser.page().triggerAction(WebAction.Paste);
1189
1190                 // This is done because pasting into an encryption block
1191                 // can cause multiple cells (which can't happen).  It 
1192                 // just goes through the table, extracts the data, & 
1193                 // puts it back as one table cell.
1194                 if (insideEncryption) {
1195                         String js = new String( "function fixEncryption() { "
1196                                         +"   var selObj = window.getSelection();"
1197                                         +"   var selRange = selObj.getRangeAt(0);"
1198                                         +"   var workingNode = window.getSelection().anchorNode;"
1199                                         +"   while(workingNode != null && workingNode.nodeName.toLowerCase() != 'table') { " 
1200                                         +"           workingNode = workingNode.parentNode;"
1201                                         +"   } "
1202                                         +"   workingNode.innerHTML = window.jambi.fixEncryptionPaste(workingNode.innerHTML);"
1203                                         +"} fixEncryption();");
1204                         browser.page().mainFrame().evaluateJavaScript(js);
1205                 }
1206         }
1207         
1208         // This basically removes all the table tags and returns just the contents.
1209         // This is called by JavaScript to fix encryption pastes.
1210         public String fixEncryptionPaste(String data) {
1211                 data = data.replace("<tbody>", "");
1212                 data = data.replace("</tbody>", "");
1213                 data = data.replace("<tr>", "");
1214                 data = data.replace("</tr>", "");
1215                 data = data.replace("<td>", "");
1216                 data = data.replace("</td>", "<br>");
1217                 data = data.replace("<br><br>", "<br>");
1218
1219                 return "<tbody><tr><td>"+data+"</td></tr></tbody>";
1220         }
1221         
1222         // insert date/time
1223         @SuppressWarnings("unused")
1224         private void insertDateTime() {
1225                 String fmt = Global.getDateFormat() + " " + Global.getTimeFormat();
1226                 String dateTimeFormat = new String(fmt);
1227                 SimpleDateFormat simple = new SimpleDateFormat(dateTimeFormat);
1228                 Calendar cal = Calendar.getInstance();
1229                 
1230                 browser.page().mainFrame().evaluateJavaScript(
1231                         "document.execCommand('insertHtml', false, '"+simple.format(cal.getTime())+"');");
1232                 
1233                 browser.setFocus();
1234
1235         }
1236
1237         // Listener when Left is clicked
1238         @SuppressWarnings("unused")
1239         private void justifyLeftClicked() {
1240                 browser.page().mainFrame().evaluateJavaScript(
1241                                 "document.execCommand('JustifyLeft', false, '');");
1242                 browser.setFocus();
1243         }
1244
1245         // Listener when Center is clicked
1246         @SuppressWarnings("unused")
1247         private void justifyCenterClicked() {
1248                 browser.page().mainFrame().evaluateJavaScript(
1249                                 "document.execCommand('JustifyCenter', false, '');");
1250                 browser.setFocus();
1251         }
1252
1253         // Listener when Left is clicked
1254         @SuppressWarnings("unused")
1255         private void justifyRightClicked() {
1256                 browser.page().mainFrame().evaluateJavaScript(
1257                                 "document.execCommand('JustifyRight', false, '');");
1258                 browser.setFocus();
1259         }
1260
1261         // Listener when HLINE is clicked
1262         @SuppressWarnings("unused")
1263         private void hlineClicked() {
1264                 browser.page().mainFrame().evaluateJavaScript(
1265                                 "document.execCommand('insertHorizontalRule', false, '');");
1266                 browser.setFocus();
1267         }
1268
1269         // Listener when outdent is clicked
1270         private void outdentClicked() {
1271                 browser.page().mainFrame().evaluateJavaScript(
1272                                 "document.execCommand('outdent', false, '');");
1273                 browser.setFocus();
1274         }
1275
1276         // Listener when a bullet list is clicked
1277         @SuppressWarnings("unused")
1278         private void bulletListClicked() {
1279                 browser.page().mainFrame().evaluateJavaScript(
1280                                 "document.execCommand('InsertUnorderedList', false, '');");
1281                 browser.setFocus();
1282         }
1283
1284         // Listener when a bullet list is clicked
1285         @SuppressWarnings("unused")
1286         private void numberListClicked() {
1287                 browser.page().mainFrame().evaluateJavaScript(
1288                                 "document.execCommand('InsertOrderedList', false, '');");
1289                 browser.setFocus();
1290         }
1291
1292         // Listener when indent is clicked
1293         private void indentClicked() {
1294                 browser.page().mainFrame().evaluateJavaScript(
1295                                 "document.execCommand('indent', false, '');");
1296                 browser.setFocus();
1297         }
1298
1299         // Listener when the font name is changed
1300         @SuppressWarnings("unused")
1301         private void fontChanged(String font) {
1302                 browser.page().mainFrame().evaluateJavaScript(
1303                                 "document.execCommand('fontName',false,'" + font + "');");
1304                 browser.setFocus();
1305         }
1306
1307         // Listener when a font size is changed
1308         @SuppressWarnings("unused")
1309         private void fontSizeChanged(String font) {
1310                 String text = browser.selectedText();
1311                 if (text.trim().equalsIgnoreCase(""))
1312                         return;
1313
1314                 String selectedText = browser.selectedText();
1315                 String url = "<span style=\"font-size:" +font +"pt; \">"+selectedText +"</a>";
1316                 String script = "document.execCommand('insertHtml', false, '"+url+"');";
1317                 browser.page().mainFrame().evaluateJavaScript(script);
1318 /*              browser.page().mainFrame().evaluateJavaScript(
1319                                 "document.execCommand('fontSize',false,'"
1320                                                 + font + "');");
1321 */
1322                 browser.setFocus();
1323         }
1324
1325         // Load the font combo box based upon the font selected
1326         private void loadFontSize(String name) {        
1327                 QFontDatabase db = new QFontDatabase(); 
1328                 fontSize.clear();
1329                 List<Integer> points = db.pointSizes(name); 
1330                 for (int i=0; i<points.size(); i++) { 
1331                         fontSize.addItem(points.get(i).toString()); 
1332                 }
1333                 /*
1334                 fontSize.addItem("x-small");
1335                 fontSize.addItem("small");
1336                 fontSize.addItem("medium");
1337                 fontSize.addItem("large");
1338                 fontSize.addItem("x-large");
1339                 fontSize.addItem("xx-large");
1340                 fontSize.addItem("xxx-large");
1341                 */
1342         }
1343
1344         // Listener when a font size is changed
1345         @SuppressWarnings("unused")
1346         private void fontColorClicked() {
1347 //              QColorDialog dialog = new QColorDialog();
1348 //              QColor color = QColorDialog.getColor();
1349                 QColor color = fontColorMenu.getColor();
1350                 if (color.isValid())
1351                         browser.page().mainFrame().evaluateJavaScript(
1352                                         "document.execCommand('foreColor',false,'" + color.name()
1353                                                         + "');");
1354                 browser.setFocus();
1355         }
1356
1357         // Listener for when a background color change is requested
1358         @SuppressWarnings("unused")
1359         private void fontHilightClicked() {
1360 //              QColorDialog dialog = new QColorDialog();
1361 //              QColor color = QColorDialog.getColor();
1362                 QColor color = fontHilightColorMenu.getColor();
1363                 if (color.isValid())
1364                         browser.page().mainFrame().evaluateJavaScript(
1365                                         "document.execCommand('backColor',false,'" + color.name()
1366                                                         + "');");
1367                 browser.setFocus();
1368         }
1369         
1370         // Listener for when a background color change is requested
1371         @SuppressWarnings("unused")
1372         private void superscriptClicked() {
1373                 browser.page().mainFrame().evaluateJavaScript(
1374                                         "document.execCommand('superscript');");
1375                 browser.setFocus();
1376         }
1377         
1378         // Listener for when a background color change is requested
1379         @SuppressWarnings("unused")
1380         private void subscriptClicked() {
1381                 browser.page().mainFrame().evaluateJavaScript(
1382                                         "document.execCommand('subscript');");
1383                 browser.setFocus();
1384         }
1385         // Insert a to-do checkbox
1386         @SuppressWarnings("unused")
1387         private void todoClicked() {
1388                 FileNameMap fileNameMap = URLConnection.getFileNameMap();
1389                 String script_start = new String(
1390                                 "document.execCommand('insertHtml', false, '");
1391                 String script_end = new String("');");
1392                 String todo = new String(
1393                                 "<input TYPE=\"CHECKBOX\" value=\"false\" " +
1394                                 "onMouseOver=\"style.cursor=\\'hand\\'\" " +
1395                                 "onClick=\"value=checked; window.jambi.contentChanged(); \" />");
1396                 browser.page().mainFrame().evaluateJavaScript(
1397                                 script_start + todo + script_end);
1398                 browser.setFocus();
1399         }
1400
1401         // Encrypt the selected text
1402         @SuppressWarnings("unused")
1403         private void encryptText() {
1404                 String text = browser.selectedText();
1405                 if (text.trim().equalsIgnoreCase(""))
1406                         return;
1407                 text = new String(text.replaceAll("\n", "<br/>"));
1408
1409                 EnCryptDialog dialog = new EnCryptDialog();
1410                 dialog.exec();
1411                 if (!dialog.okPressed()) {
1412                         return;
1413                 }
1414
1415                 EnCrypt crypt = new EnCrypt();
1416                 String encrypted = crypt.encrypt(text, dialog.getPassword().trim(), 64);
1417                 String decrypted = crypt.decrypt(encrypted, dialog.getPassword().trim(), 64);
1418
1419                 if (encrypted.trim().equals("")) {
1420                         QMessageBox.information(this, tr("Error"), tr("Error Encrypting String"));
1421                         return;
1422                 }
1423                 StringBuffer buffer = new StringBuffer(encrypted.length() + 100);
1424                 buffer.append("<img en-tag=\"en-crypt\" cipher=\"RC2\" hint=\""
1425                                 + dialog.getHint().replace("'","\\'") + "\" length=\"64\" ");
1426                 buffer.append("contentEditable=\"false\" alt=\"");
1427                 buffer.append(encrypted);
1428                 buffer.append("\" src=\"").append(FileUtils.toForwardSlashedPath(Global.getFileManager().getImageDirPath("encrypt.png") +"\""));
1429                 Global.cryptCounter++;
1430                 buffer.append(" id=\"crypt"+Global.cryptCounter.toString() +"\"");
1431                 buffer.append(" onMouseOver=\"style.cursor=\\'hand\\'\"");
1432                 buffer.append(" onClick=\"window.jambi.decryptText(\\'crypt"+Global.cryptCounter.toString() 
1433                                 +"\\', \\'"+encrypted+"\\', \\'"+dialog.getHint().replace("'", "\\&amp;apos;")+"\\');\"");
1434                 buffer.append("style=\"display:block\" />");
1435
1436                 String script_start = new String(
1437                                 "document.execCommand('insertHtml', false, '");
1438                 String script_end = new String("');");
1439                 browser.page().mainFrame().evaluateJavaScript(
1440                                 script_start + buffer.toString() + script_end);
1441         }
1442
1443
1444         // Insert a Quick hyperlink
1445         public void insertQuickLink() {
1446                 logger.log(logger.EXTREME, "Inserting link");
1447                 String text = browser.selectedText();
1448                 if (text.trim().equalsIgnoreCase(""))
1449                         return;
1450                 
1451                 NoteQuickLinkDialog dialog = new NoteQuickLinkDialog(logger, conn, text, cbObserver);
1452
1453                 if (dialog.getResults().size() == 0) {
1454                         QMessageBox.critical(null, tr("No Matches Found") ,tr("No matching notes found."));
1455                         return;
1456                 }
1457                 if (dialog.getResults().size() > 1) {
1458                         dialog.exec();
1459                         if (!dialog.okPressed) {
1460                                 logger.log(logger.EXTREME, "Insert link canceled");
1461                                 return;
1462                         }
1463                 }
1464
1465                 User user = Global.getUserInformation();
1466                 String dUrl = new String("evernote:///view/") + new String(user.getId() + "/" +user.getShardId() +"/"
1467                                 +dialog.getSelectedNote()+"/"+dialog.getSelectedNote() +"/ " +"style=\"color:#69aa35\"");
1468                 
1469                 String url = "<a title=\"" +dUrl
1470                                 +"\" href=" +dUrl 
1471                                 +" >"+text +"</a>";
1472                 String script = "document.execCommand('insertHtml', false, '"+url+"');";
1473                 browser.page().mainFrame().evaluateJavaScript(script);  
1474                 contentChanged();
1475         }
1476
1477         // Insert a hyperlink
1478         public void insertLink() {
1479                 logger.log(logger.EXTREME, "Inserting link");
1480                 String text = browser.selectedText();
1481                 if (text.trim().equalsIgnoreCase(""))
1482                         return;
1483
1484                 InsertLinkDialog dialog = new InsertLinkDialog(insertHyperlink);
1485                 if (currentHyperlink != null && currentHyperlink != "") {
1486                         dialog.setUrl(currentHyperlink);
1487                 }
1488                 dialog.exec();
1489                 if (!dialog.okPressed()) {
1490                         logger.log(logger.EXTREME, "Insert link canceled");
1491                         return;
1492                 }
1493                 
1494                 // Take care of inserting new links
1495                 if (insertHyperlink) {
1496                         String selectedText = browser.selectedText();
1497                         if (dialog.getUrl().trim().equals(""))
1498                                 return;
1499                         logger.log(logger.EXTREME, "Inserting link on text "+selectedText);
1500                         logger.log(logger.EXTREME, "URL Link " +dialog.getUrl().trim());
1501                         String dUrl = StringUtils.replace(dialog.getUrl().trim(), "'", "\\'");
1502                         String url = "<a href=\"" +dUrl
1503                                         +"\" title=" +dUrl 
1504                                         +" >"+selectedText +"</a>";
1505                         String script = "document.execCommand('insertHtml', false, '"+url+"');";
1506                         browser.page().mainFrame().evaluateJavaScript(script);
1507                         return;
1508                 }
1509                 
1510                 // Edit existing links
1511                 String js = new String( "function getCursorPos() {"
1512                                 +"var cursorPos;"
1513                                 +"if (window.getSelection) {"
1514                                 +"   var selObj = window.getSelection();"
1515                                 +"   var selRange = selObj.getRangeAt(0);"
1516                                 +"   var workingNode = window.getSelection().anchorNode.parentNode;"
1517                                 +"   while(workingNode != null) { " 
1518                                 +"      if (workingNode.nodeName.toLowerCase()=='a') workingNode.setAttribute('href','" +dialog.getUrl() +"');"
1519                                 +"      workingNode = workingNode.parentNode;"
1520                                 +"   }"
1521                                 +"}"
1522                                 +"} getCursorPos();");
1523                         browser.page().mainFrame().evaluateJavaScript(js);
1524                 
1525                 if (!dialog.getUrl().trim().equals("")) {
1526                         contentChanged();
1527                         return;
1528                 }
1529                 
1530                 // Remove URL
1531                 js = new String( "function getCursorPos() {"
1532                                 +"var cursorPos;"
1533                                 +"if (window.getSelection) {"
1534                                 +"   var selObj = window.getSelection();"
1535                                 +"   var selRange = selObj.getRangeAt(0);"
1536                                 +"   var workingNode = window.getSelection().anchorNode.parentNode;"
1537                                 +"   while(workingNode != null) { " 
1538                                 +"      if (workingNode.nodeName.toLowerCase()=='a') { "
1539                                 +"         workingNode.removeAttribute('href');"
1540                                 +"         workingNode.removeAttribute('title');"
1541                                 +"         var text = document.createTextNode(workingNode.innerText);"
1542                                 +"         workingNode.parentNode.insertBefore(text, workingNode);"
1543                                 +"         workingNode.parentNode.removeChild(workingNode);"
1544                                 +"      }"
1545                                 +"      workingNode = workingNode.parentNode;"
1546                                 +"   }"
1547                                 +"}"
1548                                 +"} getCursorPos();");
1549                         browser.page().mainFrame().evaluateJavaScript(js);
1550                         
1551                         contentChanged();
1552
1553                 
1554         }
1555         
1556         
1557         // Insert a hyperlink
1558         public void insertLatex() {
1559                 editLatex(null);
1560         }
1561         public void editLatex(String guid) {
1562                 logger.log(logger.EXTREME, "Inserting latex");
1563                 String text = browser.selectedText();
1564                 if (text.trim().equalsIgnoreCase("\n") || text.trim().equalsIgnoreCase("")) {
1565                         InsertLatexImage dialog = new InsertLatexImage();
1566                         if (guid != null) {
1567                                 String formula = conn.getNoteTable().noteResourceTable.getNoteSourceUrl(guid).replace("http://latex.codecogs.com/gif.latex?", "");
1568                                 dialog.setFormula(formula);
1569                         }
1570                         dialog.exec();
1571                         if (!dialog.okPressed()) {
1572                                 logger.log(logger.EXTREME, "Edit LaTex canceled");
1573                                 return;
1574                         }
1575                         text = dialog.getFormula().trim();
1576                 }
1577                 blockApplication.emit(this);
1578                 logger.log(logger.EXTREME, "Inserting LaTeX formula:" +text);
1579                 latexGuid = guid;
1580                 text = StringUtils.replace(text, "'", "\\'");
1581                 String url = "http://latex.codecogs.com/gif.latex?" +text;
1582                 logger.log(logger.EXTREME, "Sending request to codecogs --> " + url);
1583                 QNetworkAccessManager manager = new QNetworkAccessManager(this);
1584                 manager.finished.connect(this, "insertLatexImageReady(QNetworkReply)");
1585                 unblockTime = new GregorianCalendar().getTimeInMillis()+5000;
1586                 awaitingHttpResponse = true;
1587                 manager.get(new QNetworkRequest(new QUrl(url)));
1588         }
1589         
1590         public void insertLatexImageReady(QNetworkReply reply) {
1591                 logger.log(logger.EXTREME, "Response received from CodeCogs");
1592                 if (reply.error() != NetworkError.NoError) 
1593                         return;
1594
1595                 unblockTime = -1;
1596                 if (!awaitingHttpResponse)
1597                         return;
1598                 
1599                 awaitingHttpResponse = false;
1600                 QUrl replyUrl = reply.url();            
1601                 QByteArray image = reply.readAll();
1602                 reply.close();
1603                 logger.log(logger.EXTREME, "New image size: " +image.size());
1604
1605                 Resource newRes = null;
1606                 QFile tfile;
1607                 String path;
1608                 if (latexGuid == null) {
1609                         logger.log(logger.EXTREME, "Creating temporary gif");                   
1610                         path = Global.getFileManager().getResDirPath("latex-temp.gif");
1611                         tfile = new QFile(path);
1612                         tfile.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.WriteOnly));
1613                         logger.log(logger.EXTREME, "File Open: " +tfile.errorString());
1614                         tfile.write(image);
1615                         logger.log(logger.EXTREME, "Bytes writtes: "+tfile.size());
1616                         tfile.close();
1617                         logger.log(logger.EXTREME, "Creating resource");
1618                         int sequence = 0;
1619                         if (currentNote.getResources() != null || currentNote.getResources().size() > 0)
1620                                 sequence = currentNote.getResources().size();
1621                         newRes = createResource(path,sequence ,"image/gif", false);
1622                         QImage pix = new QImage();
1623                         pix.loadFromData(image);
1624                         newRes.setHeight(new Integer(pix.height()).shortValue());
1625                         newRes.setWidth(new Integer(pix.width()).shortValue());
1626                         logger.log(logger.EXTREME, "Renaming temporary file to " +newRes.getGuid()+".gif");
1627                         path = Global.getFileManager().getResDirPath(newRes.getGuid()+".gif");
1628                         tfile.rename(path);
1629                 } else {
1630                         newRes = conn.getNoteTable().noteResourceTable.getNoteResource(latexGuid, false);
1631                         path = Global.getFileManager().getResDirPath(newRes.getGuid()+".gif");
1632                         tfile = new QFile(path);
1633                         tfile.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.WriteOnly));
1634                         tfile.write(image);
1635                         tfile.close();
1636                         newRes.getData().setBody(image.toByteArray());
1637                         // Calculate the new hash value
1638                 MessageDigest md;
1639
1640                 logger.log(logger.EXTREME, "Generating MD5");
1641                 try {
1642                                 md = MessageDigest.getInstance("MD5");
1643                         md.update(image.toByteArray());
1644                         byte[] hash = md.digest();
1645                         newRes.getData().setBodyHash(hash);
1646                         } catch (NoSuchAlgorithmException e) {
1647                                 e.printStackTrace();
1648                         }
1649                         QImage pix = new QImage();
1650                         pix.loadFromData(image);
1651                         newRes.setHeight(new Integer(pix.height()).shortValue());
1652                         newRes.setWidth(new Integer(pix.width()).shortValue());
1653                         conn.getNoteTable().noteResourceTable.updateNoteResource(newRes, true);
1654                 }
1655
1656                 logger.log(logger.EXTREME, "Setting source: " +replyUrl.toString());
1657                 newRes.getAttributes().setSourceURL(replyUrl.toString());
1658                 conn.getNoteTable().noteResourceTable.updateNoteSourceUrl(newRes.getGuid(), replyUrl.toString(), true);
1659                 
1660                 for(int i=0; i<currentNote.getResourcesSize(); i++) {
1661                         if (currentNote.getResources().get(i).getGuid().equals(newRes.getGuid())) {
1662                                 currentNote.getResources().remove(i);
1663                                 i=currentNote.getResourcesSize();
1664                         }
1665                 }
1666                 currentNote.getResources().add(newRes);
1667                 
1668
1669                 // do the actual insert into the note.  We only do this on new formulas.  
1670                 if (latexGuid == null) {
1671                         StringBuffer buffer = new StringBuffer(100);
1672                         String formula = replyUrl.toString().toLowerCase().replace("http://latex.codecogs.com/gif.latex?", "");
1673                         buffer.append("<a href=\"latex://"+path.replace("\\", "/")+"\" title=\""+formula+"\""
1674                                         +"><img src=\"");
1675                         buffer.append(path.replace("\\", "/"));
1676                         buffer.append("\" en-tag=\"en-latex\" type=\"image/gif\""
1677                                 +" hash=\""+Global.byteArrayToHexString(newRes.getData().getBodyHash()) +"\""
1678                                 +" guid=\"" +newRes.getGuid() +"\""
1679                                 + " /></a>");
1680                 
1681                         String script_start = new String("document.execCommand('insertHTML', false, '");
1682                         String script_end = new String("');");
1683                         browser.page().mainFrame().evaluateJavaScript(
1684                                         script_start + buffer + script_end);
1685                 } else {
1686                         HtmlTagModifier modifier = new HtmlTagModifier(getContent());
1687                         modifier.modifyLatexTagHash(newRes);
1688                         String newContent = modifier.getHtml();
1689                         setContent(new QByteArray(newContent));
1690                 }
1691
1692                 logger.log(logger.EXTREME, "New HTML set\n" +browser.page().currentFrame().toHtml());
1693                 QWebSettings.setMaximumPagesInCache(0);
1694                 QWebSettings.setObjectCacheCapacities(0, 0, 0);
1695                 
1696                 browser.page().mainFrame().setHtml(browser.page().mainFrame().toHtml());
1697                 browser.reload();
1698                 contentChanged();
1699 //              resourceSignal.contentChanged.emit(path);
1700                 unblockTime = -1;
1701         unblockApplication.emit();
1702                 return;
1703                 
1704         }
1705
1706         
1707         
1708         // Insert a table
1709         public void insertTable() {
1710                 TableDialog dialog = new TableDialog();
1711                 dialog.exec();
1712                 if (!dialog.okPressed()) {
1713                         return;
1714                 }
1715                 
1716                 int cols = dialog.getCols();
1717                 int rows = dialog.getRows();
1718                 int width = dialog.getWidth();
1719                 boolean percent = dialog.isPercent();
1720                 
1721                 String newHTML = "<table border=\"1\" width=\"" +new Integer(width).toString();
1722                 if (percent)
1723                         newHTML = newHTML +"%";
1724                 newHTML = newHTML + "\"><tbody>";
1725
1726                 for (int i=0; i<rows; i++) {
1727                         newHTML = newHTML +"<tr>";
1728                         for (int j=0; j<cols; j++) {
1729                                 newHTML = newHTML +"<td>&nbsp;</td>";
1730                         }
1731                         newHTML = newHTML +"</tr>";
1732                 }
1733                 newHTML = newHTML+"</tbody></table>";   
1734         
1735                 String script = "document.execCommand('insertHtml', false, '"+newHTML+"');";
1736                 browser.page().mainFrame().evaluateJavaScript(script);
1737         }
1738         
1739         
1740         // Text content changed
1741         @SuppressWarnings("unused")
1742         private void selectionChanged() {
1743                 browser.encryptAction.setEnabled(true);
1744                 browser.insertLinkAction.setEnabled(true);
1745                 browser.insertQuickLinkAction.setEnabled(true);
1746                 String scriptStart = "var selection_text = (window.getSelection()).toString();"
1747                                 + "var range = (window.getSelection()).getRangeAt(0);"
1748                                 + "var parent_html = range.commonAncestorContainer.innerHTML;"
1749                                 + "if (parent_html == undefined) {window.jambi.saveSelectedText(selection_text); return;}"
1750                                 + "var first_text = range.startContainer.nodeValue.substr(range.startOffset);"
1751                                 + "var last_text = (range.endContainer.nodeValue).substring(0,range.endOffset);"
1752                                 + "var start = parent_html.indexOf(first_text);"
1753                                 + "var end = parent_html.indexOf(last_text,start+1)+last_text.length;"
1754                                 + "var value = parent_html.substring(start,end);"
1755                                 + "window.jambi.saveSelectedText(value);" ;
1756                 browser.page().mainFrame().evaluateJavaScript(scriptStart);
1757
1758         }
1759
1760         public void saveSelectedText(String text) {
1761                 boolean enabled = true;
1762                 if (text.trim().length() == 0)
1763                         enabled=false;
1764                 if (text.indexOf("en-tag=\"en-crypt\"") >= 0)
1765                         enabled=false;
1766                 if (text.indexOf("<img en-tag=\"en-media\"") >= 0)
1767                         enabled=false;
1768                 if (text.indexOf("<a en-tag=\"en-media\"") >= 0)
1769                         enabled=false;
1770                 if (text.indexOf("<input ") >= 0)
1771                         enabled=false;
1772                 
1773                 browser.encryptAction.setEnabled(enabled);
1774                 browser.insertLinkAction.setEnabled(enabled);
1775                 browser.insertQuickLinkAction.setEnabled(enabled);
1776 //              selectedText = text;
1777         }
1778
1779         // Decrypt clicked text
1780         public void decryptText(String id, String text, String hint) {
1781                 EnCrypt crypt = new EnCrypt();
1782                 String plainText = null;
1783                 Calendar currentTime = new GregorianCalendar();
1784                 Long l = new Long(currentTime.getTimeInMillis());
1785                 String slot = new String(Long.toString(l));
1786                 
1787                 // First, try to decrypt with any keys we already have
1788                 for (int i=0; i<Global.passwordRemember.size(); i++) {
1789                         plainText = crypt.decrypt(text, Global.passwordRemember.get(i).getFirst(), 64);
1790                         if (plainText != null) {
1791                                 slot = new String(Long.toString(l));
1792                                 Global.passwordSafe.put(slot, Global.passwordRemember.get(i));
1793                                 removeEncryption(id, plainText, false, slot);   
1794                                 return;
1795                         }
1796                 }
1797                 
1798                 
1799                 EnDecryptDialog dialog = new EnDecryptDialog();
1800                 dialog.setHint(hint);
1801                 while (plainText == null || !dialog.okPressed()) {
1802                         dialog.exec();
1803                         if (!dialog.okPressed()) {
1804                                 return;
1805                         }
1806                         plainText = crypt.decrypt(text, dialog.getPassword().trim(), 64);
1807                         if (plainText == null) {
1808                                 QMessageBox.warning(this, tr("Incorrect Password"), tr("The password entered is not correct"));
1809                         }
1810                 }
1811                 Pair<String,String> passwordPair = new Pair<String,String>();
1812                 passwordPair.setFirst(dialog.getPassword());
1813                 passwordPair.setSecond(dialog.getHint());
1814                 Global.passwordSafe.put(slot, passwordPair);
1815 //              removeEncryption(id, plainText.replaceAll("\n", "<br/>"), dialog.permanentlyDecrypt(), slot);
1816                 removeEncryption(id, plainText, dialog.permanentlyDecrypt(), slot);
1817                 if (dialog.rememberPassword()) {
1818                         Pair<String, String> pair = new Pair<String,String>();
1819                         pair.setFirst(dialog.getPassword());
1820                         pair.setSecond(dialog.getHint());
1821                         Global.passwordRemember.add(pair);
1822                 }
1823
1824         }
1825
1826         // Get the editor tag line
1827         public TagLineEdit getTagLine() {
1828                 return tagEdit;
1829         }
1830
1831         // Modify a note's tags
1832         @SuppressWarnings("unused")
1833         private void modifyTags() {
1834                 TagAssign tagWindow = new TagAssign(allTags, currentTags, !conn.getNotebookTable().isLinked(currentNote.getNotebookGuid()));
1835                 tagWindow.exec();
1836                 if (tagWindow.okClicked()) {
1837                         currentTags.clear();
1838                         StringBuffer tagDisplay = new StringBuffer();
1839
1840                         List<QListWidgetItem> newTags = tagWindow.getTagList()
1841                                         .selectedItems();
1842                         for (int i = 0; i < newTags.size(); i++) {
1843                                 currentTags.add(newTags.get(i).text());
1844                                 tagDisplay.append(newTags.get(i).text());
1845                                 if (i < newTags.size() - 1) {
1846                                         tagDisplay.append(Global.tagDelimeter + " ");
1847                                 }
1848                         }
1849                         tagEdit.setText(tagDisplay.toString());
1850                         noteSignal.tagsChanged.emit(currentNote.getGuid(), currentTags);
1851                 }
1852         }
1853
1854         // Tag line has been modified by typing text
1855         @SuppressWarnings("unused")
1856         private void modifyTagsTyping() {
1857                 String completionText = "";
1858                 if (tagEdit.currentCompleterSelection != null && !tagEdit.currentCompleterSelection.equals("")) {
1859                         completionText = tagEdit.currentCompleterSelection;
1860                         tagEdit.currentCompleterSelection = "";
1861                 }
1862                 
1863                 if (tagEdit.text().equalsIgnoreCase(saveTagList))
1864                         return;
1865
1866                 if (saveTagList == null) {
1867                         return;
1868                 }
1869                 
1870                 // We know something has changed...
1871                 String oldTagArray[] = saveTagList.split(Global.tagDelimeter);
1872                 String newTagArray[];
1873                 if (!completionText.equals("")) {
1874                         String before = tagEdit.text().substring(0,tagEdit.cursorPosition());
1875                         int lastDelimiter = before.lastIndexOf(Global.tagDelimeter);
1876                         if (lastDelimiter > 0)
1877                                 before = before.substring(0,before.lastIndexOf(Global.tagDelimeter));
1878                         else 
1879                                 before = "";
1880                         String after = tagEdit.text().substring(tagEdit.cursorPosition());
1881                         newTagArray = (before+Global.tagDelimeter+completionText+Global.tagDelimeter+after).split(Global.tagDelimeter);
1882                 }
1883                 else {
1884                         newTagArray = tagEdit.text().split(Global.tagDelimeter);
1885                 }
1886                 
1887                 // Remove any traling or leading blanks
1888                 for (int i=0; i<newTagArray.length; i++)
1889                         newTagArray[i] = newTagArray[i].trim().replaceAll("^\\s+", "");;
1890                 
1891                 // Remove any potential duplicates from the new list
1892                 for (int i=0; i<newTagArray.length; i++) {
1893                         boolean foundOnce = false;
1894                         for (int j=0; j<newTagArray.length; j++) {
1895                                 if (newTagArray[j].equalsIgnoreCase(newTagArray[i])) {
1896                                         if (!foundOnce) {
1897                                                 foundOnce = true;
1898                                         } else
1899                                                 newTagArray[j] = "";
1900                                 }
1901                         }
1902                 }
1903
1904                 List<String> newTagList = new ArrayList<String>();
1905                 List<String> oldTagList = new ArrayList<String>();
1906
1907                 for (int i = 0; i < oldTagArray.length; i++)
1908                         if (!oldTagArray[i].trim().equals(""))
1909                                 oldTagList.add(oldTagArray[i]);
1910                 for (int i = 0; i < newTagArray.length; i++)
1911                         if (!newTagArray[i].trim().equals(""))
1912                                 newTagList.add(newTagArray[i]);
1913
1914                 if (conn.getNotebookTable().isLinked(currentNote.getNotebookGuid())) {
1915                         for (int i=newTagList.size()-1; i>=0; i--) {
1916                                 boolean found = false;
1917                                 for (int j=0; j<allTags.size(); j++) {
1918                                         if (allTags.get(j).getName().equalsIgnoreCase(newTagList.get(i))) {
1919                                                 found = true;
1920                                                 j=allTags.size();
1921                                         }
1922                                 }
1923                                 if (!found)
1924                                         newTagList.remove(i);
1925                         }
1926                 }
1927
1928                 // Let's cleanup the appearance of the tag list
1929                 Collections.sort(newTagList);
1930                 String newDisplay = "";
1931                 for (int i=0; i<newTagList.size(); i++) {
1932                         newDisplay = newDisplay+newTagList.get(i);
1933                         if (i<newTagList.size()-1)
1934                                 newDisplay = newDisplay+Global.tagDelimeter +" ";
1935                 }
1936                 tagEdit.blockSignals(true);
1937                 tagEdit.setText(newDisplay);
1938                 tagEdit.blockSignals(false);
1939                 
1940                 // We now have lists of the new & old. Remove duplicates. If all
1941                 // are removed from both then nothing has really changed
1942                 for (int i = newTagList.size() - 1; i >= 0; i--) {
1943                         String nTag = newTagList.get(i);
1944                         for (int j = oldTagList.size() - 1; j >= 0; j--) {
1945                                 String oTag = oldTagList.get(j);
1946                                 if (oTag.equalsIgnoreCase(nTag)) {
1947                                         oldTagList.remove(j);
1948                                         newTagList.remove(i);
1949                                         j = -1;
1950                                 }
1951                         }
1952                 }
1953
1954                 if (oldTagList.size() != 0 || newTagList.size() != 0) {
1955                         currentTags.clear();
1956                         newTagArray = tagEdit.text().split(Global.tagDelimeter);
1957                         for (int i = 0; i < newTagArray.length; i++)
1958                                 if (!newTagArray[i].trim().equals(""))
1959                                         currentTags.add(newTagArray[i].trim());
1960
1961                         noteSignal.tagsChanged.emit(currentNote.getGuid(), currentTags);
1962                 }
1963                 
1964         }
1965
1966         // Tab button was pressed
1967         public void tabPressed() {
1968                 if (insideEncryption)
1969                         return;
1970                 if (!insideList && !insideTable) {
1971                         String script_start = new String(
1972                         "document.execCommand('insertHtml', false, '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;');");
1973                         browser.page().mainFrame().evaluateJavaScript(script_start);
1974                         return;
1975                 }
1976                 if (insideList) {
1977                         indentClicked();
1978                 }
1979                 if (insideTable) {
1980                         String js = new String( "function getCursorPosition() { "
1981                                         +"   var selObj = window.getSelection();"
1982                                         +"   var selRange = selObj.getRangeAt(0);"
1983                                         +"   var workingNode = window.getSelection().anchorNode;"
1984                                         +"   var rowCount = 0;"
1985                                         +"   var colCount = 0;"
1986                                         +"   while(workingNode != null && workingNode.nodeName.toLowerCase() != 'table') { " 
1987                                         +"      if (workingNode.nodeName.toLowerCase()=='tr') {"
1988                                         +"         rowCount = rowCount+1;"
1989                                         +"      }"
1990                                         +"      if (workingNode.nodeName.toLowerCase() == 'td') {"
1991                                         +"         colCount = colCount+1;"
1992                                         +"      }"
1993                                         +"      if (workingNode.previousSibling != null)"
1994                                         +"          workingNode = workingNode.previousSibling;"
1995                                         +"      else "
1996                                         +"           workingNode = workingNode.parentNode;"
1997                                         +"   }"
1998                                         +"   var nodes = workingNode.getElementsByTagName('tr');"
1999                                         +"   var tableRows = nodes.length;"
2000                                         +"   nodes = nodes[0].getElementsByTagName('td');"
2001                                         +"   var tableColumns = nodes.length;"
2002                                         +"   window.jambi.setTableCursorPositionTab(rowCount, colCount, tableRows, tableColumns);"
2003                                         +"} getCursorPosition();");
2004                         browser.page().mainFrame().evaluateJavaScript(js);
2005                 }
2006         }
2007         
2008         // If a user presses tab from within a table
2009         public void setTableCursorPositionTab(int currentRow, int currentCol, int tableRows, int tableColumns) {
2010                 if (tableRows == currentRow && currentCol == tableColumns) {
2011                         insertTableRow();
2012                 }
2013                 KeyboardModifiers modifiers = new KeyboardModifiers(KeyboardModifier.NoModifier);
2014                 QKeyEvent right = new QKeyEvent(Type.KeyPress, Qt.Key.Key_Right.value(), modifiers);
2015                 QKeyEvent end = new QKeyEvent(Type.KeyPress, Qt.Key.Key_End.value(), modifiers);
2016                 QKeyEvent end2 = new QKeyEvent(Type.KeyPress, Qt.Key.Key_End.value(), modifiers);
2017                 getBrowser().focusWidget();
2018                 QCoreApplication.postEvent(getBrowser(), end);
2019                 QCoreApplication.postEvent(getBrowser(), right);
2020                 QCoreApplication.postEvent(getBrowser(), end2);
2021         }
2022                 
2023         public void backtabPressed() {
2024                 if (insideEncryption) 
2025                         return;
2026                 if (insideList)
2027                         outdentClicked();
2028                 if (insideTable) {
2029                         String js = new String( "function getCursorPosition() { "
2030                                         +"   var selObj = window.getSelection();"
2031                                         +"   var selRange = selObj.getRangeAt(0);"
2032                                         +"   var workingNode = window.getSelection().anchorNode;"
2033                                         +"   var rowCount = 0;"
2034                                         +"   var colCount = 0;"
2035                                         +"   while(workingNode != null && workingNode.nodeName.toLowerCase() != 'table') { " 
2036                                         +"      if (workingNode.nodeName.toLowerCase()=='tr') {"
2037                                         +"         rowCount = rowCount+1;"
2038                                         +"      }"
2039                                         +"      if (workingNode.nodeName.toLowerCase() == 'td') {"
2040                                         +"         colCount = colCount+1;"
2041                                         +"      }"
2042                                         +"      if (workingNode.previousSibling != null)"
2043                                         +"          workingNode = workingNode.previousSibling;"
2044                                         +"      else "
2045                                         +"           workingNode = workingNode.parentNode;"
2046                                         +"   }"
2047                                         +"   var nodes = workingNode.getElementsByTagName('tr');"
2048                                         +"   var tableRows = nodes.length;"
2049                                         +"   nodes = nodes[0].getElementsByTagName('td');"
2050                                         +"   var tableColumns = nodes.length;"
2051                                         +"   window.jambi.setTableCursorPositionBackTab(rowCount, colCount, tableRows, tableColumns);"
2052                                         +"} getCursorPosition();");
2053                         browser.page().mainFrame().evaluateJavaScript(js);
2054                         
2055                 }
2056         }
2057         
2058         // If a user presses backtab from within a table
2059         public void setTableCursorPositionBackTab(int currentRow, int currentCol, int tableRows, int tableColumns) {
2060                 if (currentRow  == 1 && currentCol == 1) {
2061                         return;
2062                 }
2063                 KeyboardModifiers modifiers = new KeyboardModifiers(KeyboardModifier.NoModifier);
2064                 QKeyEvent left = new QKeyEvent(Type.KeyPress, Qt.Key.Key_Left.value(), modifiers);
2065                 QKeyEvent home = new QKeyEvent(Type.KeyPress, Qt.Key.Key_Home.value(), modifiers);
2066                 getBrowser().focusWidget();
2067                 QCoreApplication.postEvent(getBrowser(), home);
2068                 QCoreApplication.postEvent(getBrowser(), left);
2069         }
2070         
2071         
2072         public void setInsideList() {
2073                 insideList = true;
2074         }
2075         
2076         // The title has been edited
2077         @SuppressWarnings("unused")
2078         private void titleEdited() {
2079                 // If we don't have a good note, or if the current title
2080                 // matches the old title then we don't need to do anything
2081                 if (currentNote == null)
2082                         return;
2083                 if (currentNote.getTitle().trim().equals(titleLabel.text().trim()))
2084                         return;
2085                 
2086                 // If we have a real change, we need to save it.
2087                 String text = titleLabel.text().trim();
2088                 if (text.equals("")) 
2089                         text = tr("Untitled Note");
2090                 noteSignal.titleChanged.emit(currentNote.getGuid(), text);
2091                 currentNote.setTitle(text);
2092                 saveNoteTitle = text;
2093                 checkNoteTitle();
2094         }
2095
2096         // Set the list of note tags
2097         public void setAllTags(List<Tag> l) {
2098                 allTags = l;
2099                 tagEdit.setTagList(l);
2100         }
2101
2102         // Setter for the current tags
2103         public void setCurrentTags(List<String> s) {
2104                 currentTags = s;
2105         }
2106
2107         // Save the list of notebooks
2108         public void setNotebookList(List<Notebook> n) {
2109                 notebookList = n;
2110                 loadNotebookList();
2111         }
2112
2113         // Load the notebook list and select the current notebook
2114         private void loadNotebookList() {
2115                 if (notebookBox.count() != 0)
2116                         notebookBox.clear();
2117                 if (notebookList == null)
2118                         return;
2119
2120                 for (int i = 0; i < notebookList.size(); i++) {
2121                         notebookBox.addItem(notebookList.get(i).getName());
2122                         if (currentNote != null) {
2123                                 if (currentNote.getNotebookGuid().equals(
2124                                                 notebookList.get(i).getGuid())) {
2125                                         notebookBox.setCurrentIndex(i);
2126                                 }
2127                         }
2128                 }
2129         }
2130         
2131         
2132         // Set the notebook for a note
2133         public void setNotebook(String notebook) {
2134                 currentNote.setNotebookGuid(notebook);
2135                 loadNotebookList();
2136         }
2137
2138         // Get the contents of the editor
2139         public String getContent() {
2140                 return browser.page().currentFrame().toHtml();
2141         }
2142
2143         // The note contents have changed
2144         public void contentChanged() {
2145                 String content = getContent();
2146                 
2147                 // This puts in a 1/2 second delay
2148                 // before updating the source editor.
2149                 // It improves response when someone is doing
2150                 // frequent updates on a large note.
2151                 // If the source editor isn't visible, then there
2152                 // is no point to doing any of this.
2153                 if (sourceEdit.isVisible()) {
2154                         setSourceTimer.stop();
2155                         setSourceTimer.setInterval(500);
2156                         setSourceTimer.setSingleShot(true);
2157                         setSourceTimer.start();
2158                 }
2159                 
2160                 checkNoteTitle();
2161                 noteSignal.noteChanged.emit(currentNote.getGuid(), content); 
2162         }
2163
2164         // The notebook selection has changed
2165         @SuppressWarnings("unused")
2166         private void notebookChanged() {
2167                 boolean changed = false;
2168                 String n = notebookBox.currentText();
2169                 for (int i = 0; i < notebookList.size(); i++) {
2170                         if (n.equals(notebookList.get(i).getName())) {
2171                                 if (!notebookList.get(i).getGuid().equals(currentNote.getNotebookGuid())) {
2172                                         String guid = conn.getNotebookTable().findNotebookByName(n);
2173                                         if (conn.getNotebookTable().isLinked(guid)) {
2174                                                 tagEdit.setText("");
2175                                                 noteSignal.tagsChanged.emit(currentNote.getGuid(), new ArrayList<String>());
2176                                                 FilterEditorTags t = new FilterEditorTags(conn, logger);
2177                                                 setAllTags(t.getValidTags(currentNote));
2178                                         }
2179                                         currentNote.setNotebookGuid(notebookList.get(i).getGuid());
2180                                         changed = true;
2181                                 }
2182                                 i = notebookList.size();
2183                         }
2184                 }
2185                 
2186                 // If the notebook changed, signal the update
2187                 if (changed)
2188                         noteSignal.notebookChanged.emit(currentNote.getGuid(), currentNote
2189                                         .getNotebookGuid());
2190         }
2191
2192         // Check the note title
2193         private void checkNoteTitle() {
2194                 String text = browser.page().currentFrame().toPlainText();
2195                 if (saveNoteTitle == null)
2196                         saveNoteTitle = new String();
2197                 text = text.trim();
2198                 if (!saveNoteTitle.trim().equals("") && !saveNoteTitle.trim().equals("Untitled Note"))
2199                         text = saveNoteTitle.trim();
2200                 int newLine = text.indexOf("\n");
2201                 if (newLine > 0)
2202                         text = text.substring(0,newLine);
2203                 if (saveNoteTitle.trim().equals("") || saveNoteTitle.trim().equals("Untitled Note")) {
2204                         if (text.trim().equals(""))
2205                                 text = tr("Untitled Note");
2206                                 titleLabel.setText(text);
2207                 } else {
2208                         if (text.length() > Constants.EDAM_NOTE_TITLE_LEN_MAX)
2209                                 titleLabel.setText(text.substring(0, Constants.EDAM_NOTE_TITLE_LEN_MAX));
2210                         else {
2211                                 titleLabel.blockSignals(true);
2212                                 if (text.trim().equals(""))
2213                                         titleLabel.setText(tr("Untitled Note"));
2214                                 else
2215                                         titleLabel.setText(text);
2216                                 titleLabel.blockSignals(false);
2217                         }
2218                 }
2219                 if (currentNote != null && titleLabel != null && !currentNote.getTitle().equals(text))
2220                         noteSignal.titleChanged.emit(currentNote.getGuid(), text);
2221         }
2222
2223         // Return the note contents so we can email them
2224         public String getContentsToEmail() {
2225                 return browser.page().currentFrame().toPlainText().trim();
2226                 /*
2227                  * int body = browser.page().currentFrame().toHtml().indexOf("<body>");
2228                  * String temp = browser.page().currentFrame().toHtml(); if (body == -1)
2229                  * temp = "<html><body><b>Test</b></body></html>"; else temp =
2230                  * "<html>"+temp.substring(body); return temp; // return
2231                  * urlEncode(browser.page().currentFrame().toHtml());
2232                  */
2233         }
2234
2235         // Insert an image into the editor
2236         private void insertImage(QMimeData mime) {
2237                 logger.log(logger.EXTREME, "Entering insertImage");
2238                 QImage img = (QImage) mime.imageData();
2239                 String script_start = new String(
2240                                 "document.execCommand('insertHTML', false, '");
2241                 String script_end = new String("');");
2242
2243                 long now = new Date().getTime();
2244                 String path = Global.getFileManager().getResDirPath(
2245                                 (new Long(now).toString()) + ".jpg");
2246
2247                 // This block is just a hack to make sure we wait at least 1ms so we
2248                 // don't
2249                 // have collisions on image names
2250                 long i = new Date().getTime();
2251                 while (now == i)
2252                         i = new Date().getTime();
2253
2254                 // Open the file & write the data
2255                 QFile tfile = new QFile(path);
2256                 tfile.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.WriteOnly));
2257                 if (!img.save(tfile)) {
2258                         tfile.close();
2259                         return;
2260                 }
2261                 tfile.close();
2262                 
2263                 Resource newRes = createResource(QUrl.fromLocalFile(path).toString(), 0, "image/jpeg", false);
2264                 if (newRes == null)
2265                         return;
2266                 currentNote.getResources().add(newRes);
2267
2268                 // do the actual insert into the note
2269                 StringBuffer buffer = new StringBuffer(100);
2270                 buffer.append("<img src=\"");
2271                 buffer.append(tfile.fileName());
2272                 buffer.append("\" en-tag=en-media type=\"image/jpeg\""
2273                                 +" hash=\""+Global.byteArrayToHexString(newRes.getData().getBodyHash()) +"\""
2274                                 +" guid=\"" +newRes.getGuid() +"\""
2275                                 +" onContextMenu=\"window.jambi.imageContextMenu(&amp." +tfile.fileName() +"&amp.);\""
2276                                 + " />");
2277                 
2278                 browser.page().mainFrame().evaluateJavaScript(
2279                                 script_start + buffer + script_end);
2280
2281                 return;
2282         }
2283
2284         // Handle pasting of a note-to-note link
2285         private void handleNoteLink(QMimeData mime) {
2286                 for (int i=0; i<mime.urls().size(); i++) {
2287                         StringTokenizer tokens = new StringTokenizer(mime.urls().get(i).toString().replace("evernote:///view/", ""), "/");
2288                         tokens.nextToken();
2289                         tokens.nextToken();
2290                         String sid = tokens.nextToken();
2291                         String lid = tokens.nextToken();
2292                         
2293                         if (!sid.equals(currentNote.getGuid()) && !lid.equals(currentNote.getGuid())) {
2294                                 
2295                                 Note note = conn.getNoteTable().getNote(sid, false, false, false, false, false);
2296                                 if (note == null)
2297                                         note = conn.getNoteTable().getNote(lid, false, false, false, false, false);
2298                 
2299                                 if (note == null)
2300                                         return;
2301
2302                                 // If we've gotten this far, we have a bunch of values.  We need to build the link.
2303                                 StringBuffer url = new StringBuffer(100);
2304                                 String script_start = new String(
2305                                         "document.execCommand('insertHtml', false, '");
2306                                 String script_end = new String("');");
2307         
2308                                 url.append("<a href=\""+mime.urls().get(i).toString() +"\" style=\"color:#69aa35\">");
2309                                 url.append(note.getTitle());
2310                                 url.append("</a>");
2311                                 if (mime.urls().size() > 1)
2312                                         url.append("&nbsp;");
2313                                 browser.page().mainFrame().evaluateJavaScript(
2314                                                 script_start + url + script_end);
2315                         }
2316                 }
2317         }
2318         
2319         // Handle URLs that are trying to be pasted
2320         public void handleUrls(QMimeData mime) {
2321                 logger.log(logger.EXTREME, "Starting handleUrls");
2322                 FileNameMap fileNameMap = URLConnection.getFileNameMap();
2323
2324                 List<QUrl> urlList = mime.urls();
2325                 String url = new String();
2326                 String script_start = new String(
2327                                 "document.execCommand('createLink', false, '");
2328                 String script_end = new String("');");
2329
2330                 for (int i = 0; i < urlList.size(); i++) {
2331                         url = urlList.get(i).toString();
2332                         // Find out what type of file we have
2333                         String mimeType = fileNameMap.getContentTypeFor(url);
2334
2335                         // If null returned, we need to guess at the file type
2336                         if (mimeType == null)
2337                                 mimeType = "application/"
2338                                                 + url.substring(url.lastIndexOf(".") + 1);
2339
2340                         // Check if we have an image or some other type of file
2341                         if (url.substring(0, 5).equalsIgnoreCase("file:")
2342                                         && mimeType.substring(0, 5).equalsIgnoreCase("image")) {
2343                                 handleLocalImageURLPaste(mime, mimeType);
2344                                 return;
2345                         }
2346
2347                         boolean smallEnough = checkFileAttachmentSize(url);
2348                         if (smallEnough 
2349                                         && url.substring(0, 5).equalsIgnoreCase("file:")
2350                                         && !mimeType.substring(0, 5).equalsIgnoreCase("image")) {
2351                                 handleLocalAttachment(mime, mimeType);
2352                                 return;
2353                         }
2354                         browser.page().mainFrame().evaluateJavaScript(
2355                                         script_start + url + script_end);
2356                 }
2357                 return;
2358         }
2359
2360         // If a URL being pasted is an image URL, then attach the image
2361         private void handleLocalImageURLPaste(QMimeData mime, String mimeType) {
2362                 List<QUrl> urlList = mime.urls();
2363                 String url = new String();
2364                 String script_start_image = new String(
2365                                 "document.execCommand('insertHtml', false, '");
2366                 String script_end = new String("');");
2367                 StringBuffer buffer;
2368
2369                 // Copy the image over into the resource directory and create a new resource 
2370                 // record for each url pasted
2371                 for (int i = 0; i < urlList.size(); i++) {
2372                         url = urlList.get(i).toString();
2373
2374                         Resource newRes = createResource(url, i, mimeType, false);
2375                         if (newRes == null)
2376                                 return;
2377                         currentNote.getResources().add(newRes);
2378                         buffer = new StringBuffer(100);
2379                         
2380                         // Open the file & write the data
2381                         String fileName = Global.getFileManager().getResDirPath(newRes.getGuid());
2382                         QFile tfile = new QFile(fileName);
2383                         tfile.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.WriteOnly));
2384                         tfile.write(newRes.getData().getBody());
2385                         tfile.close();
2386                         buffer.append(script_start_image);
2387                         buffer.append("<img src=\"" + FileUtils.toForwardSlashedPath(fileName));
2388 //                      if (mimeType.equalsIgnoreCase("image/jpg"))
2389 //                              mimeType = "image/jpeg";
2390                         buffer.append("\" en-tag=\"en-media\" type=\"" + mimeType +"\""
2391                                         +" hash=\""+Global.byteArrayToHexString(newRes.getData().getBodyHash()) +"\""
2392                                         +" guid=\"" +newRes.getGuid() +"\""
2393                                         +" onContextMenu=\"window.jambi.imageContextMenu(&apos;" +tfile.fileName() +"&apos;);\""
2394                                         + " />");
2395                         buffer.append(script_end);
2396                         browser.page().mainFrame().evaluateJavaScript(buffer.toString());
2397                 }
2398                 return;
2399         }
2400         
2401
2402         // If a URL being pasted is a local file URL, then attach the file
2403         private void handleLocalAttachment(QMimeData mime, String mimeType) {
2404                 logger.log(logger.EXTREME, "Attaching local file");
2405                 List<QUrl> urlList = mime.urls();
2406                 String script_start = new String(
2407                                 "document.execCommand('insertHtml', false, '");
2408                 String script_end = new String("');");
2409                 StringBuffer buffer;
2410
2411                         String[] type = mimeType.split("/");
2412                         String icon = findIcon(type[1]);
2413                         if (icon.equals("attachment.png"))
2414                                 icon = findIcon(type[0]);
2415                         buffer = new StringBuffer(100);
2416
2417                 for (int i = 0; i < urlList.size(); i++) {
2418                         String url = urlList.get(i).toString();
2419
2420                         // Start building the HTML
2421                         if (icon.equals("attachment.png"))
2422                                 icon = findIcon(url.substring(url.lastIndexOf(".")+1));
2423                         String imageURL = FileUtils.toFileURLString(Global.getFileManager().getImageDirFile(icon));
2424
2425                         logger.log(logger.EXTREME, "Creating resource ");
2426                         Resource newRes = createResource(url, i, mimeType, true);
2427                         if (newRes == null)
2428                                 return;
2429                         logger.log(logger.EXTREME, "New resource size: " +newRes.getData().getSize());
2430                         currentNote.getResources().add(newRes);
2431                         
2432                         String fileName = newRes.getGuid() + Global.attachmentNameDelimeter+newRes.getAttributes().getFileName();
2433                         // If we have a PDF, we need to setup the preview.
2434                         if (icon.equalsIgnoreCase("pdf.png") && Global.pdfPreview()) {
2435                                 logger.log(logger.EXTREME, "Setting up PDF preview");
2436                                 if (newRes.getAttributes() != null && 
2437                                                 newRes.getAttributes().getFileName() != null && 
2438                                                 !newRes.getAttributes().getFileName().trim().equals(""))
2439                                         fileName = newRes.getGuid()+Global.attachmentNameDelimeter+
2440                                                 newRes.getAttributes().getFileName();
2441                                 else
2442                                         fileName = newRes.getGuid()+".pdf";
2443                                 QFile file = new QFile(Global.getFileManager().getResDirPath(fileName));
2444                         QFile.OpenMode mode = new QFile.OpenMode();
2445                         mode.set(QFile.OpenModeFlag.WriteOnly);
2446                         file.open(mode);
2447                         QDataStream out = new QDataStream(file);
2448 //                      Resource resBinary = conn.getNoteTable().noteResourceTable.getNoteResource(newRes.getGuid(), true);
2449                                 QByteArray binData = new QByteArray(newRes.getData().getBody());
2450 //                              resBinary = null;
2451                         out.writeBytes(binData.toByteArray());
2452                         file.close();
2453
2454                                 PDFPreview pdfPreview = new PDFPreview();
2455                                 if (pdfPreview.setupPreview(Global.getFileManager().getResDirPath(fileName), "pdf",0)) {
2456                                 imageURL = file.fileName() + ".png";
2457                                 }
2458                         }
2459                         
2460                         logger.log(logger.EXTREME, "Generating link tags");
2461                         buffer.delete(0, buffer.length());
2462                         buffer.append("<a en-tag=\"en-media\" guid=\"" +newRes.getGuid()+"\" ");
2463                         buffer.append(" onContextMenu=\"window.jambi.imageContextMenu(&apos;")
2464                       .append(Global.getFileManager().getResDirPath(fileName))
2465                       .append("&apos;);\" ");                   
2466                         buffer.append("type=\"" + mimeType + "\" href=\"nnres://" + fileName +"\" hash=\""+Global.byteArrayToHexString(newRes.getData().getBodyHash()) +"\" >");
2467                         buffer.append("<img src=\"" + imageURL + "\" title=\"" +newRes.getAttributes().getFileName());
2468                         buffer.append("\"></img>");
2469                         buffer.append("</a>");
2470                         browser.page().mainFrame().evaluateJavaScript(
2471                                         script_start + buffer.toString() + script_end);
2472                 }
2473                 return;
2474         }
2475
2476         private Resource createResource(String url, int sequence, String mime, boolean attachment) {
2477                 logger.log(logger.EXTREME, "Inside create resource");
2478                 QFile resourceFile; 
2479                 //These two lines are added to handle odd characters in the name like #.  Without it
2480                 // toLocalFile() chokes and returns the wrong name.
2481                 logger.log(logger.EXTREME, "File URL:" +url);
2482                 String whichOS = System.getProperty("os.name");
2483                 if (whichOS.contains("Windows")) 
2484                         url = url.replace("file:///", "");
2485                 else
2486                         url = url.replace("file://", "");
2487                 String urlTest = new QUrl(url).toLocalFile();
2488                 logger.log(logger.EXTREME, "File URL toLocalFile():" +urlTest);
2489                 urlTest = url;
2490                 if (!urlTest.equals(""))
2491                         url = urlTest;
2492 //              url = url.replace("/", File.separator);
2493                 logger.log(logger.EXTREME, "Reading from file to create resource:" +url);
2494                 resourceFile = new QFile(url); 
2495         resourceFile.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.ReadOnly));
2496         logger.log(logger.EXTREME, "Error opening file "+url.toString()  +": "+resourceFile.errorString());
2497         byte[] fileData = resourceFile.readAll().toByteArray();
2498         resourceFile.close();
2499         logger.log(logger.EXTREME, "File Length: " +fileData.length);
2500         if (fileData.length == 0)
2501                 return null;
2502         MessageDigest md;
2503         try {
2504                 logger.log(logger.EXTREME, "Generating MD5");
2505                 md = MessageDigest.getInstance("MD5");
2506                 md.update(fileData);
2507                 byte[] hash = md.digest();
2508   
2509                 Resource r = new Resource();
2510                 Calendar time = new GregorianCalendar();
2511                 long prevTime = time.getTimeInMillis();
2512                 while (prevTime == time.getTimeInMillis()) {
2513                         time = new GregorianCalendar();
2514                 }
2515                 r.setGuid(time.getTimeInMillis()+new Integer(sequence).toString());
2516                 r.setNoteGuid(currentNote.getGuid());
2517                 r.setMime(mime);
2518                 r.setActive(true);
2519                 r.setUpdateSequenceNum(0);
2520                 r.setWidth((short) 0);
2521                 r.setHeight((short) 0);
2522                 r.setDuration((short) 0);
2523                                 
2524                 Data d = new Data();
2525                 d.setBody(fileData);
2526                 d.setBodyIsSet(true);
2527                 d.setBodyHash(hash);
2528                 d.setBodyHashIsSet(true);
2529                 r.setData(d);
2530                 d.setSize(fileData.length);
2531                 
2532                 int fileNamePos = url.lastIndexOf(File.separator);
2533                 if (fileNamePos == -1)
2534                         fileNamePos = url.lastIndexOf("/");
2535                         String fileName = url.substring(fileNamePos+1);
2536                 ResourceAttributes a = new ResourceAttributes();
2537                 a.setAltitude(0);
2538                 a.setAltitudeIsSet(false);
2539                 a.setLongitude(0);
2540                 a.setLongitudeIsSet(false);
2541                 a.setLatitude(0);
2542                 a.setLatitudeIsSet(false);
2543                 a.setCameraMake("");
2544                 a.setCameraMakeIsSet(false);
2545                 a.setCameraModel("");
2546                 a.setCameraModelIsSet(false);
2547                 a.setAttachment(attachment);
2548                 a.setAttachmentIsSet(true);
2549                 a.setClientWillIndex(false);
2550                 a.setClientWillIndexIsSet(true);
2551                 a.setRecoType("");
2552                 a.setRecoTypeIsSet(false);
2553                 a.setSourceURL(url);
2554                 a.setSourceURLIsSet(true);
2555                 a.setTimestamp(0);
2556                 a.setTimestampIsSet(false);
2557                 a.setFileName(fileName);
2558                 a.setFileNameIsSet(true);
2559                 r.setAttributes(a);
2560                 
2561                 conn.getNoteTable().noteResourceTable.saveNoteResource(r, true);
2562                 logger.log(logger.EXTREME, "Resource created");
2563                 return r;
2564         } catch (NoSuchAlgorithmException e1) {
2565                 e1.printStackTrace();
2566                 }
2567         return null;
2568         }
2569         
2570
2571     // find the appropriate icon for an attachment
2572     private String findIcon(String appl) {
2573         appl = appl.toLowerCase();
2574         File f = Global.getFileManager().getImageDirFile(appl + ".png");
2575         if (f.exists())
2576                 return appl+".png";
2577         return "attachment.png";
2578     }
2579
2580
2581
2582         // Check the file attachment to be sure it isn't over 25 mb
2583         private boolean checkFileAttachmentSize(String url) {
2584                 String fileName = url.substring(8);
2585                 QFile resourceFile = new QFile(fileName);
2586                 resourceFile.open(new QIODevice.OpenMode(
2587                                 QIODevice.OpenModeFlag.ReadOnly));
2588                 long size = resourceFile.size();
2589                 resourceFile.close();
2590                 size = size / 1024 / 1024;
2591                 if (size < 50 && Global.isPremium())
2592                         return true;
2593                 if (size < 25)
2594                         return true;
2595
2596                 String error = tr("A file attachment may not exceed 25MB.");
2597                 QMessageBox.information(this, tr("Attachment Size"), error);
2598                 return false;
2599         }
2600
2601
2602         @SuppressWarnings("unused")
2603         private void createdChanged() {
2604                 QDateTime dt = new QDateTime();
2605                 dt.setDate(createdDate.date());
2606                 dt.setTime(createdTime.time());
2607                 noteSignal.createdDateChanged.emit(currentNote.getGuid(), dt);
2608
2609         }
2610
2611         @SuppressWarnings("unused")
2612         private void alteredChanged() {
2613                 QDateTime dt = new QDateTime();
2614                 dt.setDate(alteredDate.date());
2615                 dt.setTime(alteredTime.time());
2616                 noteSignal.alteredDateChanged.emit(currentNote.getGuid(), dt);
2617         }
2618
2619         @SuppressWarnings("unused")
2620         private void subjectDateTimeChanged() {
2621                 QDateTime dt = new QDateTime();
2622                 dt.setDate(subjectDate.date());
2623                 dt.setTime(subjectTime.time());
2624                 noteSignal.subjectDateChanged.emit(currentNote.getGuid(), dt);
2625
2626         }
2627
2628         @SuppressWarnings("unused")
2629         private void sourceUrlChanged() {
2630                 noteSignal.sourceUrlChanged.emit(currentNote.getGuid(), urlText.text());
2631         }
2632
2633         @SuppressWarnings("unused")
2634         private void authorChanged() {
2635                 noteSignal.authorChanged.emit(currentNote.getGuid(), authorText.text());
2636         }
2637         
2638         @SuppressWarnings("unused")
2639         private void geoBoxChanged() {
2640                 int index = geoBox.currentIndex();
2641                 geoBox.setCurrentIndex(0);
2642                 if (index == 1) {