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