OSDN Git Service

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