OSDN Git Service

Change generation of files/paths in db dir to use FileManager
[neighbornote/NeighborNote.git] / src / cx / fbn / nevernote / NeverNote.java
1 /*
2  * This file is part of NeverNote 
3  * Copyright 2009 Randy Baumgarte
4  * 
5  * This file may be licensed under the terms of of the
6  * GNU General Public License Version 2 (the ``GPL'').
7  *
8  * Software distributed under the License is distributed
9  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
10  * express or implied. See the GPL for the specific language
11  * governing rights and limitations.
12  *
13  * You should have received a copy of the GPL along with this
14  * program. If not, go to http://www.gnu.org/licenses/gpl.html
15  * or write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18 */
19 package cx.fbn.nevernote;
20 import java.awt.Desktop;
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.FileNotFoundException;
24 import java.security.MessageDigest;
25 import java.security.NoSuchAlgorithmException;
26 import java.sql.Connection;
27 import java.sql.DriverManager;
28 import java.sql.SQLException;
29 import java.text.SimpleDateFormat;
30 import java.util.ArrayList;
31 import java.util.Calendar;
32 import java.util.Collections;
33 import java.util.Comparator;
34 import java.util.Date;
35 import java.util.GregorianCalendar;
36 import java.util.HashMap;
37 import java.util.List;
38 import java.util.SortedMap;
39 import java.util.Vector;
40
41 import org.apache.thrift.TException;
42
43 import com.evernote.edam.error.EDAMNotFoundException;
44 import com.evernote.edam.error.EDAMSystemException;
45 import com.evernote.edam.error.EDAMUserException;
46 import com.evernote.edam.notestore.NoteFilter;
47 import com.evernote.edam.notestore.NoteVersionId;
48 import com.evernote.edam.type.Data;
49 import com.evernote.edam.type.Note;
50 import com.evernote.edam.type.NoteAttributes;
51 import com.evernote.edam.type.Notebook;
52 import com.evernote.edam.type.QueryFormat;
53 import com.evernote.edam.type.Resource;
54 import com.evernote.edam.type.SavedSearch;
55 import com.evernote.edam.type.Tag;
56 import com.evernote.edam.type.User;
57 import com.trolltech.qt.QThread;
58 import com.trolltech.qt.core.QByteArray;
59 import com.trolltech.qt.core.QDataStream;
60 import com.trolltech.qt.core.QDateTime;
61 import com.trolltech.qt.core.QDir;
62 import com.trolltech.qt.core.QFile;
63 import com.trolltech.qt.core.QFileInfo;
64 import com.trolltech.qt.core.QFileSystemWatcher;
65 import com.trolltech.qt.core.QIODevice;
66 import com.trolltech.qt.core.QModelIndex;
67 import com.trolltech.qt.core.QSize;
68 import com.trolltech.qt.core.QTemporaryFile;
69 import com.trolltech.qt.core.QTextCodec;
70 import com.trolltech.qt.core.QThreadPool;
71 import com.trolltech.qt.core.QTimer;
72 import com.trolltech.qt.core.QUrl;
73 import com.trolltech.qt.core.Qt;
74 import com.trolltech.qt.core.QIODevice.OpenModeFlag;
75 import com.trolltech.qt.core.Qt.SortOrder;
76 import com.trolltech.qt.core.Qt.WidgetAttribute;
77 import com.trolltech.qt.gui.QAbstractItemView;
78 import com.trolltech.qt.gui.QAction;
79 import com.trolltech.qt.gui.QApplication;
80 import com.trolltech.qt.gui.QCloseEvent;
81 import com.trolltech.qt.gui.QColor;
82 import com.trolltech.qt.gui.QComboBox;
83 import com.trolltech.qt.gui.QCursor;
84 import com.trolltech.qt.gui.QDesktopServices;
85 import com.trolltech.qt.gui.QDialog;
86 import com.trolltech.qt.gui.QFileDialog;
87 import com.trolltech.qt.gui.QGridLayout;
88 import com.trolltech.qt.gui.QHBoxLayout;
89 import com.trolltech.qt.gui.QIcon;
90 import com.trolltech.qt.gui.QImage;
91 import com.trolltech.qt.gui.QLabel;
92 import com.trolltech.qt.gui.QListWidget;
93 import com.trolltech.qt.gui.QMainWindow;
94 import com.trolltech.qt.gui.QMenu;
95 import com.trolltech.qt.gui.QMessageBox;
96 import com.trolltech.qt.gui.QPixmap;
97 import com.trolltech.qt.gui.QPrintDialog;
98 import com.trolltech.qt.gui.QPrinter;
99 import com.trolltech.qt.gui.QProgressBar;
100 import com.trolltech.qt.gui.QSizePolicy;
101 import com.trolltech.qt.gui.QSpinBox;
102 import com.trolltech.qt.gui.QSplashScreen;
103 import com.trolltech.qt.gui.QSplitter;
104 import com.trolltech.qt.gui.QStatusBar;
105 import com.trolltech.qt.gui.QSystemTrayIcon;
106 import com.trolltech.qt.gui.QTableWidgetItem;
107 import com.trolltech.qt.gui.QTextEdit;
108 import com.trolltech.qt.gui.QToolBar;
109 import com.trolltech.qt.gui.QTreeWidgetItem;
110 import com.trolltech.qt.gui.QAbstractItemView.ScrollHint;
111 import com.trolltech.qt.gui.QComboBox.InsertPolicy;
112 import com.trolltech.qt.gui.QFileDialog.AcceptMode;
113 import com.trolltech.qt.gui.QFileDialog.FileMode;
114 import com.trolltech.qt.gui.QMessageBox.StandardButton;
115 import com.trolltech.qt.gui.QSizePolicy.Policy;
116 import com.trolltech.qt.webkit.QWebSettings;
117 import com.trolltech.qt.webkit.QWebPage.WebAction;
118 import com.trolltech.qt.xml.QDomAttr;
119 import com.trolltech.qt.xml.QDomDocument;
120 import com.trolltech.qt.xml.QDomElement;
121 import com.trolltech.qt.xml.QDomNodeList;
122
123 import cx.fbn.nevernote.config.InitializationException;
124 import cx.fbn.nevernote.config.StartupConfig;
125 import cx.fbn.nevernote.dialog.AccountDialog;
126 import cx.fbn.nevernote.dialog.ConfigDialog;
127 import cx.fbn.nevernote.dialog.DatabaseLoginDialog;
128 import cx.fbn.nevernote.dialog.DatabaseStatus;
129 import cx.fbn.nevernote.dialog.FindDialog;
130 import cx.fbn.nevernote.dialog.LoginDialog;
131 import cx.fbn.nevernote.dialog.NotebookArchive;
132 import cx.fbn.nevernote.dialog.NotebookEdit;
133 import cx.fbn.nevernote.dialog.OnlineNoteHistory;
134 import cx.fbn.nevernote.dialog.SavedSearchEdit;
135 import cx.fbn.nevernote.dialog.TagEdit;
136 import cx.fbn.nevernote.dialog.ThumbnailViewer;
137 import cx.fbn.nevernote.dialog.WatchFolder;
138 import cx.fbn.nevernote.filters.EnSearch;
139 import cx.fbn.nevernote.gui.AttributeTreeWidget;
140 import cx.fbn.nevernote.gui.BrowserWindow;
141 import cx.fbn.nevernote.gui.DateAttributeFilterTable;
142 import cx.fbn.nevernote.gui.MainMenuBar;
143 import cx.fbn.nevernote.gui.NotebookTreeWidget;
144 import cx.fbn.nevernote.gui.PDFPreview;
145 import cx.fbn.nevernote.gui.SavedSearchTreeWidget;
146 import cx.fbn.nevernote.gui.TableView;
147 import cx.fbn.nevernote.gui.TagTreeWidget;
148 import cx.fbn.nevernote.gui.Thumbnailer;
149 import cx.fbn.nevernote.gui.TrashTreeWidget;
150 import cx.fbn.nevernote.signals.DBRunnerSignal;
151 import cx.fbn.nevernote.sql.DatabaseConnection;
152 import cx.fbn.nevernote.sql.runners.WatchFolderRecord;
153 import cx.fbn.nevernote.threads.DBRunner;
154 import cx.fbn.nevernote.threads.IndexRunner;
155 import cx.fbn.nevernote.threads.SyncRunner;
156 import cx.fbn.nevernote.utilities.AESEncrypter;
157 import cx.fbn.nevernote.utilities.ApplicationLogger;
158 import cx.fbn.nevernote.utilities.FileImporter;
159 import cx.fbn.nevernote.utilities.ListManager;
160 import cx.fbn.nevernote.utilities.SyncTimes;
161 import cx.fbn.nevernote.xml.ExportData;
162 import cx.fbn.nevernote.xml.ImportData;
163 import cx.fbn.nevernote.xml.XMLInsertHilight;
164
165
166 public class NeverNote extends QMainWindow{
167         
168         QStatusBar                              statusBar;                                      // Application status bar
169         
170         DatabaseConnection              conn;
171         
172         MainMenuBar                             menuBar;                                        // Main menu bar
173         FindDialog                              find;                                           // Text search in note dialog
174         List<String>                    emitLog;                                        // Messages displayed in the status bar;
175         QSystemTrayIcon                 trayIcon;                                       // little tray icon
176         QMenu                                   trayMenu;                                       // System tray menu
177         QAction                                 trayExitAction;                         // Exit the application
178         QAction                                 trayShowAction;                         // toggle the show/hide action          
179         QAction                                 trayAddNoteAction;                      // Add a note from the system tray
180         
181     NotebookTreeWidget          notebookTree;                           // List of notebooks
182     AttributeTreeWidget         attributeTree;                          // List of note attributes
183     TagTreeWidget                       tagTree;                                        // list of user created tags
184     SavedSearchTreeWidget       savedSearchTree;                        // list of saved searches
185     TrashTreeWidget                     trashTree;                                      // Trashcan
186     TableView                           noteTableView;                          //      List of notes (the widget).
187
188     public BrowserWindow        browserWindow;                          // Window containing browser & labels
189     QToolBar                            toolBar;                                        // The tool bar under the menu
190 //    QLineEdit                                 searchField;                            // The search filter bar on the toolbar
191     QComboBox                           searchField;                            // search filter bar on the toolbar;
192     boolean                                     searchPerformed = false;        // Search was done?
193     QProgressBar                        quotaBar;                                       // The current quota usage
194     
195     ApplicationLogger           logger;
196     List<String>                        selectedNotebookGUIDs;          // List of notebook GUIDs
197     List<String>                        selectedTagGUIDs;                       // List of selected tag GUIDs
198     List<String>                        selectedNoteGUIDs;                      // List of selected notes
199     String                                      selectedSavedSearchGUID;        // Currently selected saved searches
200     
201     NoteFilter                          filter;                                         // Note filter
202     String                                      currentNoteGuid;                        // GUID of the current note 
203     Note                                        currentNote;                            // The currently viewed note
204     boolean                                     noteDirty;                                      // Has the note been changed?
205     boolean                             inkNote;                    // if this is an ink note, it is read only
206   
207     QThread                                     dbThread;                                       // Thread to handle DB communication
208     ListManager                         listManager;                                    // DB runnable task
209     
210     List<QTemporaryFile>        tempFiles;                                      // Array of temporary files;
211     
212     QTimer                                      indexTimer;                                     // timer to start the index thread
213     IndexRunner                         indexRunner;                            // thread to index notes
214     QThread                                     indexThread;
215     
216     QTimer                                      syncTimer;                                      // Sync on an interval
217     QTimer                                      syncDelayTimer;                         // Sync delay to free up database
218     SyncRunner                          syncRunner;                                     // thread to do a sync.
219     QThread                                     syncThread;
220     QTimer                                      saveTimer;                                      // Timer to save note contents
221     
222     QTimer                                      authTimer;                                      // Refresh authentication
223     QTimer                                      externalFileSaveTimer;          // Save files altered externally
224     List<String>                        externalFiles;                          // External files to save later
225     List<String>                        importFilesKeep;                        // Auto-import files to save later
226     List<String>                        importFilesDelete;                      // Auto-import files to save later
227     
228     int                                         indexTime;                                      // how often to try and index
229     boolean                                     indexRunning;                           // Is indexing running?
230     boolean                                     indexDisabled;                          // Is indexing disabled?
231     
232     int                                         syncThreadsReady;                       // number of sync threads that are free
233     int                                         syncTime;                                       // Sync interval
234     boolean                                     syncRunning;                            // Is sync running?
235     boolean                                     automaticSync;                          // do sync automatically?
236     QTreeWidgetItem                     attributeTreeSelected;
237
238     QAction                             prevButton;                                     // Go to the previous item viewed
239     QAction                             nextButton;                                     // Go to the next item in the history
240     QAction                             downButton;                                     // Go to the next item in the list
241     QAction                             upButton;                                       // Go to the prev. item in the list;
242     QAction                             synchronizeButton;                      // Synchronize with Evernote
243     List<QIcon>                 synchronizeAnimation;           // Synchronize movie
244     QTimer                              synchronizeAnimationTimer;      // Timer to change animation button
245     int                                 synchronizeFrame;                       // Current frame being viewed
246     QAction                     printButton;                            // Print Button
247     QAction                             tagButton;                                      // Tag edit button
248     QAction                             attributeButton;                        // Attribute information button
249     QAction                     emailButton;                            // Email button
250     QAction                     deleteButton;                           // Delete button
251     QAction                             newButton;                                      // new Note Button;
252     QSpinBox                    zoomSpinner;                            // Zoom zoom
253     QAction                             searchClearButton;                      // Clear the search field
254     
255     QSplitter                   mainLeftRightSplitter;          // main splitter for left/right side
256     QSplitter                   leftSplitter1;                          // first left hand splitter
257     QSplitter                   browserIndexSplitter;           // splitter between note index & note text
258     
259     QFileSystemWatcher  importKeepWatcher;                      // Watch & keep auto-import
260     QFileSystemWatcher  importDeleteWatcher;            // Watch & Delete auto-import
261     List<String>                importedFiles;                          // History of imported files (so we don't import twice)
262     
263     OnlineNoteHistory   historyWindow;                          // online history window 
264     List<NoteVersionId> versions;                                       // history versions
265     
266     QTimer                              threadMonitorTimer;                     // Timer to watch threads.
267     int                                 dbThreadDeadCount=0;            // number of consecutive dead times for the db thread
268     int                                 syncThreadDeadCount=0;          // number of consecutive dead times for the sync thread
269     int                                 indexThreadDeadCount=0;         // number of consecutive dead times for the index thread
270     int                                 notebookThreadDeadCount=0;      // number of consecutive dead times for the notebook thread
271     int                                 tagDeadCount=0;                         // number of consecutive dead times for the tag thread
272     int                                 trashDeadCount=0;                       // number of consecutive dead times for the trash thread
273     int                                 saveThreadDeadCount=0;          // number of consecutive dead times for the save thread
274     
275     HashMap<String, String>             noteCache;                      // Cash of note content 
276     List<String>                historyGuids;                           // GUIDs of previously viewed items
277     int                                 historyPosition;                        // Position within the viewed items
278     boolean                             fromHistory;                            // Is this from the history queue?
279     String                              trashNoteGuid;                          // Guid to restore / set into or out of trash to save position
280     Thumbnailer                 preview;                                        // generate preview image
281     ThumbnailViewer             thumbnailViewer;                        // View preview thumbnail; 
282     
283     String iconPath = new String("classpath:cx/fbn/nevernote/icons/");
284         
285         
286     //***************************************************************
287     //***************************************************************
288     //** Constructor & main entry point
289     //***************************************************************
290     //***************************************************************
291     // Application Constructor  
292         public NeverNote()  {
293                                 
294                 if (!lockApplication()) {
295                         System.exit(0);
296                 }
297                 thread().setPriority(Thread.MAX_PRIORITY);
298                 
299                 logger = new ApplicationLogger("nevernote.log");
300                 logger.log(logger.HIGH, tr("Starting Application"));
301                 conn = new DatabaseConnection(logger, Global.mainThreadId);
302                 conn.dbSetup();
303                 
304                 logger.log(logger.EXTREME, tr("Creating index connection"));    
305                 logger.log(logger.EXTREME, tr("Building DB thread"));
306                 Global.dbRunnerSignal = new DBRunnerSignal();
307                 if (Global.getDatabaseUrl().toUpperCase().indexOf("CIPHER=") > -1) {
308                         boolean goodCheck = false;
309                         while (!goodCheck) {
310                                 DatabaseLoginDialog dialog = new DatabaseLoginDialog();
311                                 dialog.exec();
312                                 if (!dialog.okPressed())
313                                         System.exit(0);
314                                 Global.cipherPassword = dialog.getPassword();
315                                 goodCheck = databaseCheck(Global.getDatabaseUrl(), Global.getDatabaseUserid(), Global.getDatabaseUserPassword(), Global.cipherPassword);
316                         }
317                 }
318                 Global.dbRunner = new DBRunner(Global.getDatabaseUrl(), Global.getDatabaseUserid(), Global.getDatabaseUserPassword(), Global.cipherPassword);
319                 logger.log(logger.EXTREME, tr("Starting DB thread"));
320                 dbThread = new QThread(Global.dbRunner, "Database Thread");
321                 dbThread.start();
322                 logger.log(logger.EXTREME, tr("DB Thread has started"));
323                 Global.dbRunnerSignal.start.emit();
324                 logger.log(logger.EXTREME, tr("Setting main thread DB connection"));
325                 logger.log(logger.EXTREME, tr("main DB thread setup complete."));
326                 conn.checkDatabaseVersion();
327                 
328                 // Start building the invalid XML tables
329                 Global.invalidElements = conn.getInvalidXMLTable().getInvalidElements();
330                 List<String> elements = conn.getInvalidXMLTable().getInvalidAttributeElements();
331                 
332                 for (int i=0; i<elements.size(); i++) {
333                         Global.invalidAttributes.put(elements.get(i), conn.getInvalidXMLTable().getInvalidAttributes(elements.get(i)));
334                 }
335                 
336                 logger.log(logger.EXTREME, tr("Starting GUI build"));
337                 Global.originalPalette = QApplication.palette();
338                 QApplication.setStyle(Global.getStyle());
339                 if (Global.useStandardPalette())
340                         QApplication.setPalette(QApplication.style().standardPalette());
341         setWindowTitle("NeverNote");
342         
343         mainLeftRightSplitter = new QSplitter();
344         setCentralWidget(mainLeftRightSplitter);
345         leftSplitter1 = new QSplitter();
346         leftSplitter1.setOrientation(Qt.Orientation.Vertical);
347                 
348         browserIndexSplitter = new QSplitter();
349         browserIndexSplitter.setOrientation(Qt.Orientation.Vertical);
350         
351         //* Setup threads & thread timers
352         int indexRunnerCount = Global.getIndexThreads();
353         indexRunnerCount = 1;
354         QThreadPool.globalInstance().setMaxThreadCount(indexRunnerCount+5);     // increase max thread count
355
356                 logger.log(logger.EXTREME, tr("Building list manager"));
357         listManager = new ListManager(conn, logger, Global.mainThreadId);
358         
359                 logger.log(logger.EXTREME, tr("Building index runners & timers"));
360         indexRunner = new IndexRunner("indexRunner.log");
361                 indexThread = new QThread(indexRunner, "Index Thread");
362                 indexThread.start();
363                 
364         synchronizeAnimationTimer = new QTimer();
365         synchronizeAnimationTimer.timeout.connect(this, "updateSyncButton()");
366         
367                 indexTimer = new QTimer();
368                 indexTime = 1000*60*5;     // look for unindexed every 5 minutes
369 //              indexTime = 1000*5;
370                 indexTimer.start(indexTime);  // Start indexing timer
371                 indexTimer.timeout.connect(this, "indexTimer()");
372                 indexDisabled = false;
373                 indexRunning = false;
374                                 
375                 logger.log(logger.EXTREME, tr("Setting sync thread & timers"));
376                 syncThreadsReady=1;
377                 syncRunner = new SyncRunner("syncRunner.log");
378                 syncTime = new SyncTimes().timeValue(Global.getSyncInterval());
379                 syncTimer = new QTimer();
380                 syncTimer.timeout.connect(this, "syncTimer()");
381         syncRunner.status.message.connect(this, "setMessage(String)");
382         syncRunner.syncSignal.finished.connect(this, "syncThreadComplete(Boolean)");
383         syncRunner.syncSignal.errorDisconnect.connect(this, "remoteErrorDisconnect()");
384         syncRunning = false;    
385                 if (syncTime > 0) {
386                         automaticSync = true;
387                         syncTimer.start(syncTime*60*1000);
388                 } else {
389                         automaticSync = false;
390                         syncTimer.stop();
391                 }
392                 syncRunner.setEvernoteUpdateCount(Global.getEvernoteUpdateCount());
393                 syncThread = new QThread(syncRunner, "Synchronization Thread");
394                 syncThread.start();
395                 
396                 
397                 logger.log(logger.EXTREME, tr("Starting authentication timer"));
398                 authTimer = new QTimer();
399                 authTimer.timeout.connect(this, "authTimer()");
400                 authTimer.start(1000*60*15);
401                 syncRunner.syncSignal.authRefreshComplete.connect(this, "authRefreshComplete(boolean)");
402                 
403                 logger.log(logger.EXTREME, tr("Setting save note timer"));
404                 saveTimer = new QTimer();
405                 saveTimer.timeout.connect(this, "saveNote()");
406                 if (Global.getAutoSaveInterval() > 0) {
407                         saveTimer.setInterval(1000*60*Global.getAutoSaveInterval()); 
408 //                      saveTimer.setInterval(1000*10); // auto save every 20 seconds;
409                         saveTimer.start();
410                 }
411                 
412 //              Global.trace();
413                 logger.log(logger.EXTREME, tr("Starting external file monitor timer"));
414                 externalFileSaveTimer = new QTimer();
415                 externalFileSaveTimer.timeout.connect(this, "externalFileEditedSaver()");
416                 externalFileSaveTimer.setInterval(1000*5);   // save every 5 seconds;
417                 externalFiles = new ArrayList<String>();
418                 importFilesDelete = new ArrayList<String>();
419                 importFilesKeep = new ArrayList<String>();
420                 externalFileSaveTimer.start();
421                 
422         notebookTree = new NotebookTreeWidget();
423         attributeTree = new AttributeTreeWidget();
424         tagTree = new TagTreeWidget(conn);
425         savedSearchTree = new SavedSearchTreeWidget();
426         trashTree = new TrashTreeWidget();
427         noteTableView = new TableView(logger);
428         
429         QGridLayout leftGrid = new QGridLayout();
430         leftSplitter1.setLayout(leftGrid);
431         leftGrid.addWidget(notebookTree, 1, 1);
432         leftGrid.addWidget(tagTree,2,1);
433         leftGrid.addWidget(attributeTree,3,1);
434         leftGrid.addWidget(savedSearchTree,4,1);
435         leftGrid.addWidget(trashTree, 5, 1);
436         
437         // Setup the browser window
438         noteCache = new HashMap<String,String>();
439         browserWindow = new BrowserWindow(conn);
440
441         browserIndexSplitter.addWidget(noteTableView);
442         browserIndexSplitter.addWidget(browserWindow);
443         
444         mainLeftRightSplitter.addWidget(leftSplitter1);
445         mainLeftRightSplitter.addWidget(browserIndexSplitter);
446
447         searchField = new QComboBox();
448         searchField.setEditable(true);
449         searchField.activatedIndex.connect(this, "searchFieldChanged()");
450         searchField.setDuplicatesEnabled(false);
451         searchField.editTextChanged.connect(this,"searchFieldTextChanged(String)");
452         
453         quotaBar = new QProgressBar();
454         
455         // Setup the thumbnail viewer
456         thumbnailViewer = new ThumbnailViewer();
457         thumbnailViewer.upArrow.connect(this, "upAction()");
458         thumbnailViewer.downArrow.connect(this, "downAction()");
459         thumbnailViewer.leftArrow.connect(this, "nextViewedAction()");
460         thumbnailViewer.rightArrow.connect(this, "previousViewedAction()");
461
462         listManager.loadNotesIndex();
463         initializeNotebookTree();
464         initializeTagTree();
465         initializeSavedSearchTree();
466         attributeTree.itemClicked.connect(this, "attributeTreeClicked(QTreeWidgetItem, Integer)");
467         attributeTreeSelected = null;
468         initializeNoteTable();    
469
470                 selectedNoteGUIDs = new ArrayList<String>();
471                 statusBar = new QStatusBar();
472                 setStatusBar(statusBar);
473                 menuBar = new MainMenuBar(this);
474                 emitLog = new ArrayList<String>();
475                 
476                 tagTree.setDeleteAction(menuBar.tagDeleteAction);
477                 tagTree.setEditAction(menuBar.tagEditAction);
478                 tagTree.setAddAction(menuBar.tagAddAction);
479                 tagTree.setVisible(Global.isWindowVisible("tagTree"));
480                 tagTree.noteSignal.tagsAdded.connect(this, "tagsAdded(String, String)");
481                 menuBar.hideTags.setChecked(Global.isWindowVisible("tagTree"));
482                 listManager.tagSignal.listChanged.connect(this, "reloadTagTree()");
483         
484                 notebookTree.setDeleteAction(menuBar.notebookDeleteAction);
485                 notebookTree.setEditAction(menuBar.notebookEditAction);
486                 notebookTree.setAddAction(menuBar.notebookAddAction);
487                 notebookTree.setVisible(Global.isWindowVisible("notebookTree"));
488                 notebookTree.noteSignal.notebookChanged.connect(this, "updateNoteNotebook(String, String)");
489                 menuBar.hideNotebooks.setChecked(Global.isWindowVisible("notebookTree"));
490
491                 savedSearchTree.setAddAction(menuBar.savedSearchAddAction);
492                 savedSearchTree.setEditAction(menuBar.savedSearchEditAction);
493                 savedSearchTree.setDeleteAction(menuBar.savedSearchDeleteAction);
494                 savedSearchTree.itemSelectionChanged.connect(this, "updateSavedSearchSelection()");
495                 savedSearchTree.setVisible(Global.isWindowVisible("savedSearchTree"));
496                 menuBar.hideSavedSearches.setChecked(Global.isWindowVisible("savedSearchTree"));
497                         
498                 noteTableView.setAddAction(menuBar.noteAdd);
499                 noteTableView.setDeleteAction(menuBar.noteDelete);
500                 noteTableView.setRestoreAction(menuBar.noteRestoreAction);
501                 noteTableView.setNoteDuplicateAction(menuBar.noteDuplicateAction);
502                 noteTableView.setNoteHistoryAction(menuBar.noteOnlineHistoryAction);
503                 noteTableView.noteSignal.titleColorChanged.connect(this, "titleColorChanged(Integer)");
504                 noteTableView.setMergeNotesAction(menuBar.noteMergeAction);
505                 noteTableView.rowChanged.connect(this, "scrollToGuid(String)");
506                 noteTableView.resetViewport.connect(this, "scrollToCurrentGuid()");
507                 noteTableView.doubleClicked.connect(this, "listDoubleClick()");
508                 listManager.trashSignal.countChanged.connect(trashTree, "updateCounts(Integer)");
509                 trashTree.load();
510         trashTree.itemSelectionChanged.connect(this, "trashTreeSelection()");
511                 trashTree.setEmptyAction(menuBar.emptyTrashAction);
512                 trashTree.setVisible(Global.isWindowVisible("trashTree"));
513                 menuBar.hideTrash.setChecked(Global.isWindowVisible("trashTree"));
514                 trashTree.updateCounts(listManager.getTrashCount());
515
516                 attributeTree.setVisible(Global.isWindowVisible("attributeTree"));
517                 menuBar.hideAttributes.setChecked(Global.isWindowVisible("attributeTree"));
518
519                 noteTableView.setVisible(Global.isWindowVisible("noteList"));
520                 menuBar.hideNoteList.setChecked(Global.isWindowVisible("noteList"));
521                 
522                 if (!Global.isWindowVisible("editorButtonBar"))
523                         toggleEditorButtonBar();
524                 if (!Global.isWindowVisible("leftPanel"))
525                         menuBar.hideLeftSide.setChecked(true);
526                 
527                 setMenuBar(menuBar);
528                 setupToolBar();
529                 find = new FindDialog();
530                 find.getOkButton().clicked.connect(this, "doFindText()");
531                 
532                 // Setup the tray icon menu bar
533                 trayShowAction = new QAction("Show/Hide", this);
534                 trayExitAction = new QAction("Exit", this);
535                 trayAddNoteAction = new QAction("Add Note", this);
536                 
537                 trayExitAction.triggered.connect(this, "close()");
538                 trayAddNoteAction.triggered.connect(this, "addNote()");
539                 trayShowAction.triggered.connect(this, "trayToggleVisible()");
540                 
541                 trayMenu = new QMenu(this);
542                 trayMenu.addAction(trayAddNoteAction);
543                 trayMenu.addAction(trayShowAction);
544                 trayMenu.addAction(trayExitAction);
545                 
546                 
547                 trayIcon = new QSystemTrayIcon(this);
548                 trayIcon.setToolTip("NeverNote");
549                 trayIcon.setContextMenu(trayMenu);
550                 trayIcon.activated.connect(this, "trayActivated(com.trolltech.qt.gui.QSystemTrayIcon$ActivationReason)");
551
552                 currentNoteGuid="";
553                 currentNoteGuid = Global.getLastViewedNoteGuid();
554         historyGuids = new ArrayList<String>();
555         historyPosition = 0;
556         fromHistory = false;
557                 noteDirty = false;
558                 if (!currentNoteGuid.trim().equals("")) {
559                         currentNote = conn.getNoteTable().getNote(currentNoteGuid, true,true,false,false,true);
560                 }
561                 
562                 noteIndexUpdated(true);
563                 showColumns();
564                 menuBar.showEditorBar.setChecked(Global.isWindowVisible("editorButtonBar"));
565                 if (menuBar.showEditorBar.isChecked())
566                 showEditorButtons();
567                 tagIndexUpdated(true);
568                 savedSearchIndexUpdated();
569                 notebookIndexUpdated();
570                 updateQuotaBar();
571         setupSyncSignalListeners();        
572         setupBrowserSignalListeners();
573         setupIndexListeners();
574               
575         
576         tagTree.tagSignal.listChanged.connect(this, "tagIndexUpdated()");
577         tagTree.showAllTags(true);
578
579                 QIcon appIcon = new QIcon(iconPath+"nevernote.png");
580         setWindowIcon(appIcon);
581         trayIcon.setIcon(appIcon);
582         if (Global.showTrayIcon())
583                 trayIcon.show();
584         else
585                 trayIcon.hide();
586         
587         scrollToGuid(currentNoteGuid);
588         if (Global.automaticLogin()) {
589                 remoteConnect();
590                 if (Global.isConnected)
591                         syncTimer();
592         }
593         setupFolderImports();
594         
595         loadStyleSheet();
596         restoreWindowState();
597         
598         if (Global.mimicEvernoteInterface) {
599                 notebookTree.selectGuid("");
600         }
601         
602         threadMonitorTimer = new QTimer();
603         threadMonitorTimer.timeout.connect(this, "threadMonitorCheck()");
604         threadMonitorTimer.start(1000*10);  // Check for threads every 10 seconds;              
605         
606         historyGuids.add(currentNoteGuid);
607         historyPosition = 1;
608         
609         int sortCol = Global.getSortColumn();
610                 int sortOrder = Global.getSortOrder();
611                 noteTableView.sortByColumn(sortCol, SortOrder.resolve(sortOrder));
612         }
613
614         
615         // Main entry point
616         public static void main(String[] args) {
617                 QApplication.initialize(args);
618                 QPixmap pixmap = new QPixmap("classpath:cx/fbn/nevernote/icons/splash_logo.png");
619                 QSplashScreen splash = new QSplashScreen(pixmap);
620
621                 try {
622                     initializeGlobalSettings(args);
623                 } catch (InitializationException e) {
624                         QMessageBox.critical(null, "Startup error", "Aborting: " + e.getMessage());
625                         return;
626                 }
627
628                 boolean showSplash = Global.isWindowVisible("SplashScreen");
629                 if (showSplash) 
630                         splash.show();
631                 NeverNote application = new NeverNote();
632                 application.setAttribute(WidgetAttribute.WA_DeleteOnClose, true);
633                 if (Global.wasWindowMaximized())
634                         application.showMaximized();
635                 else
636                         application.show();
637                 if (showSplash)
638                         splash.finish(application);
639                 QApplication.exec();
640                 System.out.println("Goodbye.");
641                 QApplication.exit();
642         }
643
644         private static void initializeGlobalSettings(String[] args) throws InitializationException {
645                 StartupConfig startupConfig = new StartupConfig();
646
647                 for (String arg : args) {
648                         String lower = arg.toLowerCase();
649                         if (lower.startsWith("--name="))
650                                 startupConfig.setName(arg.substring(arg.indexOf('=') + 1));
651                         if (lower.startsWith("--home="))
652                                 startupConfig.setHomeDirPath(arg.substring(arg.indexOf('=') + 1));
653                         if (lower.startsWith("--disable-viewing"))
654                                 startupConfig.setDisableViewing(true);
655                 }
656
657                 Global.setup(startupConfig);
658         }
659
660     // Exit point
661         @Override
662         public void closeEvent(QCloseEvent event) {     
663                 logger.log(logger.HIGH, "Entering NeverNote.closeEvent");
664                 waitCursor(true);
665                 
666                 if (currentNote!= null & browserWindow!=null) {
667                         if (!currentNote.getTitle().equals(browserWindow.getTitle()))
668                                 conn.getNoteTable().updateNoteTitle(currentNote.getGuid(), browserWindow.getTitle());
669                 }
670                 saveNote();
671                 setMessage("Beginning shutdown.");
672
673                 externalFileEditedSaver();
674                 if (Global.isConnected && Global.synchronizeOnClose()) {
675                         setMessage("Performing synchronization before closing.");
676                         syncRunner.addWork("SYNC");
677                 }
678                 setMessage("Closing Program.");
679                 threadMonitorTimer.stop();
680
681                 syncRunner.addWork("STOP");
682                 indexRunner.addWork("STOP");
683                 saveNote();
684                 listManager.stop();
685                 saveWindowState();
686
687                 if (tempFiles != null)
688                         tempFiles.clear();
689
690                 browserWindow.noteSignal.tagsChanged.disconnect();
691                 browserWindow.noteSignal.titleChanged.disconnect();
692                 browserWindow.noteSignal.noteChanged.disconnect();
693                 browserWindow.noteSignal.notebookChanged.disconnect();
694                 browserWindow.noteSignal.createdDateChanged.disconnect();
695                 browserWindow.noteSignal.alteredDateChanged.disconnect();
696                 syncRunner.searchSignal.listChanged.disconnect();
697                 syncRunner.tagSignal.listChanged.disconnect();
698         syncRunner.notebookSignal.listChanged.disconnect();
699         syncRunner.noteIndexSignal.listChanged.disconnect();
700
701
702                 int position = noteTableView.header.visualIndex(Global.noteTableCreationPosition);
703                 Global.setColumnPosition("noteTableCreationPosition", position);
704                 position = noteTableView.header.visualIndex(Global.noteTableTagPosition);
705                 Global.setColumnPosition("noteTableTagPosition", position);
706                 position = noteTableView.header.visualIndex(Global.noteTableNotebookPosition);
707                 Global.setColumnPosition("noteTableNotebookPosition", position);
708                 position = noteTableView.header.visualIndex(Global.noteTableChangedPosition);
709                 Global.setColumnPosition("noteTableChangedPosition", position);
710                 position = noteTableView.header.visualIndex(Global.noteTableAuthorPosition);
711                 Global.setColumnPosition("noteTableAuthorPosition", position);
712                 position = noteTableView.header.visualIndex(Global.noteTableSourceUrlPosition);
713                 Global.setColumnPosition("noteTableSourceUrlPosition", position);
714                 position = noteTableView.header.visualIndex(Global.noteTableSubjectDatePosition);
715                 Global.setColumnPosition("noteTableSubjectDatePosition", position);
716                 position = noteTableView.header.visualIndex(Global.noteTableTitlePosition);
717                 Global.setColumnPosition("noteTableTitlePosition", position);
718                 position = noteTableView.header.visualIndex(Global.noteTableSynchronizedPosition);
719                 Global.setColumnPosition("noteTableSynchronizedPosition", position);
720                 
721                 saveNoteIndexWidth();
722                 
723                 int width = notebookTree.columnWidth(0);
724                 Global.setColumnWidth("notebookTreeName", width);
725                 width = tagTree.columnWidth(0);
726                 Global.setColumnWidth("tagTreeName", width);
727                 
728                 Global.saveWindowMaximized(isMaximized());
729                 Global.saveCurrentNoteGuid(currentNoteGuid);
730                         
731                 int sortCol = noteTableView.proxyModel.sortColumn();
732                 int sortOrder = noteTableView.proxyModel.sortOrder().value();
733                 Global.setSortColumn(sortCol);
734                 Global.setSortOrder(sortOrder);
735                 
736                 hide();
737                 trayIcon.hide();
738                 Global.keepRunning = false;
739                 try {
740                         logger.log(logger.MEDIUM, "Waiting for indexThread to stop");
741                         indexRunner.thread().join(50);
742                         logger.log(logger.MEDIUM, "Index thread has stopped");
743                 } catch (InterruptedException e1) {
744                         e1.printStackTrace();
745                 }
746                 if (!syncRunner.isIdle()) {
747                         try {
748                                 logger.log(logger.MEDIUM, "Waiting for syncThread to stop");
749                                 syncThread.join();
750                                 logger.log(logger.MEDIUM, "Sync thread has stopped");
751                         } catch (InterruptedException e1) {
752                                 e1.printStackTrace();
753                         }
754                 }
755
756                 logger.log(logger.EXTREME, "Shutting down database");
757                 conn.dbShutdown();
758                 logger.log(logger.EXTREME, "Waiting for DB thread to terminate");
759                 try {
760                         dbThread.join();
761                 } catch (InterruptedException e) {
762                         e.printStackTrace();
763                 }
764                 logger.log(logger.EXTREME, "DB Thread has terminated");
765                 unlockApplication();
766                 logger.log(logger.HIGH, "Leaving NeverNote.closeEvent");
767         }
768
769         public void setMessage(String s) {
770                 logger.log(logger.HIGH, "Entering NeverNote.setMessage");
771                 logger.log(logger.HIGH, "Message: " +s);
772                 statusBar.showMessage(s);
773                 emitLog.add(s);
774                 logger.log(logger.HIGH, "Leaving NeverNote.setMessage");
775         }
776                 
777         private void waitCursor(boolean wait) {
778                 if (wait)
779                         QApplication.setOverrideCursor(new QCursor(Qt.CursorShape.WaitCursor));
780                 else
781                         QApplication.restoreOverrideCursor();
782         }
783         
784         private void setupIndexListeners() {
785                 indexRunner.noteSignal.noteIndexed.connect(this, "indexThreadComplete(String)");
786                 indexRunner.resourceSignal.resourceIndexed.connect(this, "indexThreadComplete(String)");
787 //                      indexRunner.threadSignal.indexNeeded.connect(listManager, "setIndexNeeded(String, String, Boolean)");
788         }
789         private void setupSyncSignalListeners() {
790                 syncRunner.tagSignal.listChanged.connect(this, "tagIndexUpdated()");
791         syncRunner.searchSignal.listChanged.connect(this, "savedSearchIndexUpdated()");
792         syncRunner.notebookSignal.listChanged.connect(this, "notebookIndexUpdated()");
793         syncRunner.noteIndexSignal.listChanged.connect(this, "noteIndexUpdated(boolean)");
794         syncRunner.noteSignal.quotaChanged.connect(this, "updateQuotaBar()");
795         
796 //              syncRunner.syncSignal.setSequenceDate.connect(this,"setSequenceDate(long)");
797                 syncRunner.syncSignal.saveUploadAmount.connect(this,"saveUploadAmount(long)");
798 //              syncRunner.syncSignal.setUpdateSequenceNumber.connect(this,"setUpdateSequenceNumber(int)");
799                 syncRunner.syncSignal.saveUserInformation.connect(this,"saveUserInformation(User)");
800                 syncRunner.syncSignal.saveEvernoteUpdateCount.connect(this,"saveEvernoteUpdateCount(int)");
801                 
802                 syncRunner.noteSignal.guidChanged.connect(this, "noteGuidChanged(String, String)");
803                 syncRunner.noteSignal.noteChanged.connect(this, "invalidateNoteCache(String, String)");
804                 syncRunner.resourceSignal.resourceGuidChanged.connect(this, "noteResourceGuidChanged(String,String,String)");
805                 
806                 syncRunner.syncSignal.refreshLists.connect(this, "refreshLists()");
807         }
808         
809         private void setupBrowserSignalListeners() {
810                 
811                 browserWindow.fileWatcher.fileChanged.connect(this, "externalFileEdited(String)");
812                 browserWindow.noteSignal.tagsChanged.connect(this, "updateNoteTags(String, List)");
813             browserWindow.noteSignal.tagsChanged.connect(this, "updateListTags(String, List)");
814                 browserWindow.noteSignal.noteChanged.connect(this, "invalidateNoteCache(String, String)");
815             browserWindow.noteSignal.noteChanged.connect(this, "setNoteDirty()");
816             browserWindow.noteSignal.titleChanged.connect(listManager, "updateNoteTitle(String, String)");
817             browserWindow.noteSignal.titleChanged.connect(this, "updateListTitle(String, String)");
818             browserWindow.noteSignal.notebookChanged.connect(this, "updateNoteNotebook(String, String)");
819             browserWindow.noteSignal.createdDateChanged.connect(listManager, "updateNoteCreatedDate(String, QDateTime)");
820             browserWindow.noteSignal.createdDateChanged.connect(this, "updateListDateCreated(String, QDateTime)");
821             browserWindow.noteSignal.alteredDateChanged.connect(listManager, "updateNoteAlteredDate(String, QDateTime)");
822             browserWindow.noteSignal.alteredDateChanged.connect(this, "updateListDateChanged(String, QDateTime)");
823             browserWindow.noteSignal.subjectDateChanged.connect(listManager, "updateNoteSubjectDate(String, QDateTime)");
824             browserWindow.noteSignal.subjectDateChanged.connect(this, "updateListDateSubject(String, QDateTime)");
825             browserWindow.noteSignal.authorChanged.connect(listManager, "updateNoteAuthor(String, String)");
826             browserWindow.noteSignal.authorChanged.connect(this, "updateListAuthor(String, String)");
827             browserWindow.noteSignal.sourceUrlChanged.connect(listManager, "updateNoteSourceUrl(String, String)");
828             browserWindow.noteSignal.sourceUrlChanged.connect(this, "updateListSourceUrl(String, String)");
829             browserWindow.focusLost.connect(this, "saveNote()");
830             browserWindow.resourceSignal.contentChanged.connect(this, "externalFileEdited(String)");
831 //          browserWindow.resourceSignal.externalFileEdit.connect(this, "saveResourceLater(String, String)");
832         }
833         private boolean lockApplication() {
834                                 
835                 // NFC TODO: who creates this - H2? should it be parameterized with databaseName like in RDatabaseConnection? 
836                 String fileName = Global.getFileManager().getDbDirPath("NeverNote.lock.db");
837 //              QFile.remove(fileName);
838                 if (QFile.exists(fileName)) {
839                         QMessageBox.question(this, "Lock File Detected",
840                                         "While starting I've found a database lock file.\n" +
841                                         "to prevent multiple instances from accessing the database \n"+
842                                         "at the same time.  Please stop any other program, or (if you\n" +
843                                         "are sure nothing else is using the database) remove the file\n" +
844                                         fileName +".");
845                         return false;
846                         
847                 }
848                 return true;
849 /*              String fileName = Global.currentDir +"nevernote.lock";
850
851                 
852                 if (QFile.exists(fileName)) {
853                         if (QMessageBox.question(this, "Confirmation",
854                                 "While starting I've found a lock file.  This file is used to prevent multiple "+
855                                 "instances of this program running at once.  If NeverNote has crashed this " +
856                                 "is just a file that wasn't cleaned up and you can safely, "+
857                                 "continue, but if there is another instance of this running you are " +
858                                 "running the risk of creating problems.\n\n" +
859                                 "Are you sure you want to continue?",
860                                 QMessageBox.StandardButton.Yes, 
861                                 QMessageBox.StandardButton.No)==StandardButton.No.value()) {
862                                         return false;
863                                 }
864                 }
865                 
866                 QFile file = new QFile(fileName);
867                 file.open(OpenModeFlag.WriteOnly);
868                 file.write(new QByteArray("This file is used to prevent multiple instances " +
869                                 "of NeverNote running more than once.  " +
870                                 "It should be deleted when NeverNote ends"));
871                 file.close();
872                 return true;
873 */
874         }
875         private void unlockApplication() {
876                 String fileName = Global.currentDir +"nevernote.lock";
877                 if (QFile.exists(fileName)) {
878                         QFile.remove(fileName);
879                 }
880         }
881         
882
883         //***************************************************************
884         //***************************************************************
885         //* Settings and look & feel
886         //***************************************************************
887         //***************************************************************
888         @SuppressWarnings("unused")
889         private void settings() {
890                 logger.log(logger.HIGH, "Entering NeverNote.settings");
891         ConfigDialog settings = new ConfigDialog(this);
892         String dateFormat = Global.getDateFormat();
893         String timeFormat = Global.getTimeFormat();
894         
895         settings.exec();
896         if (Global.showTrayIcon())
897                 trayIcon.show();
898         else
899                 trayIcon.hide();
900         showColumns();
901         if (menuBar.showEditorBar.isChecked())
902                 showEditorButtons();
903         
904         // Reset the save timer
905         if (Global.getAutoSaveInterval() > 0)
906                         saveTimer.setInterval(1000*60*Global.getAutoSaveInterval());
907         else
908                 saveTimer.stop();
909         
910         // This is a hack to force a reload of the index in case the date or time changed.
911 //        if (!dateFormat.equals(Global.getDateFormat()) ||
912 //                      !timeFormat.equals(Global.getTimeFormat())) {
913                 noteCache.clear();
914                 noteIndexUpdated(true);
915 //        }
916         
917         logger.log(logger.HIGH, "Leaving NeverNote.settings");
918         }
919         // Restore things to the way they were
920         private void restoreWindowState() {
921                 // We need to name things or this doesn't work.
922                 setObjectName("NeverNote");
923                 mainLeftRightSplitter.setObjectName("mainLeftRightSplitter");
924                 browserIndexSplitter.setObjectName("browserIndexSplitter");
925                 leftSplitter1.setObjectName("leftSplitter1");   
926                 
927                 // Restore the actual positions.
928                 restoreGeometry(Global.restoreGeometry(objectName()));
929         mainLeftRightSplitter.restoreState(Global.restoreState(mainLeftRightSplitter.objectName()));
930         browserIndexSplitter.restoreState(Global.restoreState(browserIndexSplitter.objectName()));
931         leftSplitter1.restoreState(Global.restoreState(leftSplitter1.objectName()));
932        
933         }
934         // Save window positions for the next start
935         private void saveWindowState() {
936                 Global.saveGeometry(objectName(), saveGeometry());
937                 Global.saveState(mainLeftRightSplitter.objectName(), mainLeftRightSplitter.saveState());
938                 Global.saveState(browserIndexSplitter.objectName(), browserIndexSplitter.saveState());
939                 Global.saveState(leftSplitter1.objectName(), leftSplitter1.saveState());
940         }    
941         // Load the style sheet
942         private void loadStyleSheet() {
943                 String fileName = Global.currentDir +"qss"+System.getProperty("file.separator")+ "default.qss";
944                 QFile file = new QFile(fileName);
945                 file.open(OpenModeFlag.ReadOnly);
946                 String styleSheet = file.readAll().toString();
947                 file.close();
948                 setStyleSheet(styleSheet);
949         }
950         // Save column widths for the next time
951         private void saveNoteIndexWidth() {
952                 int width;
953         width = noteTableView.getColumnWidth(Global.noteTableCreationPosition);
954         Global.setColumnWidth("noteTableCreationPosition", width);
955                 width = noteTableView.getColumnWidth(Global.noteTableChangedPosition);
956                 Global.setColumnWidth("noteTableChangedPosition", width);
957                 width = noteTableView.getColumnWidth(Global.noteTableGuidPosition);
958                 Global.setColumnWidth("noteTableGuidPosition", width);
959                 width = noteTableView.getColumnWidth(Global.noteTableNotebookPosition);
960                 Global.setColumnWidth("noteTableNotebookPosition", width);
961                 width = noteTableView.getColumnWidth(Global.noteTableTagPosition);
962                 Global.setColumnWidth("noteTableTagPosition", width);
963                 width = noteTableView.getColumnWidth(Global.noteTableTitlePosition);
964                 Global.setColumnWidth("noteTableTitlePosition", width);
965                 width = noteTableView.getColumnWidth(Global.noteTableSourceUrlPosition);
966                 Global.setColumnWidth("noteTableSourceUrlPosition", width);
967                 width = noteTableView.getColumnWidth(Global.noteTableAuthorPosition);
968                 Global.setColumnWidth("noteTableAuthorPosition", width);
969                 width = noteTableView.getColumnWidth(Global.noteTableSubjectDatePosition);
970                 Global.setColumnWidth("noteTableSubjectDatePosition", width);
971                 width = noteTableView.getColumnWidth(Global.noteTableSynchronizedPosition);
972                 Global.setColumnWidth("noteTableSynchronizedPosition", width);
973         }
974         
975         
976     //***************************************************************
977     //***************************************************************
978     //** These functions deal with Notebook menu items
979     //***************************************************************
980     //***************************************************************
981     // Setup the tree containing the user's notebooks.
982     private void initializeNotebookTree() {       
983         logger.log(logger.HIGH, "Entering NeverNote.initializeNotebookTree");
984         notebookTree.itemSelectionChanged.connect(this, "notebookTreeSelection()");
985         listManager.notebookSignal.refreshNotebookTreeCounts.connect(notebookTree, "updateCounts(List, List)");
986  //     notebookTree.resize(Global.getSize("notebookTree"));
987         logger.log(logger.HIGH, "Leaving NeverNote.initializeNotebookTree");
988     }   
989     // Listener when a notebook is selected
990     @SuppressWarnings("unused")
991         private void notebookTreeSelection() {
992                 logger.log(logger.HIGH, "Entering NeverNote.notebookTreeSelection");
993
994                 clearTrashFilter();
995                 clearAttributeFilter();
996                 clearSavedSearchFilter();
997                 if (Global.mimicEvernoteInterface) {
998                         clearTagFilter();
999                         searchField.clear();
1000                 }
1001                 
1002                 menuBar.noteRestoreAction.setVisible(false);            
1003         menuBar.notebookEditAction.setEnabled(true);
1004         menuBar.notebookDeleteAction.setEnabled(true);
1005         List<QTreeWidgetItem> selections = notebookTree.selectedItems();
1006         QTreeWidgetItem currentSelection;
1007         selectedNotebookGUIDs.clear();
1008         if (!Global.mimicEvernoteInterface) {
1009                 for (int i=0; i<selections.size(); i++) {
1010                         currentSelection = selections.get(i);
1011                         selectedNotebookGUIDs.add(currentSelection.text(2));
1012                 }
1013         
1014                 
1015                 // There is the potential for no notebooks to be selected if this 
1016                 // happens then we make it look like all notebooks were selecetd.
1017                 // If that happens, just select the "all notebooks"
1018                 selections = notebookTree.selectedItems();
1019                 if (selections.size()==0) {
1020                         selectedNotebookGUIDs.clear();
1021                         menuBar.notebookEditAction.setEnabled(false);
1022                         menuBar.notebookDeleteAction.setEnabled(false);
1023                 }
1024         } else {
1025                 String guid = "";
1026                 if (selections.size() > 0)
1027                         guid = (selections.get(0).text(2));
1028                 if (!guid.equals(""))
1029                         selectedNotebookGUIDs.add(guid);
1030         }
1031         listManager.setSelectedNotebooks(selectedNotebookGUIDs);
1032         listManager.loadNotesIndex();
1033         noteIndexUpdated(false);
1034                 logger.log(logger.HIGH, "Leaving NeverNote.notebookTreeSelection");
1035
1036     }
1037     private void clearNotebookFilter() {
1038         notebookTree.blockSignals(true);
1039         notebookTree.clearSelection();
1040                 menuBar.noteRestoreAction.setVisible(false);
1041         menuBar.notebookEditAction.setEnabled(false);
1042         menuBar.notebookDeleteAction.setEnabled(false);
1043         selectedNotebookGUIDs.clear();
1044         listManager.setSelectedNotebooks(selectedNotebookGUIDs);
1045         notebookTree.blockSignals(false);
1046     }
1047         // Triggered when the notebook DB has been updated
1048         private void notebookIndexUpdated() {
1049                 logger.log(logger.HIGH, "Entering NeverNote.notebookIndexUpdated");
1050                 if (selectedNotebookGUIDs == null)
1051                         selectedNotebookGUIDs = new ArrayList<String>();
1052                 List<Notebook> books = conn.getNotebookTable().getAll();
1053                 for (int i=books.size()-1; i>=0; i--) {
1054                         for (int j=0; j<listManager.getArchiveNotebookIndex().size(); j++) {
1055                                 if (listManager.getArchiveNotebookIndex().get(j).getGuid().equals(books.get(i).getGuid())) {
1056                                         books.remove(i);
1057                                         j=listManager.getArchiveNotebookIndex().size();
1058                                 }
1059                         }
1060                 }
1061                 
1062                 
1063                 listManager.countNotebookResults(listManager.getNoteIndex());
1064                 notebookTree.blockSignals(true);
1065         notebookTree.load(books, listManager.getLocalNotebooks());
1066         for (int i=selectedNotebookGUIDs.size()-1; i>=0; i--) {
1067                 boolean found = notebookTree.selectGuid(selectedNotebookGUIDs.get(i));
1068                 if (!found)
1069                         selectedNotebookGUIDs.remove(i);
1070         }
1071         notebookTree.blockSignals(false);
1072         
1073                 logger.log(logger.HIGH, "Leaving NeverNote.notebookIndexUpdated");
1074     }
1075     // Show/Hide note information
1076         private void toggleNotebookWindow() {
1077                 logger.log(logger.HIGH, "Entering NeverNote.toggleNotebookWindow");
1078         if (notebookTree.isVisible())
1079                 notebookTree.hide();
1080         else
1081                 notebookTree.show();
1082         menuBar.hideNotebooks.setChecked(notebookTree.isVisible());
1083         Global.saveWindowVisible("notebookTree", notebookTree.isVisible());
1084         logger.log(logger.HIGH, "Leaving NeverNote.toggleNotebookWindow");
1085     }   
1086         // Add a new notebook
1087         @SuppressWarnings("unused")
1088         private void addNotebook() {
1089                 logger.log(logger.HIGH, "Inside NeverNote.addNotebook");
1090                 NotebookEdit edit = new NotebookEdit();
1091                 edit.setNotebooks(listManager.getNotebookIndex());
1092                 edit.exec();
1093         
1094                 if (!edit.okPressed())
1095                         return;
1096         
1097                 Calendar currentTime = new GregorianCalendar();
1098                 Long l = new Long(currentTime.getTimeInMillis());
1099                 String randint = new String(Long.toString(l));
1100         
1101                 Notebook newBook = new Notebook();
1102                 newBook.setUpdateSequenceNum(0);
1103                 newBook.setGuid(randint);
1104                 newBook.setName(edit.getNotebook());
1105                 newBook.setServiceCreated(new Date().getTime());
1106                 newBook.setServiceUpdated(new Date().getTime());
1107                 newBook.setDefaultNotebook(false);
1108                 newBook.setPublished(false);
1109                 
1110                 listManager.getNotebookIndex().add(newBook);
1111                 if (edit.isLocal())
1112                         listManager.getLocalNotebooks().add(newBook.getGuid());
1113                 conn.getNotebookTable().addNotebook(newBook, true, edit.isLocal());
1114                 notebookIndexUpdated();
1115                 listManager.countNotebookResults(listManager.getNoteIndex());
1116 //              notebookTree.updateCounts(listManager.getNotebookIndex(), listManager.getNotebookCounter());
1117                 logger.log(logger.HIGH, "Leaving NeverNote.addNotebook");
1118         }
1119         // Edit an existing notebook
1120         @SuppressWarnings("unused")
1121         private void editNotebook() {
1122                 logger.log(logger.HIGH, "Entering NeverNote.editNotebook");
1123                 NotebookEdit edit = new NotebookEdit();
1124                 edit.setTitle("Edit Notebook");
1125                 edit.setLocalCheckboxEnabled(false);
1126                 List<QTreeWidgetItem> selections = notebookTree.selectedItems();
1127                 QTreeWidgetItem currentSelection;
1128                 currentSelection = selections.get(0);
1129                 edit.setNotebook(currentSelection.text(0));
1130                 edit.setNotebooks(listManager.getNotebookIndex());
1131                 edit.exec();
1132         
1133                 if (!edit.okPressed())
1134                         return;
1135         
1136                 String guid = currentSelection.text(2);
1137                 updateListNotebookName(currentSelection.text(0), edit.getNotebook());
1138                 currentSelection.setText(0, edit.getNotebook());
1139                 
1140                 for (int i=0; i<listManager.getNotebookIndex().size(); i++) {
1141                         if (listManager.getNotebookIndex().get(i).getGuid().equals(guid)) {
1142                                 listManager.getNotebookIndex().get(i).setName(edit.getNotebook());
1143                                 conn.getNotebookTable().updateNotebook(listManager.getNotebookIndex().get(i), true);
1144                                 i=listManager.getNotebookIndex().size();
1145                         }
1146                 }
1147                 
1148                 // Build a list of non-closed notebooks
1149                 List<Notebook> nbooks = new ArrayList<Notebook>();
1150                 for (int i=0; i<listManager.getNotebookIndex().size(); i++) {
1151                         boolean found=false;
1152                         for (int j=0; j<listManager.getArchiveNotebookIndex().size(); j++) {
1153                                 if (listManager.getArchiveNotebookIndex().get(j).getGuid().equals(listManager.getNotebookIndex().get(i).getGuid()))
1154                                         found = true;
1155                         }
1156                         if (!found)
1157                                 nbooks.add(listManager.getNotebookIndex().get(i));
1158                 }
1159                 
1160                 browserWindow.setNotebookList(nbooks);
1161                 logger.log(logger.HIGH, "Leaving NeverNote.editNotebook");
1162         }
1163         // Delete an existing notebook
1164         @SuppressWarnings("unused")
1165         private void deleteNotebook() {
1166                 logger.log(logger.HIGH, "Entering NeverNote.deleteNotebook");
1167                 boolean assigned = false;
1168                 // Check if any notes have this notebook
1169                 List<QTreeWidgetItem> selections = notebookTree.selectedItems();
1170         for (int i=0; i<selections.size(); i++) {
1171                 QTreeWidgetItem currentSelection;
1172                 currentSelection = selections.get(i);
1173                 String guid = currentSelection.text(2);
1174                 for (int j=0; j<listManager.getNoteIndex().size(); j++) {
1175                         String noteGuid = listManager.getNoteIndex().get(j).getNotebookGuid();
1176                         if (noteGuid.equals(guid)) {
1177                                 assigned = true;
1178                                 j=listManager.getNoteIndex().size();
1179                                 i=selections.size();
1180                         }
1181                 }
1182         }
1183                 if (assigned) {
1184                         QMessageBox.information(this, "Unable to Delete", "Some of the selected notebook(s) contain notes.\n"+
1185                                         "Please delete the notes or move them to another notebook before deleting any notebooks.");
1186                         return;
1187                 }
1188                 
1189                 if (conn.getNotebookTable().getAll().size() == 1) {
1190                         QMessageBox.information(this, "Unable to Delete", "You must have at least one notebook.");
1191                         return;
1192                 }
1193         
1194         // If all notebooks are clear, verify the delete
1195                 if (QMessageBox.question(this, "Confirmation", "Delete the selected notebooks?",
1196                         QMessageBox.StandardButton.Yes, 
1197                         QMessageBox.StandardButton.No)==StandardButton.No.value()) {
1198                         return;
1199                 }
1200                 
1201                 // If confirmed, delete the notebook
1202         for (int i=selections.size()-1; i>=0; i--) {
1203                 QTreeWidgetItem currentSelection;
1204                 currentSelection = selections.get(i);
1205                 String guid = currentSelection.text(2);
1206                 conn.getNotebookTable().expungeNotebook(guid, true);
1207                 listManager.deleteNotebook(guid);
1208         }
1209 //        for (int i=<dbRunner.getLocalNotebooks().size()-1; i>=0; i--) {
1210  //             if (dbRunner.getLocalNotebooks().get(i).equals(arg0))
1211  //       }
1212         notebookTree.load(listManager.getNotebookIndex(), listManager.getLocalNotebooks());
1213         listManager.countNotebookResults(listManager.getNoteIndex());
1214 //              notebookTree.updateCounts(listManager.getNotebookIndex(), listManager.getNotebookCounter());
1215         logger.log(logger.HIGH, "Entering NeverNote.deleteNotebook");
1216         }
1217         // A note's notebook has been updated
1218         @SuppressWarnings("unused")
1219         private void updateNoteNotebook(String guid, String notebookGuid) {
1220                 
1221                 // Update the list manager
1222                 listManager.updateNoteNotebook(guid, notebookGuid);
1223                 listManager.countNotebookResults(listManager.getNoteIndex());
1224 //              notebookTree.updateCounts(listManager.getNotebookIndex(), listManager.getNotebookCounter());    
1225                 
1226                 // Find the name of the notebook
1227                 String notebookName = null;
1228                 for (int i=0; i<listManager.getNotebookIndex().size(); i++) {
1229                         if (listManager.getNotebookIndex().get(i).getGuid().equals(notebookGuid)) {
1230                                 notebookName = listManager.getNotebookIndex().get(i).getName();
1231                                 i=listManager.getNotebookIndex().size();
1232                         }
1233                 }
1234                 
1235                 // If we found the name, update the browser window
1236                 if (notebookName != null) {
1237                         updateListNoteNotebook(guid, notebookName);
1238                         if (guid.equals(currentNoteGuid)) {
1239                                 int pos =  browserWindow.notebookBox.findText(notebookName);
1240                                 if (pos >=0)
1241                                         browserWindow.notebookBox.setCurrentIndex(pos);
1242                         }
1243                 }
1244                 
1245                 // If we're dealing with the current note, then we need to be sure and update the notebook there
1246                 if (guid.equals(currentNoteGuid)) {
1247                         if (currentNote != null) {
1248                                 currentNote.setNotebookGuid(notebookGuid);
1249                         }
1250                 }
1251         }
1252         // Open/close notebooks
1253         @SuppressWarnings("unused")
1254         private void closeNotebooks() {
1255                 NotebookArchive na = new NotebookArchive(listManager.getNotebookIndex(), listManager.getArchiveNotebookIndex());
1256                 na.exec();
1257                 if (!na.okClicked())
1258                         return;
1259                 
1260                 waitCursor(true);
1261                 listManager.getArchiveNotebookIndex().clear();
1262                 
1263                 for (int i=na.getClosedBookList().count()-1; i>=0; i--) {
1264                         String text = na.getClosedBookList().takeItem(i).text();
1265                         for (int j=0; j<listManager.getNotebookIndex().size(); j++) {
1266                                 if (listManager.getNotebookIndex().get(j).getName().equalsIgnoreCase(text)) {
1267                                         Notebook n = listManager.getNotebookIndex().get(j);
1268                                         conn.getNotebookTable().setArchived(n.getGuid(),true);
1269                                         listManager.getArchiveNotebookIndex().add(n);
1270                                         j=listManager.getNotebookIndex().size();
1271                                 }
1272                         }
1273                 }
1274                 
1275                 for (int i=na.getOpenBookList().count()-1; i>=0; i--) {
1276                         String text = na.getOpenBookList().takeItem(i).text();
1277                         for (int j=0; j<listManager.getNotebookIndex().size(); j++) {
1278                                 if (listManager.getNotebookIndex().get(j).getName().equalsIgnoreCase(text)) {
1279                                         Notebook n = listManager.getNotebookIndex().get(j);
1280                                         conn.getNotebookTable().setArchived(n.getGuid(),false);
1281                                         j=listManager.getNotebookIndex().size();
1282                                 }
1283                         }
1284                 }
1285                 
1286                 listManager.loadNotesIndex();
1287                 notebookIndexUpdated();
1288                 noteIndexUpdated(true);
1289 //              noteIndexUpdated(false);
1290                 
1291                 // Build a list of non-closed notebooks
1292                 List<Notebook> nbooks = new ArrayList<Notebook>();
1293                 for (int i=0; i<listManager.getNotebookIndex().size(); i++) {
1294                         boolean found=false;
1295                         for (int j=0; j<listManager.getArchiveNotebookIndex().size(); j++) {
1296                                 if (listManager.getArchiveNotebookIndex().get(j).getGuid().equals(listManager.getNotebookIndex().get(i).getGuid()))
1297                                         found = true;
1298                         }
1299                         if (!found)
1300                                 nbooks.add(listManager.getNotebookIndex().get(i));
1301                 }
1302                 waitCursor(false);
1303                 browserWindow.setNotebookList(nbooks);
1304         }
1305
1306         
1307         
1308         
1309         
1310     //***************************************************************
1311     //***************************************************************
1312     //** These functions deal with Tag menu items
1313     //***************************************************************
1314     //***************************************************************
1315         // Add a new notebook
1316         @SuppressWarnings("unused")
1317         private void addTag() {
1318                 logger.log(logger.HIGH, "Inside NeverNote.addTag");
1319                 TagEdit edit = new TagEdit();
1320                 edit.setTagList(listManager.getTagIndex());
1321                 edit.exec();
1322         
1323                 if (!edit.okPressed())
1324                         return;
1325         
1326                 Calendar currentTime = new GregorianCalendar();
1327                 Long l = new Long(currentTime.getTimeInMillis());
1328                 String randint = new String(Long.toString(l));
1329         
1330                 Tag newTag = new Tag();
1331                 newTag.setUpdateSequenceNum(0);
1332                 newTag.setGuid(randint);
1333                 newTag.setName(edit.getTag());
1334                 conn.getTagTable().addTag(newTag, true);
1335                 listManager.getTagIndex().add(newTag);
1336                 reloadTagTree();
1337                 
1338                 logger.log(logger.HIGH, "Leaving NeverNote.addTag");
1339         }
1340         private void reloadTagTree() {
1341                 logger.log(logger.HIGH, "Entering NeverNote.reloadTagTree");
1342                 tagIndexUpdated(false);
1343                 boolean filter = false;
1344                 listManager.countTagResults(listManager.getNoteIndex());
1345                 if (notebookTree.selectedItems().size() > 0 
1346                                                   && !notebookTree.selectedItems().get(0).text(0).equalsIgnoreCase("All Notebooks"))
1347                                                   filter = true;
1348                 if (tagTree.selectedItems().size() > 0)
1349                         filter = true;
1350                 tagTree.showAllTags(!filter);
1351                 logger.log(logger.HIGH, "Leaving NeverNote.reloadTagTree");
1352         }
1353         // Edit an existing tag
1354         @SuppressWarnings("unused")
1355         private void editTag() {
1356                 logger.log(logger.HIGH, "Entering NeverNote.editTag");
1357                 TagEdit edit = new TagEdit();
1358                 edit.setTitle("Edit Tag");
1359                 List<QTreeWidgetItem> selections = tagTree.selectedItems();
1360                 QTreeWidgetItem currentSelection;
1361                 currentSelection = selections.get(0);
1362                 edit.setTag(currentSelection.text(0));
1363                 edit.setTagList(listManager.getTagIndex());
1364                 edit.exec();
1365         
1366                 if (!edit.okPressed())
1367                         return;
1368         
1369                 String guid = currentSelection.text(2);
1370                 currentSelection.setText(0,edit.getTag());
1371                 
1372                 for (int i=0; i<listManager.getTagIndex().size(); i++) {
1373                         if (listManager.getTagIndex().get(i).getGuid().equals(guid)) {
1374                                 listManager.getTagIndex().get(i).setName(edit.getTag());
1375                                 conn.getTagTable().updateTag(listManager.getTagIndex().get(i), true);
1376                                 updateListTagName(guid);
1377                                 if (currentNote != null && currentNote.getTagGuids().contains(guid))
1378                                         browserWindow.setTag(getTagNamesForNote(currentNote));
1379                                 logger.log(logger.HIGH, "Leaving NeverNote.editTag");
1380                                 return;
1381                         }
1382                 }
1383                 browserWindow.setTag(getTagNamesForNote(currentNote));
1384                 logger.log(logger.HIGH, "Leaving NeverNote.editTag...");
1385         }
1386         // Delete an existing tag
1387         @SuppressWarnings("unused")
1388         private void deleteTag() {
1389                 logger.log(logger.HIGH, "Entering NeverNote.deleteTag");
1390                 
1391                 if (QMessageBox.question(this, "Confirmation", "Delete the selected tags?",
1392                         QMessageBox.StandardButton.Yes, 
1393                         QMessageBox.StandardButton.No)==StandardButton.No.value()) {
1394                                                         return;
1395                 }
1396                 
1397                 List<QTreeWidgetItem> selections = tagTree.selectedItems();
1398         for (int i=selections.size()-1; i>=0; i--) {
1399                 QTreeWidgetItem currentSelection;
1400                 currentSelection = selections.get(i);                   
1401                 removeTagItem(currentSelection.text(2));
1402         }
1403         tagIndexUpdated(true);
1404         listManager.countTagResults(listManager.getNoteIndex());
1405 //              tagTree.updateCounts(listManager.getTagCounter());
1406         logger.log(logger.HIGH, "Leaving NeverNote.deleteTag");
1407         }
1408         // Remove a tag tree item.  Go recursively down & remove the children too
1409         private void removeTagItem(String guid) {
1410         for (int j=listManager.getTagIndex().size()-1; j>=0; j--) {             
1411                 String parent = listManager.getTagIndex().get(j).getParentGuid();
1412                 if (parent != null && parent.equals(guid)) {            
1413                         //Remove this tag's children
1414                         removeTagItem(listManager.getTagIndex().get(j).getGuid());
1415                 }
1416         }
1417         //Now, remove this tag
1418         removeListTagName(guid);
1419         conn.getTagTable().expungeTag(guid, true);                      
1420         for (int a=0; a<listManager.getTagIndex().size(); a++) {
1421                 if (listManager.getTagIndex().get(a).getGuid().equals(guid)) {
1422                         listManager.getTagIndex().remove(a);
1423                         return;
1424                 }
1425         }
1426         }
1427         // Setup the tree containing the user's tags
1428     private void initializeTagTree() {
1429         logger.log(logger.HIGH, "Entering NeverNote.initializeTagTree");
1430         tagTree.itemSelectionChanged.connect(this, "tagTreeSelection()");
1431         listManager.tagSignal.refreshTagTreeCounts.connect(tagTree, "updateCounts(List)");
1432         logger.log(logger.HIGH, "Leaving NeverNote.initializeTagTree");
1433     }
1434     // Listener when a tag is selected
1435     @SuppressWarnings("unused")
1436         private void tagTreeSelection() {
1437         logger.log(logger.HIGH, "Entering NeverNote.tagTreeSelection");
1438         
1439         List<QTreeWidgetItem> x = tagTree.selectedItems();
1440         for (int i=0; i<x.size(); i++) {
1441         }
1442         
1443         clearTrashFilter();
1444         clearAttributeFilter();
1445         clearSavedSearchFilter();
1446         
1447                 menuBar.noteRestoreAction.setVisible(false);
1448                 
1449         List<QTreeWidgetItem> selections = tagTree.selectedItems();
1450         QTreeWidgetItem currentSelection;
1451         selectedTagGUIDs.clear();
1452         for (int i=0; i<selections.size(); i++) {
1453                 currentSelection = selections.get(i);
1454                 selectedTagGUIDs.add(currentSelection.text(2));
1455         }
1456         if (selections.size() > 0) {
1457                 menuBar.tagEditAction.setEnabled(true);
1458                 menuBar.tagDeleteAction.setEnabled(true);
1459         }
1460         else {
1461                 menuBar.tagEditAction.setEnabled(false);
1462                 menuBar.tagDeleteAction.setEnabled(false);
1463         }
1464         listManager.setSelectedTags(selectedTagGUIDs);
1465         listManager.loadNotesIndex();
1466         noteIndexUpdated(false);
1467         logger.log(logger.HIGH, "Leaving NeverNote.tagTreeSelection");
1468     }
1469     // trigger the tag index to be refreshed
1470     @SuppressWarnings("unused")
1471         private void tagIndexUpdated() {
1472         tagIndexUpdated(true);
1473     }
1474     private void tagIndexUpdated(boolean reload) {
1475         logger.log(logger.HIGH, "Entering NeverNote.tagIndexUpdated");
1476                 if (selectedTagGUIDs == null)
1477                         selectedTagGUIDs = new ArrayList<String>();
1478 //              selectedTagGUIDs.clear();  // clear out old entries
1479
1480                 tagTree.blockSignals(true);
1481                 if (reload)
1482                         tagTree.load(listManager.getTagIndex());
1483         for (int i=selectedTagGUIDs.size()-1; i>=0; i--) {
1484                 boolean found = tagTree.selectGuid(selectedTagGUIDs.get(i));
1485                 if (!found)
1486                         selectedTagGUIDs.remove(i);
1487         }
1488         tagTree.blockSignals(false);
1489         
1490                 browserWindow.setTag(getTagNamesForNote(currentNote));
1491         logger.log(logger.HIGH, "Leaving NeverNote.tagIndexUpdated");
1492     }   
1493     // Show/Hide note information
1494         private void toggleTagWindow() {
1495                 logger.log(logger.HIGH, "Entering NeverNote.toggleTagWindow");
1496         if (tagTree.isVisible())
1497                 tagTree.hide();
1498         else
1499                 tagTree.show();
1500         menuBar.hideTags.setChecked(tagTree.isVisible());
1501         Global.saveWindowVisible("tagTree", tagTree.isVisible());
1502         logger.log(logger.HIGH, "Leaving NeverNote.toggleTagWindow");
1503     }   
1504         // A note's tags have been updated
1505         @SuppressWarnings("unused")
1506         private void updateNoteTags(String guid, List<String> tags) {
1507                 listManager.saveNoteTags(guid, tags);
1508                 listManager.countTagResults(listManager.getNoteIndex());
1509                 StringBuffer names = new StringBuffer("");
1510                 for (int i=0; i<tags.size(); i++) {
1511                         names = names.append(tags.get(i));
1512                         if (i<tags.size()-1) {
1513                                 names.append(Global.tagDelimeter + " ");
1514                         }
1515                 }
1516                 browserWindow.setTag(names.toString());
1517                 noteDirty = true;
1518 //              tagTree.updateCounts(listManager.getTagCounter());
1519         }
1520         // Get a string containing all tag names for a note
1521         private String getTagNamesForNote(Note n) {
1522                 logger.log(logger.HIGH, "Entering NeverNote.getTagNamesForNote");
1523                 if (n==null || n.getGuid() == null || n.getGuid().equals(""))
1524                         return "";
1525                 StringBuffer buffer = new StringBuffer(100);
1526                 Vector<String> v = new Vector<String>();
1527                 List<String> guids = n.getTagGuids();
1528                 
1529                 if (guids == null) 
1530                         return "";
1531                 
1532                 for (int i=0; i<guids.size(); i++) {
1533                         v.add(listManager.getTagNameByGuid(guids.get(i)));
1534                 }
1535                 Comparator<String> comparator = Collections.reverseOrder();
1536                 Collections.sort(v,comparator);
1537                 Collections.reverse(v);
1538                 
1539                 for (int i = 0; i<v.size(); i++) {
1540                         if (i>0) 
1541                                 buffer.append(", ");
1542                         buffer.append(v.get(i));
1543                 }
1544                 
1545                 logger.log(logger.HIGH, "Leaving NeverNote.getTagNamesForNote");
1546                 return buffer.toString();
1547         }       
1548         // Tags were added via dropping notes from the note list
1549         @SuppressWarnings("unused")
1550         private void tagsAdded(String noteGuid, String tagGuid) {
1551                 String tagName = null;
1552                 for (int i=0; i<listManager.getTagIndex().size(); i++) {
1553                         if (listManager.getTagIndex().get(i).getGuid().equals(tagGuid)) {
1554                                 tagName = listManager.getTagIndex().get(i).getName();
1555                                 i=listManager.getTagIndex().size();
1556                         }
1557                 }
1558                 if (tagName == null)
1559                         return;
1560                 
1561                 for (int i=0; i<noteTableView.model.rowCount(); i++) {
1562                         QModelIndex modelIndex =  noteTableView.model.index(i, Global.noteTableGuidPosition);
1563                         if (modelIndex != null) {
1564                                 SortedMap<Integer, Object> ix = noteTableView.model.itemData(modelIndex);
1565                                 String titleGuid = (String)ix.values().toArray()[0];
1566                                 if (titleGuid.equals(noteGuid)) {
1567                                         String text = (String)noteTableView.model.data(i, Global.noteTableTagPosition);
1568                                         if (!text.trim().equals(""))
1569                                                 text = text + Global.tagDelimeter + " " +tagName;
1570                                         else
1571                                                 text = tagName;
1572                                         noteTableView.model.setData(i, Global.noteTableTagPosition, text);
1573                                         noteTableView.model.setData(i, Global.noteTableSynchronizedPosition, "false");
1574                                         if (noteGuid.equals(currentNoteGuid))
1575                                                 browserWindow.setTag(text);
1576                                         i=noteTableView.model.rowCount();
1577                                 }
1578                         }
1579                 }
1580         }
1581         private void clearTagFilter() {
1582                 tagTree.blockSignals(true);
1583                 tagTree.clearSelection();
1584                 menuBar.noteRestoreAction.setVisible(false);
1585                 menuBar.tagEditAction.setEnabled(false);
1586                 menuBar.tagDeleteAction.setEnabled(false);
1587                 selectedTagGUIDs.clear();
1588         listManager.setSelectedTags(selectedTagGUIDs);
1589         tagTree.blockSignals(false);
1590         }
1591         
1592         
1593     //***************************************************************
1594     //***************************************************************
1595     //** These functions deal with Saved Search menu items
1596     //***************************************************************
1597     //***************************************************************
1598         // Add a new notebook
1599         @SuppressWarnings("unused")
1600         private void addSavedSearch() {
1601                 logger.log(logger.HIGH, "Inside NeverNote.addSavedSearch");
1602                 SavedSearchEdit edit = new SavedSearchEdit();
1603                 edit.setSearchList(listManager.getSavedSearchIndex());
1604                 edit.exec();
1605         
1606                 if (!edit.okPressed())
1607                         return;
1608         
1609                 Calendar currentTime = new GregorianCalendar();         
1610                 Long l = new Long(currentTime.getTimeInMillis());
1611                 String randint = new String(Long.toString(l));
1612         
1613                 SavedSearch search = new SavedSearch();
1614                 search.setUpdateSequenceNum(0);
1615                 search.setGuid(randint);
1616                 search.setName(edit.getName());
1617                 search.setQuery(edit.getQuery());
1618                 search.setFormat(QueryFormat.USER);
1619                 listManager.getSavedSearchIndex().add(search);
1620                 conn.getSavedSearchTable().addSavedSearch(search, true);
1621                 savedSearchIndexUpdated();
1622                 logger.log(logger.HIGH, "Leaving NeverNote.addSavedSearch");
1623         }
1624         // Edit an existing tag
1625         @SuppressWarnings("unused")
1626         private void editSavedSearch() {
1627                 logger.log(logger.HIGH, "Entering NeverNote.editSavedSearch");
1628                 SavedSearchEdit edit = new SavedSearchEdit();
1629                 edit.setTitle("Edit Search");
1630                 List<QTreeWidgetItem> selections = savedSearchTree.selectedItems();
1631                 QTreeWidgetItem currentSelection;
1632                 currentSelection = selections.get(0);
1633                 String guid = currentSelection.text(1);
1634                 SavedSearch s = conn.getSavedSearchTable().getSavedSearch(guid);
1635                 edit.setName(currentSelection.text(0));
1636                 edit.setQuery(s.getQuery());
1637                 edit.setSearchList(listManager.getSavedSearchIndex());
1638                 edit.exec();
1639         
1640                 if (!edit.okPressed())
1641                         return;
1642         
1643                 List<SavedSearch> list = listManager.getSavedSearchIndex();
1644                 SavedSearch search = null;
1645                 boolean found = false;
1646                 for (int i=0; i<list.size(); i++) {
1647                         search = list.get(i);
1648                         if (search.getGuid().equals(guid)) {
1649                                 i=list.size();
1650                                 found = true;
1651                         }
1652                 }
1653                 if (!found)
1654                         return;
1655                 search.setName(edit.getName());
1656                 search.setQuery(edit.getQuery());
1657                 conn.getSavedSearchTable().updateSavedSearch(search, true);
1658                 savedSearchIndexUpdated();
1659                 logger.log(logger.HIGH, "Leaving NeverNote.editSavedSearch");
1660         }
1661         // Delete an existing tag
1662         @SuppressWarnings("unused")
1663         private void deleteSavedSearch() {
1664                 logger.log(logger.HIGH, "Entering NeverNote.deleteSavedSearch");
1665                 
1666                 if (QMessageBox.question(this, "Confirmation", "Delete the selected search?",
1667                         QMessageBox.StandardButton.Yes, 
1668                         QMessageBox.StandardButton.No)==StandardButton.No.value()) {
1669                                                         return;
1670                 }
1671                 
1672                 List<QTreeWidgetItem> selections = savedSearchTree.selectedItems();
1673         for (int i=selections.size()-1; i>=0; i--) {
1674                 QTreeWidgetItem currentSelection;
1675                 currentSelection = selections.get(i);
1676                 for (int j=0; j<listManager.getSavedSearchIndex().size(); j++) {
1677                         if (listManager.getSavedSearchIndex().get(j).getGuid().equals(currentSelection.text(1))) {
1678                                 conn.getSavedSearchTable().expungeSavedSearch(listManager.getSavedSearchIndex().get(j).getGuid(), true);
1679                                 listManager.getSavedSearchIndex().remove(j);
1680                                 j=listManager.getSavedSearchIndex().size()+1;
1681                         }
1682                 }
1683                 selections.remove(i);
1684         }
1685         savedSearchIndexUpdated();
1686         logger.log(logger.HIGH, "Leaving NeverNote.deleteSavedSearch");
1687         }
1688     // Setup the tree containing the user's tags
1689     private void initializeSavedSearchTree() {
1690         logger.log(logger.HIGH, "Entering NeverNote.initializeSavedSearchTree");
1691         savedSearchTree.itemSelectionChanged.connect(this, "savedSearchTreeSelection()");
1692         logger.log(logger.HIGH, "Leaving NeverNote.initializeSavedSearchTree");
1693     }
1694     // Listener when a tag is selected
1695     @SuppressWarnings("unused")
1696         private void savedSearchTreeSelection() {
1697         logger.log(logger.HIGH, "Entering NeverNote.savedSearchTreeSelection");
1698
1699         clearNotebookFilter();
1700         clearTagFilter();
1701         clearTrashFilter();
1702         clearAttributeFilter();
1703         
1704         String currentGuid = selectedSavedSearchGUID;
1705         menuBar.savedSearchEditAction.setEnabled(true);
1706         menuBar.savedSearchDeleteAction.setEnabled(true);
1707         List<QTreeWidgetItem> selections = savedSearchTree.selectedItems();
1708         QTreeWidgetItem currentSelection;
1709         selectedSavedSearchGUID = "";
1710         for (int i=0; i<selections.size(); i++) {
1711                 currentSelection = selections.get(i);
1712                 if (currentSelection.text(1).equals(currentGuid)) {
1713                         currentSelection.setSelected(false);
1714                 } else {
1715                         selectedSavedSearchGUID = currentSelection.text(1);
1716                 }
1717 //              i = selections.size() +1;
1718         }
1719         
1720         // There is the potential for no notebooks to be selected if this 
1721         // happens then we make it look like all notebooks were selecetd.
1722         // If that happens, just select the "all notebooks"
1723         if (selections.size()==0) {
1724                 clearSavedSearchFilter();
1725         }
1726         listManager.setSelectedSavedSearch(selectedSavedSearchGUID);
1727         
1728         logger.log(logger.HIGH, "Leaving NeverNote.savedSearchTreeSelection");
1729     }
1730     private void clearSavedSearchFilter() {
1731         menuBar.savedSearchEditAction.setEnabled(false);
1732         menuBar.savedSearchDeleteAction.setEnabled(false);
1733         savedSearchTree.blockSignals(true);
1734         savedSearchTree.clearSelection();
1735         savedSearchTree.blockSignals(false);
1736         selectedSavedSearchGUID = "";
1737         searchField.setEditText("");
1738         searchPerformed = false;
1739         listManager.setSelectedSavedSearch(selectedSavedSearchGUID);
1740     }
1741     // trigger the tag index to be refreshed
1742         private void savedSearchIndexUpdated() { 
1743                 if (selectedSavedSearchGUID == null)
1744                         selectedSavedSearchGUID = new String();
1745                 savedSearchTree.blockSignals(true);
1746         savedSearchTree.load(listManager.getSavedSearchIndex());
1747         savedSearchTree.selectGuid(selectedSavedSearchGUID);
1748         savedSearchTree.blockSignals(false);
1749     }
1750     // trigger when the saved search selection changes
1751     @SuppressWarnings("unused")
1752         private void updateSavedSearchSelection() {
1753                 logger.log(logger.HIGH, "Entering NeverNote.updateSavedSearchSelection()");
1754                 
1755         menuBar.savedSearchEditAction.setEnabled(true);
1756         menuBar.savedSearchDeleteAction.setEnabled(true);
1757         List<QTreeWidgetItem> selections = savedSearchTree.selectedItems();
1758
1759         if (selections.size() > 0) {
1760                 menuBar.savedSearchEditAction.setEnabled(true);
1761                 menuBar.savedSearchDeleteAction.setEnabled(true);
1762                 selectedSavedSearchGUID = selections.get(0).text(1);
1763                 SavedSearch s = conn.getSavedSearchTable().getSavedSearch(selectedSavedSearchGUID);
1764                 searchField.setEditText(s.getQuery());
1765         } else { 
1766                 menuBar.savedSearchEditAction.setEnabled(false);
1767                 menuBar.savedSearchDeleteAction.setEnabled(false);
1768                 selectedSavedSearchGUID = "";
1769                 searchField.setEditText("");
1770         }
1771         searchFieldChanged();
1772         
1773                 logger.log(logger.HIGH, "Leaving NeverNote.updateSavedSearchSelection()");
1774
1775         
1776     }
1777     // Show/Hide note information
1778         private void toggleSavedSearchWindow() {
1779                 logger.log(logger.HIGH, "Entering NeverNote.toggleSavedSearchWindow");
1780         if (savedSearchTree.isVisible())
1781                 savedSearchTree.hide();
1782         else
1783                 savedSearchTree.show();
1784         menuBar.hideSavedSearches.setChecked(savedSearchTree.isVisible());
1785                                 
1786                 Global.saveWindowVisible("savedSearchTree", savedSearchTree.isVisible());
1787         logger.log(logger.HIGH, "Leaving NeverNote.toggleSavedSearchWindow");
1788     }
1789         
1790         
1791         
1792         
1793     //***************************************************************
1794     //***************************************************************
1795     //** These functions deal with Help menu & tool menu items
1796     //***************************************************************
1797     //***************************************************************
1798         // Show database status
1799         @SuppressWarnings("unused")
1800         private void databaseStatus() {
1801                 waitCursor(true);
1802                 int dirty = conn.getNoteTable().getDirtyCount();
1803                 int unindexed = conn.getNoteTable().getUnindexedCount();
1804                 DatabaseStatus status = new DatabaseStatus();
1805                 status.setUnsynchronized(dirty);
1806                 status.setUnindexed(unindexed);
1807                 status.setNoteCount(conn.getNoteTable().getNoteCount());
1808                 status.setNotebookCount(listManager.getNotebookIndex().size());
1809                 status.setSavedSearchCount(listManager.getSavedSearchIndex().size());
1810                 status.setTagCount(listManager.getTagIndex().size());
1811                 status.setResourceCount(conn.getNoteTable().noteResourceTable.getResourceCount());
1812                 status.setWordCount(conn.getWordsTable().getWordCount());
1813                 waitCursor(false);
1814                 status.exec();
1815         }
1816         // Compact the database
1817         @SuppressWarnings("unused")
1818         private void compactDatabase() {
1819         logger.log(logger.HIGH, "Entering NeverNote.compactDatabase");
1820                 if (QMessageBox.question(this, "Confirmation", "This will free unused space in the database, "+
1821                                 "but please be aware that depending upon the size of your database this can be time consuming " +
1822                                 "and NeverNote will be unresponsive until it is complete.  Do you wish to continue?",
1823                                 QMessageBox.StandardButton.Yes, 
1824                                 QMessageBox.StandardButton.No)==StandardButton.No.value() && Global.verifyDelete() == true) {
1825                                                         return;
1826                 }
1827                 setMessage("Compacting database.");
1828                 waitCursor(true);
1829                 listManager.compactDatabase();
1830                 waitCursor(false);
1831                 setMessage("Database compact is complete.");            
1832         logger.log(logger.HIGH, "Leaving NeverNote.compactDatabase");
1833     }
1834         @SuppressWarnings("unused")
1835         private void accountInformation() {
1836                 logger.log(logger.HIGH, "Entering NeverNote.accountInformation");
1837                 AccountDialog dialog = new AccountDialog();
1838                 dialog.show();
1839                 logger.log(logger.HIGH, "Leaving NeverNote.accountInformation");
1840         }
1841         @SuppressWarnings("unused")
1842         private void releaseNotes() {
1843                 logger.log(logger.HIGH, "Entering NeverNote.releaseNotes");
1844                 QDialog dialog = new QDialog(this);
1845                 QHBoxLayout layout = new QHBoxLayout();
1846                 QTextEdit textBox = new QTextEdit();
1847                 layout.addWidget(textBox);
1848                 textBox.setReadOnly(true);
1849                 QFile file = new QFile(Global.getDirectoryPath()+"release.txt");
1850                 if (!file.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.ReadOnly,
1851                 QIODevice.OpenModeFlag.Text)))
1852                         return;
1853                 textBox.setText(file.readAll().toString());
1854                 file.close();
1855                 dialog.setWindowTitle("Release Notes");
1856                 dialog.setLayout(layout);
1857                 dialog.show();
1858                 logger.log(logger.HIGH, "Leaving NeverNote.releaseNotes");
1859         }
1860         // Called when user picks Log from the help menu
1861         @SuppressWarnings("unused")
1862         private void logger() {
1863                 logger.log(logger.HIGH, "Entering NeverNote.logger");
1864                 QDialog dialog = new QDialog(this);
1865                 QHBoxLayout layout = new QHBoxLayout();
1866                 QListWidget textBox = new QListWidget();
1867                 layout.addWidget(textBox);
1868                 textBox.addItems(emitLog);
1869                 
1870                 dialog.setLayout(layout);
1871                 dialog.setWindowTitle("Mesasge Log");
1872                 dialog.show();
1873                 logger.log(logger.HIGH, "Leaving NeverNote.logger");
1874         }
1875         // Menu option "help/about" was selected
1876         @SuppressWarnings("unused")
1877         private void about() {
1878                 logger.log(logger.HIGH, "Entering NeverNote.about");
1879                 QMessageBox.about(this, 
1880                                                 tr("About NeverNote"),
1881                                                 tr("<h4><center><b>NeverNote</b></center></h4><hr><center>Version "+Global.version+"<hr></center>Evernote"
1882                                                                 +" Generic client.<br><br>" 
1883                                                                 +"Licensed under GPL v2.  <br><hr><br>"
1884                                                                 +"Evernote is copyright 2001-2010 by Evernote Corporation<br>"
1885                                                                 +"Jambi and QT are the licensed trademark of Nokia Corporation<br>"
1886                                                                 +"PDFRenderer is licened under the LGPL<br>"
1887                                                                 +"Jazzy is licened under the LGPL<br>"
1888                                                                 +"Java is a registered trademark of Sun Microsystems.<br><hr>"));       
1889                 logger.log(logger.HIGH, "Leaving NeverNote.about");
1890         }
1891         // Hide the entire left hand side
1892         @SuppressWarnings("unused")
1893         private void toggleLeftSide() {
1894                 boolean hidden;
1895                 
1896                 hidden = !menuBar.hideLeftSide.isChecked();
1897                 menuBar.hideLeftSide.setChecked(!hidden);
1898                 
1899                 if (notebookTree.isVisible() != hidden)
1900                         toggleNotebookWindow();
1901                 if (savedSearchTree.isVisible() != hidden)
1902                         toggleSavedSearchWindow();
1903                 if (tagTree.isVisible() != hidden)
1904                         toggleTagWindow();
1905                 if (attributeTree.isVisible() != hidden)
1906                         toggleAttributesWindow();
1907                 if (trashTree.isVisible() != hidden)
1908                         toggleTrashWindow();
1909                 
1910                 Global.saveWindowVisible("leftPanel", hidden);
1911                 
1912         }
1913                         
1914         
1915     //***************************************************************
1916     //***************************************************************
1917     //** These functions deal with the Toolbar
1918     //***************************************************************
1919     //***************************************************************  
1920         // Text in the search bar has been cleared
1921         private void searchFieldCleared() {
1922                 searchField.setEditText("");
1923                 saveNoteIndexWidth();
1924         }
1925         // text in the search bar changed.  We only use this to tell if it was cleared, 
1926         // otherwise we trigger off searchFieldChanged.
1927         @SuppressWarnings("unused")
1928         private void searchFieldTextChanged(String text) {
1929                 if (text.trim().equals("")) {
1930                         searchFieldCleared();
1931                         if (searchPerformed) {
1932                                 noteCache.clear();
1933                                 listManager.setEnSearch("");
1934 /////                           listManager.clearNoteIndexSearch();
1935                                 //noteIndexUpdated(true);
1936                                 listManager.loadNotesIndex();
1937                                 refreshEvernoteNote(true);
1938                                 noteIndexUpdated(false);
1939                         }
1940                         searchPerformed = false;
1941                 }
1942         }
1943     // Text in the toolbar has changed
1944     private void searchFieldChanged() {
1945         logger.log(logger.HIGH, "Entering NeverNote.searchFieldChanged");
1946         noteCache.clear();
1947         saveNoteIndexWidth();
1948         String text = searchField.currentText();
1949         listManager.setEnSearch(text.trim());
1950         listManager.loadNotesIndex();
1951 //--->>>        noteIndexUpdated(true);
1952         noteIndexUpdated(false);
1953         refreshEvernoteNote(true);
1954         searchPerformed = true;
1955         logger.log(logger.HIGH, "Leaving NeverNote.searchFieldChanged");
1956     }
1957     // Build the window tool bar
1958     private void setupToolBar() {
1959         logger.log(logger.HIGH, "Entering NeverNote.setupToolBar");
1960         toolBar = addToolBar(tr("toolBar"));    
1961
1962         prevButton = toolBar.addAction("Previous");
1963         QIcon prevIcon = new QIcon(iconPath+"back.png");
1964         prevButton.setIcon(prevIcon);
1965         prevButton.triggered.connect(this, "previousViewedAction()");   
1966         
1967         nextButton = toolBar.addAction("Next");
1968         QIcon nextIcon = new QIcon(iconPath+"forward.png");
1969         nextButton.setIcon(nextIcon);
1970         nextButton.triggered.connect(this, "nextViewedAction()");       
1971         
1972         upButton = toolBar.addAction("Up");
1973         QIcon upIcon = new QIcon(iconPath+"up.png");
1974         upButton.setIcon(upIcon);
1975         upButton.triggered.connect(this, "upAction()");         
1976         
1977         downButton = toolBar.addAction("Down");
1978         QIcon downIcon = new QIcon(iconPath+"down.png");
1979         downButton.setIcon(downIcon);
1980         downButton.triggered.connect(this, "downAction()");
1981         
1982         synchronizeButton = toolBar.addAction("Synchronize");
1983         synchronizeAnimation = new ArrayList<QIcon>();
1984         synchronizeAnimation.add(new QIcon(iconPath+"synchronize-0.png"));
1985         synchronizeAnimation.add(new QIcon(iconPath+"synchronize-1.png"));
1986         synchronizeAnimation.add(new QIcon(iconPath+"synchronize-2.png"));
1987         synchronizeAnimation.add(new QIcon(iconPath+"synchronize-3.png"));
1988         synchronizeButton.setIcon(synchronizeAnimation.get(0));
1989         synchronizeFrame = 0;
1990         synchronizeButton.triggered.connect(this, "evernoteSync()");
1991         
1992         printButton = toolBar.addAction("Print");
1993         QIcon printIcon = new QIcon(iconPath+"print.png");
1994         printButton.setIcon(printIcon);
1995         printButton.triggered.connect(this, "printNote()");
1996         
1997         tagButton = toolBar.addAction("Tag"); 
1998         QIcon tagIcon = new QIcon(iconPath+"tag.png");
1999         tagButton.setIcon(tagIcon);
2000         tagButton.triggered.connect(browserWindow, "modifyTags()");
2001         
2002         attributeButton = toolBar.addAction("Attributes"); 
2003         QIcon attributeIcon = new QIcon(iconPath+"attribute.png");
2004         attributeButton.setIcon(attributeIcon);
2005         attributeButton.triggered.connect(this, "toggleNoteInformation()");
2006                 
2007         emailButton = toolBar.addAction("Email");
2008         QIcon emailIcon = new QIcon(iconPath+"email.png");
2009         emailButton.setIcon(emailIcon);
2010         emailButton.triggered.connect(this, "emailNote()");
2011         
2012         deleteButton = toolBar.addAction("Delete");     
2013         QIcon deleteIcon = new QIcon(iconPath+"delete.png");
2014         deleteButton.setIcon(deleteIcon);
2015         deleteButton.triggered.connect(this, "deleteNote()");
2016                 
2017         newButton = toolBar.addAction("New");
2018         QIcon newIcon = new QIcon(iconPath+"new.png");
2019         newButton.triggered.connect(this, "addNote()");
2020         newButton.setIcon(newIcon);
2021         toolBar.addSeparator();
2022         toolBar.addWidget(new QLabel("Quota:"));
2023         toolBar.addWidget(quotaBar);
2024         //quotaBar.setSizePolicy(Policy.Minimum, Policy.Minimum);
2025         updateQuotaBar();
2026         
2027         // Setup the zoom
2028         zoomSpinner = new QSpinBox();
2029         zoomSpinner.setMinimum(10);
2030         zoomSpinner.setMaximum(1000);
2031         zoomSpinner.setAccelerated(true);
2032         zoomSpinner.setSingleStep(10);
2033         zoomSpinner.setValue(100);
2034         zoomSpinner.valueChanged.connect(this, "zoomChanged()");
2035         toolBar.addWidget(new QLabel("Zoom"));
2036         toolBar.addWidget(zoomSpinner);
2037         
2038         //toolBar.addWidget(new QLabel("                    "));
2039         toolBar.addSeparator();
2040         toolBar.addWidget(new QLabel("  Search:"));
2041         toolBar.addWidget(searchField);
2042         QSizePolicy sizePolicy = new QSizePolicy();
2043         sizePolicy.setHorizontalPolicy(Policy.MinimumExpanding);
2044         searchField.setSizePolicy(sizePolicy);
2045         searchField.setInsertPolicy(InsertPolicy.InsertAtTop);
2046
2047         searchClearButton = toolBar.addAction("Search Clear");
2048         QIcon searchClearIcon = new QIcon(iconPath+"searchclear.png");
2049         searchClearButton.setIcon(searchClearIcon);
2050         searchClearButton.triggered.connect(this, "searchFieldCleared()");
2051         
2052         logger.log(logger.HIGH, "Leaving NeverNote.setupToolBar");
2053     }
2054     // Update the sychronize button picture
2055     @SuppressWarnings("unused")
2056         private void updateSyncButton() {
2057         synchronizeFrame++;
2058         if (synchronizeFrame == 4) 
2059                 synchronizeFrame = 0;
2060         synchronizeButton.setIcon(synchronizeAnimation.get(synchronizeFrame));
2061     }
2062     // Synchronize with Evernote
2063         @SuppressWarnings("unused")
2064         private void evernoteSync() {
2065         logger.log(logger.HIGH, "Entering NeverNote.evernoteSync");
2066         if (!Global.isConnected)
2067                 remoteConnect();
2068         if (Global.isConnected)
2069                 synchronizeAnimationTimer.start(200);
2070         syncTimer();
2071         logger.log(logger.HIGH, "Leaving NeverNote.evernoteSync");
2072     }
2073     private void updateQuotaBar() {
2074         long limit = Global.getUploadLimit();
2075         long amount = Global.getUploadAmount();
2076         if (amount>0 && limit>0) {
2077                 int percent =(int)(amount*100/limit);
2078                 quotaBar.setValue(percent);
2079         } else 
2080                 quotaBar.setValue(0);
2081     }
2082         // Zoom changed
2083     @SuppressWarnings("unused")
2084         private void zoomChanged() {
2085         browserWindow.getBrowser().setZoomFactor(new Double(zoomSpinner.value())/100);
2086     }
2087
2088     //****************************************************************
2089     //****************************************************************
2090     //* System Tray functions
2091     //****************************************************************
2092     //****************************************************************
2093         private void trayToggleVisible() {
2094         if (isVisible()) {
2095                 hide();
2096         } else {
2097                 show();
2098                 raise();
2099         }
2100     }
2101     @SuppressWarnings("unused")
2102         private void trayActivated(QSystemTrayIcon.ActivationReason reason) {
2103         if (reason == QSystemTrayIcon.ActivationReason.DoubleClick) {
2104                 String name = QSystemTrayIcon.MessageIcon.resolve(reason.value()).name();
2105                 trayToggleVisible();
2106         }
2107     }
2108     
2109     
2110     //***************************************************************
2111     //***************************************************************
2112     //** These functions deal with the trash tree
2113     //***************************************************************
2114     //***************************************************************    
2115     // Setup the tree containing the trash.
2116     @SuppressWarnings("unused")
2117         private void trashTreeSelection() {     
2118         logger.log(logger.HIGH, "Entering NeverNote.trashTreeSelection");
2119         
2120         clearNotebookFilter();
2121         clearTagFilter();
2122         clearAttributeFilter();
2123         clearSavedSearchFilter();
2124         
2125         String tempGuid = currentNoteGuid;
2126         
2127 //      currentNoteGuid = "";
2128         currentNote = new Note();
2129         selectedNoteGUIDs.clear();
2130         listManager.getSelectedNotebooks().clear();
2131         listManager.getSelectedTags().clear();
2132         listManager.setSelectedSavedSearch("");
2133         browserWindow.clear();
2134     
2135         // toggle the add buttons
2136         newButton.setEnabled(!newButton.isEnabled());
2137         menuBar.noteAdd.setEnabled(newButton.isEnabled());
2138         menuBar.noteAdd.setVisible(true);
2139         
2140         List<QTreeWidgetItem> selections = trashTree.selectedItems();
2141         if (selections.size() == 0) {
2142                 currentNoteGuid = trashNoteGuid;
2143                         trashNoteGuid = tempGuid;
2144                 Global.showDeleted = false;
2145                 menuBar.noteRestoreAction.setEnabled(false);
2146                 menuBar.noteRestoreAction.setVisible(false);
2147         }
2148         else {
2149                 currentNoteGuid = trashNoteGuid;
2150                         trashNoteGuid = tempGuid;
2151                 menuBar.noteRestoreAction.setEnabled(true);
2152                 menuBar.noteRestoreAction.setVisible(true);
2153                 Global.showDeleted = true;
2154         }
2155         listManager.loadNotesIndex();
2156         noteIndexUpdated(false);
2157 ////            browserWindow.setEnabled(newButton.isEnabled());
2158         browserWindow.setReadOnly(!newButton.isEnabled());
2159         logger.log(logger.HIGH, "Leaving NeverNote.trashTreeSelection");
2160     }
2161     // Empty the trash file
2162     @SuppressWarnings("unused")
2163         private void emptyTrash() {
2164 //      browserWindow.clear();
2165         listManager.emptyTrash();
2166         if (trashTree.selectedItems().size() > 0) {
2167                 listManager.getSelectedNotebooks().clear();
2168                 listManager.getSelectedTags().clear();
2169                 listManager.setSelectedSavedSearch("");
2170                 newButton.setEnabled(!newButton.isEnabled());
2171                 menuBar.noteAdd.setEnabled(newButton.isEnabled());
2172                 menuBar.noteAdd.setVisible(true);
2173                 browserWindow.clear();
2174                 
2175                 clearTagFilter();
2176                 clearNotebookFilter();
2177                 clearSavedSearchFilter();
2178                 clearAttributeFilter();
2179                         
2180                 Global.showDeleted = false;
2181                 menuBar.noteRestoreAction.setEnabled(false);
2182                 menuBar.noteRestoreAction.setVisible(false);
2183                 
2184                 listManager.loadNotesIndex();
2185 //--->>>                noteIndexUpdated(true);
2186                 noteIndexUpdated(false);
2187         }       
2188    }
2189     // Show/Hide trash window
2190         private void toggleTrashWindow() {
2191                 logger.log(logger.HIGH, "Entering NeverNote.toggleTrashWindow");
2192         if (trashTree.isVisible())
2193                 trashTree.hide();
2194         else
2195                 trashTree.show();
2196         menuBar.hideTrash.setChecked(trashTree.isVisible());
2197         
2198                 Global.saveWindowVisible("trashTree", trashTree.isVisible());
2199         logger.log(logger.HIGH, "Leaving NeverNote.trashWindow");
2200     }    
2201         private void clearTrashFilter() {
2202                 Global.showDeleted = false;
2203         newButton.setEnabled(true);
2204         menuBar.noteAdd.setEnabled(true);
2205         menuBar.noteAdd.setVisible(true);
2206                 trashTree.blockSignals(true);
2207                 trashTree.clearSelection();
2208                 trashTree.blockSignals(false);
2209                 
2210         }
2211     
2212    
2213     //***************************************************************
2214     //***************************************************************
2215     //** These functions deal with connection settings
2216     //***************************************************************
2217     //***************************************************************
2218         // SyncRunner had a problem and things are disconnected
2219         @SuppressWarnings("unused")
2220         private void remoteErrorDisconnect() {
2221                 menuBar.connectAction.setText("Connect");
2222                 menuBar.connectAction.setToolTip("Connect to Evernote");
2223                 menuBar.synchronizeAction.setEnabled(false);
2224                 synchronizeAnimationTimer.stop();
2225                 return;
2226         }
2227         // Do a manual connect/disconnect
2228     private void remoteConnect() {
2229         logger.log(logger.HIGH, "Entering NeverNote.remoteConnect");
2230
2231         if (Global.isConnected) {
2232                 Global.isConnected = false;
2233                 syncRunner.enDisconnect();
2234                 setupConnectMenuOptions();
2235                 setupOnlineMenu();
2236                 return;
2237         }
2238         
2239         AESEncrypter aes = new AESEncrypter();
2240         try {
2241                         aes.decrypt(new FileInputStream(Global.getDirectoryPath()+"secure.txt"));
2242                 } catch (FileNotFoundException e) {
2243                         // File not found, so we'll just get empty strings anyway. 
2244                 }
2245                 String userid = aes.getUserid();
2246                 String password = aes.getPassword();
2247                 if (!userid.equals("") && !password.equals("")) {
2248                 Global.username = userid;
2249                 Global.password = password;
2250                 }               
2251
2252         // Show the login dialog box
2253                 if (!Global.automaticLogin() || userid.equals("")|| password.equals("")) {
2254                         LoginDialog login = new LoginDialog();
2255                         login.exec();
2256                 
2257                         if (!login.okPressed()) {
2258                                 return;
2259                         }
2260         
2261                         Global.username = login.getUserid();
2262                         Global.password = login.getPassword();
2263                 }
2264                 syncRunner.username = Global.username;
2265                 syncRunner.password = Global.password;
2266                 syncRunner.userStoreUrl = Global.userStoreUrl;
2267                 syncRunner.noteStoreUrl = Global.noteStoreUrl;
2268                 syncRunner.noteStoreUrlBase = Global.noteStoreUrlBase;
2269                 syncRunner.enConnect();
2270                 Global.isConnected = syncRunner.isConnected;
2271                 setupOnlineMenu();
2272                 setupConnectMenuOptions();
2273                 logger.log(logger.HIGH, "Leaving NeverNote.remoteConnect");
2274     }
2275     private void setupConnectMenuOptions() {
2276         logger.log(logger.HIGH, "entering NeverNote.setupConnectMenuOptions");
2277                 if (!Global.isConnected) {
2278                         menuBar.connectAction.setText("Connect");
2279                         menuBar.connectAction.setToolTip("Connect to Evernote");
2280                         menuBar.synchronizeAction.setEnabled(false);
2281                 } else {
2282                         menuBar.connectAction.setText("Disconnect");
2283                         menuBar.connectAction.setToolTip("Disconnect from Evernote");
2284                         menuBar.synchronizeAction.setEnabled(true);
2285                 }
2286                 logger.log(logger.HIGH, "Leaving NeverNote.setupConnectionMenuOptions");
2287     }
2288     
2289     
2290     
2291     //***************************************************************
2292     //***************************************************************
2293     //** These functions deal with the GUI Attribute tree
2294     //***************************************************************
2295     //***************************************************************    
2296     @SuppressWarnings("unused")
2297         private void attributeTreeClicked(QTreeWidgetItem item, Integer integer) {
2298         
2299         clearTagFilter();
2300         clearNotebookFilter();
2301         clearTrashFilter();
2302         clearSavedSearchFilter();
2303
2304         if (attributeTreeSelected == null || item.nativeId() != attributeTreeSelected.nativeId()) {
2305                 if (item.childCount() > 0) {
2306                         item.setSelected(false);
2307                 } else {
2308                 Global.createdBeforeFilter.reset();
2309                 Global.createdSinceFilter.reset();
2310                 Global.changedBeforeFilter.reset();
2311                 Global.changedSinceFilter.reset();
2312                 Global.containsFilter.reset();
2313                         attributeTreeSelected = item;
2314                         DateAttributeFilterTable f = null;
2315                         f = findDateAttributeFilterTable(item.parent());
2316                         if (f!=null)
2317                                 f.select(item.text(0));
2318                         else {
2319                                 String text = item.text(0);
2320                                 Global.containsFilter.select(text);
2321                         }
2322                 }
2323                 listManager.loadNotesIndex();
2324                 noteIndexUpdated(false);
2325                 return;
2326         }
2327                 attributeTreeSelected = null;
2328                 item.setSelected(false);
2329         Global.createdBeforeFilter.reset();
2330         Global.createdSinceFilter.reset();
2331         Global.changedBeforeFilter.reset();
2332         Global.changedSinceFilter.reset();
2333         Global.containsFilter.reset();
2334         listManager.loadNotesIndex();
2335                 noteIndexUpdated(false); 
2336     }
2337     // This determines what attribute filter we need, depending upon the selection
2338     private DateAttributeFilterTable findDateAttributeFilterTable(QTreeWidgetItem w) {
2339                 if (w.parent() != null && w.childCount() > 0) {
2340                         QTreeWidgetItem parent = w.parent();
2341                         if (parent.text(0).equalsIgnoreCase("created") && 
2342                                 w.text(0).equalsIgnoreCase("since"))
2343                                         return Global.createdSinceFilter;
2344                         if (parent.text(0).equalsIgnoreCase("created") && 
2345                         w.text(0).equalsIgnoreCase("before"))
2346                                         return Global.createdBeforeFilter;
2347                         if (parent.text(0).equalsIgnoreCase("last modified") && 
2348                         w.text(0).equalsIgnoreCase("since"))
2349                                         return Global.changedSinceFilter;
2350                 if (parent.text(0).equalsIgnoreCase("last modified") && 
2351                         w.text(0).equalsIgnoreCase("before"))
2352                                                 return Global.changedBeforeFilter;
2353                 }
2354                 return null;
2355     }
2356     // Show/Hide attribute search window
2357         private void toggleAttributesWindow() {
2358                 logger.log(logger.HIGH, "Entering NeverNote.toggleAttributesWindow");
2359         if (attributeTree.isVisible())
2360                 attributeTree.hide();
2361         else
2362                 attributeTree.show();
2363         menuBar.hideAttributes.setChecked(attributeTree.isVisible());
2364         
2365                 Global.saveWindowVisible("attributeTree", attributeTree.isVisible());
2366         logger.log(logger.HIGH, "Leaving NeverNote.toggleAttributeWindow");
2367     }    
2368         private void clearAttributeFilter() {
2369         Global.createdBeforeFilter.reset();
2370         Global.createdSinceFilter.reset();
2371         Global.changedBeforeFilter.reset();
2372         Global.changedSinceFilter.reset();
2373         Global.containsFilter.reset();
2374         attributeTreeSelected = null;
2375                 attributeTree.blockSignals(true);
2376                 attributeTree.clearSelection();
2377                 attributeTree.blockSignals(false);
2378         }
2379     
2380         
2381     //***************************************************************
2382     //***************************************************************
2383     //** These functions deal with the GUI Note index table
2384     //***************************************************************
2385     //***************************************************************    
2386     // Initialize the note list table
2387         private void initializeNoteTable() {
2388                 logger.log(logger.HIGH, "Entering NeverNote.initializeNoteTable");
2389                 noteTableView.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection);
2390                 noteTableView.selectionModel().selectionChanged.connect(this, "noteTableSelection()");
2391                 logger.log(logger.HIGH, "Leaving NeverNote.initializeNoteTable");
2392         }       
2393     // Show/Hide trash window
2394         @SuppressWarnings("unused")
2395         private void toggleNoteListWindow() {
2396                 logger.log(logger.HIGH, "Entering NeverNote.toggleNoteListWindow");
2397         if (noteTableView.isVisible())
2398                 noteTableView.hide();
2399         else
2400                 noteTableView.show();
2401         menuBar.hideNoteList.setChecked(noteTableView.isVisible());
2402         
2403                 Global.saveWindowVisible("noteList", noteTableView.isVisible());
2404         logger.log(logger.HIGH, "Leaving NeverNote.toggleNoteListWindow");
2405     }   
2406         // Handle the event that a user selects a note from the table
2407     @SuppressWarnings("unused")
2408         private void noteTableSelection() {
2409                 logger.log(logger.HIGH, "Entering NeverNote.noteTableSelection");
2410                 saveNote();
2411                 if (historyGuids.size() == 0) {
2412                         historyGuids.add(currentNoteGuid);
2413                         historyPosition = 1;
2414                 }
2415         noteTableView.showColumn(Global.noteTableGuidPosition);
2416         
2417         List<QModelIndex> selections = noteTableView.selectionModel().selectedRows();
2418         noteTableView.hideColumn(Global.noteTableGuidPosition);
2419         
2420         if (selections.size() > 0) {
2421                 QModelIndex index;
2422                 menuBar.noteDuplicateAction.setEnabled(true);
2423                 menuBar.noteOnlineHistoryAction.setEnabled(true);
2424                 menuBar.noteMergeAction.setEnabled(true);
2425                 selectedNoteGUIDs.clear();
2426                 if (selections.size() != 1 || Global.showDeleted) {
2427                         menuBar.noteDuplicateAction.setEnabled(false);
2428                 }
2429                 if (selections.size() != 1 || !Global.isConnected) {
2430                         menuBar.noteOnlineHistoryAction.setEnabled(false);
2431                 }
2432                 if (selections.size() == 1) {
2433                         menuBar.noteMergeAction.setEnabled(false);
2434                 }
2435                 for (int i=0; i<selections.size(); i++) {
2436                         int row = selections.get(i).row();
2437                         if (row == 0) 
2438                                 upButton.setEnabled(false);
2439                         else
2440                                 upButton.setEnabled(true);
2441                         if (row < noteTableView.model.rowCount()-1)
2442                                 downButton.setEnabled(true);
2443                         else
2444                                 downButton.setEnabled(false);
2445                         index = noteTableView.proxyModel.index(row, Global.noteTableGuidPosition);
2446                         SortedMap<Integer, Object> ix = noteTableView.proxyModel.itemData(index);
2447                         currentNoteGuid = (String)ix.values().toArray()[0];
2448                         selectedNoteGUIDs.add(currentNoteGuid);
2449                 }
2450         }
2451         
2452         nextButton.setEnabled(true);
2453                 prevButton.setEnabled(true);
2454         if (!fromHistory) {
2455                 int endPosition = historyGuids.size()-1;
2456                 for (int j=historyPosition; j<=endPosition; j++) {
2457                         historyGuids.remove(historyGuids.size()-1);
2458                 }
2459                 historyGuids.add(currentNoteGuid);
2460                 historyPosition = historyGuids.size();
2461         } 
2462         if (historyPosition <= 1)
2463                 prevButton.setEnabled(false);
2464         if (historyPosition == historyGuids.size())
2465                 nextButton.setEnabled(false);
2466                 
2467         fromHistory = false;
2468         scrollToGuid(currentNoteGuid);
2469         refreshEvernoteNote(true);
2470                 logger.log(logger.HIGH, "Leaving NeverNote.noteTableSelection");
2471     }    
2472         // Trigger a refresh when the note db has been updated
2473         private void noteIndexUpdated(boolean reload) {
2474                 logger.log(logger.HIGH, "Entering NeverNote.noteIndexUpdated");
2475                 Global.traceReset();  
2476                 saveNote();
2477         refreshEvernoteNoteList();
2478         logger.log(logger.HIGH, "Calling note table reload in NeverNote.noteIndexUpdated() - "+reload);
2479         noteTableView.load(listManager, reload);
2480         scrollToGuid(currentNoteGuid);
2481                 logger.log(logger.HIGH, "Leaving NeverNote.noteIndexUpdated");
2482     }
2483         // Called when the list of notes is updated
2484     private void refreshEvernoteNoteList() {
2485         logger.log(logger.HIGH, "Entering NeverNote.refreshEvernoteNoteList");
2486         browserWindow.setDisabled(false);
2487                 if (selectedNoteGUIDs == null)
2488                         selectedNoteGUIDs = new ArrayList<String>();
2489                 selectedNoteGUIDs.clear();  // clear out old entries
2490                 
2491                 String saveCurrentNoteGuid = new String();
2492                 String tempNoteGuid = new String();
2493                                 
2494                 historyGuids.clear();
2495                 historyPosition = 0;
2496                 prevButton.setEnabled(false);
2497                 nextButton.setEnabled(false);
2498                 
2499                 if (currentNoteGuid == null) 
2500                         currentNoteGuid = new String();
2501                 
2502                 for (Note note : listManager.getNoteIndex()) {
2503                         tempNoteGuid = note.getGuid();
2504                         if (currentNoteGuid.equals(tempNoteGuid)) {
2505                                 saveCurrentNoteGuid = new String(tempNoteGuid);
2506                         }
2507                 }
2508                 
2509                 if (listManager.getNoteIndex().size() == 0) {
2510                         currentNoteGuid = "";
2511                         currentNote = null;
2512                         browserWindow.clear();
2513                         browserWindow.setDisabled(true);
2514                 } 
2515                 
2516                 if (saveCurrentNoteGuid.equals("") && listManager.getNoteIndex().size() >0) {
2517                         currentNoteGuid = listManager.getNoteIndex().get(listManager.getNoteIndex().size()-1).getGuid();
2518                         currentNote = listManager.getNoteIndex().get(listManager.getNoteIndex().size()-1);
2519                         refreshEvernoteNote(true);
2520                 } else {
2521                         refreshEvernoteNote(false);
2522                 }
2523                 reloadTagTree();
2524
2525                 logger.log(logger.HIGH, "Leaving NeverNote.refreshEvernoteNoteList");
2526         } 
2527     // Called when the previous arrow button is clicked 
2528     @SuppressWarnings("unused")
2529         private void previousViewedAction() {
2530         if (!prevButton.isEnabled())
2531                 return;
2532         if (historyPosition == 0)
2533                 return;
2534                 historyPosition--;
2535         if (historyPosition <= 0)
2536                 return;
2537         String historyGuid = historyGuids.get(historyPosition-1);
2538         fromHistory = true;
2539         for (int i=0; i<noteTableView.model().rowCount(); i++) {
2540                 QModelIndex modelIndex =  noteTableView.model().index(i, Global.noteTableGuidPosition);
2541                 if (modelIndex != null) {
2542                         SortedMap<Integer, Object> ix = noteTableView.model().itemData(modelIndex);
2543                         String tableGuid =  (String)ix.values().toArray()[0];
2544                         if (tableGuid.equals(historyGuid)) {
2545                                 noteTableView.selectRow(i);
2546                                 return;
2547                         }       
2548                 }
2549         }
2550     }
2551     @SuppressWarnings("unused")
2552         private void nextViewedAction() {
2553         if (!nextButton.isEnabled())
2554                 return;
2555         String historyGuid = historyGuids.get(historyPosition);
2556         historyPosition++;
2557         fromHistory = true;
2558         for (int i=0; i<noteTableView.model().rowCount(); i++) {
2559                 QModelIndex modelIndex =  noteTableView.model().index(i, Global.noteTableGuidPosition);
2560                 if (modelIndex != null) {
2561                         SortedMap<Integer, Object> ix = noteTableView.model().itemData(modelIndex);
2562                         String tableGuid =  (String)ix.values().toArray()[0];
2563                         if (tableGuid.equals(historyGuid)) {
2564                                 noteTableView.selectRow(i);
2565                                 return;
2566                         }       
2567                 }
2568         }       
2569     }
2570     // Called when the up arrow is clicked 
2571     @SuppressWarnings("unused")
2572         private void upAction() {
2573         List<QModelIndex> selections = noteTableView.selectionModel().selectedRows();
2574         int row = selections.get(0).row();
2575         if (row > 0) {
2576                 noteTableView.selectRow(row-1);
2577         }
2578     }
2579     // Called when the down arrow is clicked 
2580     @SuppressWarnings("unused")
2581         private void downAction() {
2582         List<QModelIndex> selections = noteTableView.selectionModel().selectedRows();
2583         int row = selections.get(0).row();
2584         int max = noteTableView.model.rowCount();
2585         if (row < max-1) {
2586                 noteTableView.selectRow(row+1);
2587         }
2588     }
2589     // Update a tag string for a specific note in the list
2590     @SuppressWarnings("unused")
2591         private void updateListTags(String guid, List<String> tags) {
2592         logger.log(logger.HIGH, "Entering NeverNote.updateListTags");
2593         StringBuffer tagBuffer = new StringBuffer();
2594         for (int i=0; i<tags.size(); i++) {
2595                 tagBuffer.append(tags.get(i));
2596                 if (i<tags.size()-1)
2597                         tagBuffer.append(", ");
2598         }
2599         
2600         for (int i=0; i<noteTableView.model.rowCount(); i++) {
2601                 QModelIndex modelIndex =  noteTableView.model.index(i, Global.noteTableGuidPosition);
2602                 if (modelIndex != null) {
2603                         SortedMap<Integer, Object> ix = noteTableView.model.itemData(modelIndex);
2604                         String tableGuid =  (String)ix.values().toArray()[0];
2605                         if (tableGuid.equals(guid)) {
2606                                 noteTableView.model.setData(i, Global.noteTableTagPosition,tagBuffer.toString());
2607                                 noteTableView.model.setData(i, Global.noteTableSynchronizedPosition, "false");
2608                                 return;
2609                         }
2610                 }
2611         }
2612         logger.log(logger.HIGH, "Leaving NeverNote.updateListTags");
2613     }
2614     // Update a title for a specific note in the list
2615     @SuppressWarnings("unused")
2616         private void updateListTitle(String guid, String title) {
2617         logger.log(logger.HIGH, "Entering NeverNote.updateListTitle");
2618
2619         for (int i=0; i<noteTableView.model.rowCount(); i++) {
2620                 //QModelIndex modelIndex =  noteTableView.proxyModel.index(i, Global.noteTableGuidPosition);
2621                 QModelIndex modelIndex =  noteTableView.model.index(i, Global.noteTableGuidPosition);
2622                 if (modelIndex != null) {
2623 //                      SortedMap<Integer, Object> ix = noteTableView.proxyModel.itemData(modelIndex);
2624                         SortedMap<Integer, Object> ix = noteTableView.model.itemData(modelIndex);
2625                         String tableGuid =  (String)ix.values().toArray()[0];
2626                         if (tableGuid.equals(guid)) {
2627                                 noteTableView.model.setData(i, Global.noteTableTitlePosition,title);
2628                                 noteTableView.model.setData(i, Global.noteTableSynchronizedPosition, "false");
2629                                 return;
2630                         }       
2631                 }
2632         }
2633         logger.log(logger.HIGH, "Leaving NeverNote.updateListTitle");
2634     }
2635     // Update a title for a specific note in the list
2636     @SuppressWarnings("unused")
2637         private void updateListAuthor(String guid, String author) {
2638         logger.log(logger.HIGH, "Entering NeverNote.updateListAuthor");
2639
2640         for (int i=0; i<noteTableView.model.rowCount(); i++) {
2641                 //QModelIndex modelIndex =  noteTableView.proxyModel.index(i, Global.noteTableGuidPosition);
2642                 QModelIndex modelIndex =  noteTableView.model.index(i, Global.noteTableGuidPosition);
2643                 if (modelIndex != null) {
2644 //                      SortedMap<Integer, Object> ix = noteTableView.proxyModel.itemData(modelIndex);
2645                         SortedMap<Integer, Object> ix = noteTableView.model.itemData(modelIndex);
2646                         String tableGuid =  (String)ix.values().toArray()[0];
2647                         if (tableGuid.equals(guid)) {
2648                                 noteTableView.model.setData(i, Global.noteTableAuthorPosition,author);
2649                                 noteTableView.model.setData(i, Global.noteTableSynchronizedPosition, "false");
2650                                 return;
2651                         }       
2652                 }
2653         }
2654         logger.log(logger.HIGH, "Leaving NeverNote.updateListAuthor");
2655     }
2656         private void updateListNoteNotebook(String guid, String notebook) {
2657         logger.log(logger.HIGH, "Entering NeverNote.updateListAuthor");
2658
2659         for (int i=0; i<noteTableView.model.rowCount(); i++) {
2660                 //QModelIndex modelIndex =  noteTableView.proxyModel.index(i, Global.noteTableGuidPosition);
2661                 QModelIndex modelIndex =  noteTableView.model.index(i, Global.noteTableGuidPosition);
2662                 if (modelIndex != null) {
2663 //                      SortedMap<Integer, Object> ix = noteTableView.proxyModel.itemData(modelIndex);
2664                         SortedMap<Integer, Object> ix = noteTableView.model.itemData(modelIndex);
2665                         String tableGuid =  (String)ix.values().toArray()[0];
2666                         if (tableGuid.equals(guid)) {
2667                                 noteTableView.model.setData(i, Global.noteTableNotebookPosition,notebook);
2668                                 noteTableView.model.setData(i, Global.noteTableSynchronizedPosition, "false");
2669                                 return;
2670                         }       
2671                 }
2672         }
2673         logger.log(logger.HIGH, "Leaving NeverNote.updateListAuthor");
2674     }
2675     // Update a title for a specific note in the list
2676     @SuppressWarnings("unused")
2677         private void updateListSourceUrl(String guid, String url) {
2678         logger.log(logger.HIGH, "Entering NeverNote.updateListAuthor");
2679
2680         for (int i=0; i<noteTableView.model.rowCount(); i++) {
2681                 //QModelIndex modelIndex =  noteTableView.proxyModel.index(i, Global.noteTableGuidPosition);
2682                 QModelIndex modelIndex =  noteTableView.model.index(i, Global.noteTableGuidPosition);
2683                 if (modelIndex != null) {
2684 //                      SortedMap<Integer, Object> ix = noteTableView.proxyModel.itemData(modelIndex);
2685                         SortedMap<Integer, Object> ix = noteTableView.model.itemData(modelIndex);
2686                         String tableGuid =  (String)ix.values().toArray()[0];
2687                         if (tableGuid.equals(guid)) {
2688                                 noteTableView.model.setData(i, Global.noteTableSynchronizedPosition, "false");
2689                                 noteTableView.model.setData(i, Global.noteTableSourceUrlPosition,url);
2690                                 return;
2691                         }       
2692                 }
2693         }
2694         logger.log(logger.HIGH, "Leaving NeverNote.updateListAuthor");
2695     }
2696         private void updateListGuid(String oldGuid, String newGuid) {
2697         logger.log(logger.HIGH, "Entering NeverNote.updateListTitle");
2698
2699         for (int i=0; i<noteTableView.model.rowCount(); i++) {
2700                 QModelIndex modelIndex =  noteTableView.model.index(i, Global.noteTableGuidPosition);
2701                 if (modelIndex != null) {
2702                         SortedMap<Integer, Object> ix = noteTableView.model.itemData(modelIndex);
2703                         String tableGuid =  (String)ix.values().toArray()[0];
2704                         if (tableGuid.equals(oldGuid)) {
2705                                 noteTableView.model.setData(i, Global.noteTableGuidPosition,newGuid);
2706                                 //noteTableView.model.setData(i, Global.noteTableSynchronizedPosition, "false");
2707                                 return;
2708                         }       
2709                 }
2710         }
2711         logger.log(logger.HIGH, "Leaving NeverNote.updateListTitle");
2712     }
2713         private void updateListTagName(String guid) {
2714         logger.log(logger.HIGH, "Entering NeverNote.updateTagName");
2715                 
2716                 for (int j=0; j<listManager.getNoteIndex().size(); j++) {
2717                         if (listManager.getNoteIndex().get(j).getTagGuids().contains(guid)) {
2718                                 String newName = listManager.getTagNamesForNote(listManager.getNoteIndex().get(j));
2719
2720                                 for (int i=0; i<noteTableView.model.rowCount(); i++) {
2721                                         QModelIndex modelIndex =  noteTableView.model.index(i, Global.noteTableGuidPosition);
2722                                         if (modelIndex != null) {
2723                                                 SortedMap<Integer, Object> ix = noteTableView.model.itemData(modelIndex);
2724                                                 String noteGuid = (String)ix.values().toArray()[0];
2725                                                 if (noteGuid.equalsIgnoreCase(listManager.getNoteIndex().get(j).getGuid())) {
2726                                                         noteTableView.model.setData(i, Global.noteTableTagPosition, newName);
2727                                                         //noteTableView.model.setData(i, Global.noteTableSynchronizedPosition, "false");
2728                                                         i=noteTableView.model.rowCount();
2729                                                 }
2730                                         }
2731                                 }
2732                         }
2733                 }       
2734         logger.log(logger.HIGH, "Leaving NeverNote.updateListNotebook");
2735     }
2736         private void removeListTagName(String guid) {
2737         logger.log(logger.HIGH, "Entering NeverNote.updateTagName");
2738                 
2739                 for (int j=0; j<listManager.getNoteIndex().size(); j++) {
2740                         if (listManager.getNoteIndex().get(j).getTagGuids().contains(guid)) {
2741                                 for (int i=listManager.getNoteIndex().get(j).getTagGuids().size()-1; i>=0; i--) {
2742                                         if (listManager.getNoteIndex().get(j).getTagGuids().get(i).equals(guid))
2743                                                 listManager.getNoteIndex().get(j).getTagGuids().remove(i);
2744                                 }
2745                                 
2746                                 String newName = listManager.getTagNamesForNote(listManager.getNoteIndex().get(j));
2747                                 for (int i=0; i<noteTableView.model.rowCount(); i++) {
2748                                         QModelIndex modelIndex =  noteTableView.model.index(i, Global.noteTableGuidPosition);
2749                                         if (modelIndex != null) {
2750                                                 SortedMap<Integer, Object> ix = noteTableView.model.itemData(modelIndex);
2751                                                 String noteGuid = (String)ix.values().toArray()[0];
2752                                                 if (noteGuid.equalsIgnoreCase(listManager.getNoteIndex().get(j).getGuid())) {
2753                                                         noteTableView.model.setData(i, Global.noteTableTagPosition, newName);
2754 //                                                      noteTableView.model.setData(i, Global.noteTableSynchronizedPosition, "false");
2755                                                         i=noteTableView.model.rowCount();
2756                                                 }
2757                                         }
2758                                 }
2759                         }
2760                 }       
2761         logger.log(logger.HIGH, "Leaving NeverNote.updateListNotebook");
2762     }
2763     private void updateListNotebookName(String oldName, String newName) {
2764         logger.log(logger.HIGH, "Entering NeverNote.updateListNotebookName");
2765
2766         for (int i=0; i<noteTableView.model.rowCount(); i++) {
2767                 QModelIndex modelIndex =  noteTableView.model.index(i, Global.noteTableNotebookPosition); 
2768                 if (modelIndex != null) {
2769                         SortedMap<Integer, Object> ix = noteTableView.model.itemData(modelIndex);
2770                         String tableName =  (String)ix.values().toArray()[0];
2771                         if (tableName.equalsIgnoreCase(oldName)) {
2772 //                              noteTableView.model.setData(i, Global.noteTableSynchronizedPosition, "false");
2773                                 noteTableView.model.setData(i, Global.noteTableNotebookPosition, newName);
2774                         }
2775                 }
2776         }
2777         logger.log(logger.HIGH, "Leaving NeverNote.updateListNotebookName");
2778     }
2779     @SuppressWarnings("unused")
2780         private void updateListDateCreated(String guid, QDateTime date) {
2781         logger.log(logger.HIGH, "Entering NeverNote.updateListDateCreated");
2782
2783         for (int i=0; i<noteTableView.model.rowCount(); i++) {
2784                 QModelIndex modelIndex =  noteTableView.model.index(i, Global.noteTableGuidPosition);
2785                 if (modelIndex != null) {
2786                         SortedMap<Integer, Object> ix = noteTableView.model.itemData(modelIndex);
2787                         String tableGuid =  (String)ix.values().toArray()[0];
2788                         if (tableGuid.equals(guid)) {
2789                                 noteTableView.model.setData(i, Global.noteTableCreationPosition, date.toString(Global.getDateFormat()+" " +Global.getTimeFormat()));
2790                                 return;
2791                         }
2792                 }
2793         }
2794         logger.log(logger.HIGH, "Leaving NeverNote.updateListDateCreated");
2795     }
2796     @SuppressWarnings("unused")
2797         private void updateListDateSubject(String guid, QDateTime date) {
2798         logger.log(logger.HIGH, "Entering NeverNote.updateListDateSubject");
2799
2800         for (int i=0; i<noteTableView.model.rowCount(); i++) {
2801                 QModelIndex modelIndex =  noteTableView.model.index(i, Global.noteTableGuidPosition);
2802                 if (modelIndex != null) {
2803                         SortedMap<Integer, Object> ix = noteTableView.model.itemData(modelIndex);
2804                         String tableGuid =  (String)ix.values().toArray()[0];
2805                         if (tableGuid.equals(guid)) {
2806                                 noteTableView.model.setData(i, Global.noteTableSynchronizedPosition, "false");
2807                                 noteTableView.model.setData(i, Global.noteTableSubjectDatePosition, date.toString(Global.getDateFormat()+" " +Global.getTimeFormat()));
2808                                 return;
2809                         }
2810                 }
2811         }
2812         logger.log(logger.HIGH, "Leaving NeverNote.updateListDateCreated");
2813     }
2814     @SuppressWarnings("unused")
2815         private void updateListDateChanged(String guid, QDateTime date) {
2816         logger.log(logger.HIGH, "Entering NeverNote.updateListDateChanged");
2817
2818         for (int i=0; i<noteTableView.model.rowCount(); i++) {
2819                 QModelIndex modelIndex =  noteTableView.model.index(i, Global.noteTableGuidPosition);
2820                 if (modelIndex != null) {
2821                         SortedMap<Integer, Object> ix = noteTableView.model.itemData(modelIndex);
2822                         String tableGuid =  (String)ix.values().toArray()[0];
2823                         if (tableGuid.equals(guid)) {
2824                                 noteTableView.model.setData(i, Global.noteTableSynchronizedPosition, "false");
2825                                 noteTableView.model.setData(i, Global.noteTableChangedPosition, date.toString(Global.getDateFormat()+" " +Global.getTimeFormat()));
2826                                 return;
2827                         }
2828                 }
2829         }
2830         logger.log(logger.HIGH, "Leaving NeverNote.updateListDateChanged");
2831     }
2832     private void updateListDateChanged() {
2833         logger.log(logger.HIGH, "Entering NeverNote.updateListDateChanged");
2834         QDateTime date = new QDateTime(QDateTime.currentDateTime());
2835         for (int i=0; i<noteTableView.model.rowCount(); i++) {
2836                 QModelIndex modelIndex =  noteTableView.model.index(i, Global.noteTableGuidPosition);
2837                 if (modelIndex != null) {
2838                         SortedMap<Integer, Object> ix = noteTableView.model.itemData(modelIndex);
2839                         String tableGuid =  (String)ix.values().toArray()[0];
2840                         if (tableGuid.equals(currentNoteGuid)) {
2841                                 noteTableView.model.setData(i, Global.noteTableSynchronizedPosition, "false");
2842                                 noteTableView.model.setData(i, Global.noteTableChangedPosition, date.toString(Global.getDateFormat()+" " +Global.getTimeFormat()));
2843                                 return;
2844                         }
2845                 }
2846         }
2847         logger.log(logger.HIGH, "Leaving NeverNote.updateListDateChanged");
2848     }  
2849     // Redo scroll
2850     @SuppressWarnings("unused")
2851         private void scrollToCurrentGuid() {
2852         //scrollToGuid(currentNoteGuid);
2853         List<QModelIndex> selections = noteTableView.selectionModel().selectedRows();
2854         if (selections.size() == 0)
2855                 return;
2856         QModelIndex index = selections.get(0);
2857         int row = selections.get(0).row();
2858         String guid = (String)index.model().index(row, Global.noteTableGuidPosition).data();
2859         scrollToGuid(guid);
2860     }
2861     // Scroll to a particular index item
2862     private void scrollToGuid(String guid) {
2863         if (currentNote == null || guid == null) 
2864                 return;
2865         if (currentNote.isActive() && Global.showDeleted) {
2866                 for (int i=0; i<listManager.getNoteIndex().size(); i++) {
2867                         if (!listManager.getNoteIndex().get(i).isActive()) {
2868                                 currentNote = listManager.getNoteIndex().get(i);
2869                                 currentNoteGuid =  currentNote.getGuid();
2870                                 i = listManager.getNoteIndex().size();
2871                         }
2872                 }
2873         }
2874         
2875         if (!currentNote.isActive() && !Global.showDeleted) {
2876                 for (int i=0; i<listManager.getNoteIndex().size(); i++) {
2877                         if (listManager.getNoteIndex().get(i).isActive()) {
2878                                 currentNote = listManager.getNoteIndex().get(i);
2879                                 currentNoteGuid =  currentNote.getGuid();
2880                                 i = listManager.getNoteIndex().size();
2881                         }
2882                 }
2883         }
2884         
2885         QModelIndex index; 
2886         for (int i=0; i<noteTableView.model().rowCount(); i++) {
2887                 index = noteTableView.model().index(i, Global.noteTableGuidPosition);
2888                 if (currentNoteGuid.equals(index.data())) {
2889 //                      noteTableView.setCurrentIndex(index);
2890                         noteTableView.selectRow(i);
2891                         noteTableView.scrollTo(index, ScrollHint.EnsureVisible);  // This should work, but it doesn't
2892                                 i=noteTableView.model.rowCount();
2893                 }
2894         }
2895     }
2896     // Show/Hide columns
2897     private void showColumns() {
2898                 noteTableView.setColumnHidden(Global.noteTableCreationPosition, !Global.isColumnVisible("dateCreated"));
2899                 noteTableView.setColumnHidden(Global.noteTableChangedPosition, !Global.isColumnVisible("dateChanged"));
2900                 noteTableView.setColumnHidden(Global.noteTableSubjectDatePosition, !Global.isColumnVisible("dateSubject"));
2901                 noteTableView.setColumnHidden(Global.noteTableAuthorPosition, !Global.isColumnVisible("author"));
2902                 noteTableView.setColumnHidden(Global.noteTableSourceUrlPosition, !Global.isColumnVisible("sourceUrl"));
2903                 noteTableView.setColumnHidden(Global.noteTableTagPosition, !Global.isColumnVisible("tags"));
2904                 noteTableView.setColumnHidden(Global.noteTableNotebookPosition, !Global.isColumnVisible("notebook"));
2905                 noteTableView.setColumnHidden(Global.noteTableSynchronizedPosition, !Global.isColumnVisible("synchronized"));
2906     }
2907     // Open a separate window
2908     @SuppressWarnings("unused")
2909         private void listDoubleClick() {
2910
2911     }
2912     // Title color has changed
2913     @SuppressWarnings("unused")
2914         private void titleColorChanged(Integer color) {
2915         logger.log(logger.HIGH, "Entering NeverNote.updateListAuthor");
2916
2917         QColor backgroundColor = new QColor();
2918                 QColor foregroundColor = new QColor(QColor.black);
2919                 backgroundColor.setRgb(color);
2920                 
2921                 if (backgroundColor.rgb() == QColor.black.rgb() || backgroundColor.rgb() == QColor.blue.rgb())
2922                         foregroundColor.setRgb(QColor.white.rgb());
2923         
2924                 if (selectedNoteGUIDs.size() == 0)
2925                         selectedNoteGUIDs.add(currentNoteGuid);
2926                 
2927         for (int j=0; j<selectedNoteGUIDs.size(); j++) {
2928                 for (int i=0; i<noteTableView.model.rowCount(); i++) {
2929                         QModelIndex modelIndex =  noteTableView.model.index(i, Global.noteTableGuidPosition);
2930                         if (modelIndex != null) {
2931                                 SortedMap<Integer, Object> ix = noteTableView.model.itemData(modelIndex);
2932                                 String tableGuid =  (String)ix.values().toArray()[0];
2933                                 if (tableGuid.equals(selectedNoteGUIDs.get(j))) {
2934                                         for (int k=0; k<Global.noteTableColumnCount; k++) {
2935                                                 noteTableView.model.setData(i, k, backgroundColor, Qt.ItemDataRole.BackgroundRole);
2936                                                 noteTableView.model.setData(i, k, foregroundColor, Qt.ItemDataRole.ForegroundRole);
2937                                                 listManager.updateNoteTitleColor(selectedNoteGUIDs.get(j), backgroundColor.rgb());
2938                                         }
2939                                         i=noteTableView.model.rowCount();
2940                                 }
2941                         }
2942                 }
2943         }
2944         logger.log(logger.HIGH, "Leaving NeverNote.updateListAuthor");
2945     }
2946     
2947     
2948     //***************************************************************
2949     //***************************************************************
2950     //** These functions deal with Note specific things
2951     //***************************************************************
2952     //***************************************************************    
2953     @SuppressWarnings("unused")
2954         private void setNoteDirty() {
2955                 logger.log(logger.EXTREME, "Entering NeverNote.setNoteDirty()");
2956         noteDirty = true;
2957
2958         listManager.getUnsynchronizedNotes().add(currentNoteGuid);
2959         for (int i=0; i<noteTableView.model.rowCount(); i++) {
2960                 QModelIndex modelIndex =  noteTableView.model.index(i, Global.noteTableGuidPosition);
2961                 if (modelIndex != null) {
2962                         SortedMap<Integer, Object> ix = noteTableView.model.itemData(modelIndex);
2963                         String tableGuid =  (String)ix.values().toArray()[0];
2964                         if (tableGuid.equals(currentNoteGuid)) {
2965                                 noteTableView.model.setData(i, Global.noteTableSynchronizedPosition, "false");
2966                                 return;
2967                         }
2968                 }
2969         }
2970                 logger.log(logger.EXTREME, "Leaving NeverNote.setNoteDirty()");
2971     }
2972     private void saveNote() {
2973                 logger.log(logger.EXTREME, "Inside NeverNote.saveNote()");
2974         if (noteDirty) {
2975                         logger.log(logger.EXTREME, "Note is dirty.");
2976                 waitCursor(true);
2977                 
2978                         preview = new Thumbnailer(currentNoteGuid, new QSize(1024,768));
2979                         preview.finished.connect(this, "saveThumbnail(String)");
2980                         preview.setContent(browserWindow.getContent());
2981                 
2982                         logger.log(logger.EXTREME, "Saving to cache");
2983                         QTextCodec codec = QTextCodec.codecForLocale();
2984 //              QTextDecoder decoder = codec.makeDecoder();
2985                         codec = QTextCodec.codecForName("UTF-8");
2986                 QByteArray unicode =  codec.fromUnicode(browserWindow.getContent());
2987                 noteCache.put(currentNoteGuid, unicode.toString());
2988                         
2989                 logger.log(logger.EXTREME, "updating list manager");
2990                 listManager.updateNoteContent(currentNoteGuid, browserWindow.getContent());
2991                         logger.log(logger.EXTREME, "Updating title");
2992                 listManager.updateNoteTitle(currentNoteGuid, browserWindow.getTitle());
2993                 updateListDateChanged();
2994
2995                         logger.log(logger.EXTREME, "Looking through note index for refreshed note");
2996                 for (int i=0; i<listManager.getNoteIndex().size(); i++) {
2997                         if (listManager.getNoteIndex().get(i).getGuid().equals(currentNoteGuid)) {
2998                                 currentNote = listManager.getNoteIndex().get(i);
2999                                 i = listManager.getNoteIndex().size();
3000                         }
3001                 }
3002                 noteDirty = false;
3003                 waitCursor(false);
3004         }
3005     }
3006     // Get a note from Evernote (and put it in the browser)
3007         private void refreshEvernoteNote(boolean reload) {
3008                 logger.log(logger.HIGH, "Entering NeverNote.refreshEvernoteNote");
3009                 if (Global.getDisableViewing()) {
3010                         browserWindow.setEnabled(false);
3011                         return;
3012                 }
3013                 inkNote = false;
3014                 if (!Global.showDeleted)
3015                         browserWindow.setReadOnly(false);
3016                 Global.cryptCounter =0;
3017                 if (currentNoteGuid.equals("")) {
3018                         browserWindow.setReadOnly(true);
3019                         return;
3020                 }
3021                 if (!reload)
3022                         return;
3023                 
3024                 waitCursor(true);
3025                 browserWindow.loadingData(true);
3026
3027                 currentNote = conn.getNoteTable().getNote(currentNoteGuid, true,true,false,false,true);
3028                 if (currentNote == null) 
3029                         return;
3030
3031                 if (!noteCache.containsKey(currentNoteGuid) || conn.getNoteTable().isThumbnailNeeded(currentNoteGuid)) {
3032                         QByteArray js = new QByteArray();
3033                         // We need to prepend the note with <HEAD></HEAD> or encoded characters are ugly 
3034                         js.append("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">");               
3035                         js.append("<style type=\"text/css\">en-crypt-temp { border-style:solid; border-color:blue; padding:0.5mm 0.5mm 0.5mm 0.5mm; }</style>");
3036                         js.append("<style type=\"text/css\">en-hilight { background-color: rgb(255,255,0) }</style>");
3037                         js.append("<style type=\"text/css\">en-spell { text-decoration: none; border-bottom: dotted 1px #cc0000; }</style>");
3038                         js.append("</head>");
3039                         js.append(rebuildNoteHTML(currentNoteGuid, currentNote.getContent()));
3040                         js.append("</HTML>");
3041                         js.replace("<!DOCTYPE en-note SYSTEM 'http://xml.evernote.com/pub/enml.dtd'>", "");
3042                         js.replace("<!DOCTYPE en-note SYSTEM 'http://xml.evernote.com/pub/enml2.dtd'>", "");
3043                         js.replace("<?xml version='1.0' encoding='UTF-8'?>", "");
3044                         browserWindow.getBrowser().setContent(js);
3045                         noteCache.put(currentNoteGuid, js.toString());
3046                         if (conn.getNoteTable().isThumbnailNeeded(currentNoteGuid)) {
3047                                 preview = new Thumbnailer(currentNoteGuid, new QSize(1024,768));
3048                                 preview.finished.connect(this, "saveThumbnail(String)");
3049                                 preview.setContent(js.toString());
3050                         }
3051                 } else {
3052                         logger.log(logger.HIGH, "Note content is being pulled from the cache");
3053                         String cachedContent = modifyCachedTodoTags(noteCache.get(currentNoteGuid));
3054                         browserWindow.getBrowser().setContent(new QByteArray(cachedContent));
3055                 }
3056                 
3057                 browserWindow.getBrowser().page().setContentEditable(!inkNote);  // We don't allow editing of ink notes
3058                 browserWindow.setNote(currentNote);
3059                 
3060                 // Build a list of non-closed notebooks
3061                 List<Notebook> nbooks = new ArrayList<Notebook>();
3062                 for (int i=0; i<listManager.getNotebookIndex().size(); i++) {
3063                         boolean found=false;
3064                         for (int j=0; j<listManager.getArchiveNotebookIndex().size(); j++) {
3065                                 if (listManager.getArchiveNotebookIndex().get(j).getGuid().equals(listManager.getNotebookIndex().get(i).getGuid())) 
3066                                         found = true;
3067                         }
3068                         if (!found)
3069                                 nbooks.add(listManager.getNotebookIndex().get(i));
3070                 }
3071                 
3072                 browserWindow.setNotebookList(nbooks);
3073                 browserWindow.setTitle(currentNote.getTitle());
3074                 browserWindow.setTag(getTagNamesForNote(currentNote));
3075                 browserWindow.setAuthor(currentNote.getAttributes().getAuthor());
3076                 
3077                 browserWindow.setAltered(currentNote.getUpdated());
3078                 browserWindow.setCreation(currentNote.getCreated());
3079                 if (currentNote.getAttributes().getSubjectDate() > 0)
3080                         browserWindow.setSubjectDate(currentNote.getAttributes().getSubjectDate());
3081                 else
3082                         browserWindow.setSubjectDate(currentNote.getCreated());
3083                 browserWindow.setUrl(currentNote.getAttributes().getSourceURL());
3084                 browserWindow.setAllTags(listManager.getTagIndex());
3085                 browserWindow.setCurrentTags(currentNote.getTagNames());
3086                 noteDirty = false;
3087                 scrollToGuid(currentNoteGuid);
3088                 
3089                 browserWindow.loadingData(false);
3090                 if (thumbnailViewer.isActiveWindow())
3091                         thumbnailView();
3092                 waitCursor(false);
3093                 logger.log(logger.HIGH, "Leaving NeverNote.refreshEvernoteNote");
3094         }
3095         // Save a generated thumbnail
3096         @SuppressWarnings("unused")
3097         private void saveThumbnail(String guid) {
3098                 QFile tFile = new QFile(Global.currentDir+"res/thumbnail-"+guid+".png");
3099                 tFile.open(OpenModeFlag.ReadOnly);
3100                 QByteArray imgBytes = tFile.readAll();
3101                 tFile.close();
3102                 conn.getNoteTable().setThumbnail(guid, imgBytes);
3103                 conn.getNoteTable().setThumbnailNeeded(guid, false);
3104                 thumbnailViewer.setThumbnail(QImage.fromData(imgBytes));
3105                 if (thumbnailViewer.isVisible()) 
3106                         thumbnailViewer.showFullScreen();
3107                 
3108                 /*              
3109                 QByteArray img2 = new QByteArray(conn.getNoteTable().getThumbnail(guid));
3110                 QFile file = new QFile(Global.currentDir+"res/aaaa.png");
3111                 file.open(OpenModeFlag.WriteOnly);
3112                 file.write(img2);
3113                 file.close(); 
3114                 */
3115         }
3116     // Show/Hide note information
3117         @SuppressWarnings("unused")
3118         private void toggleNoteInformation() {
3119                 logger.log(logger.HIGH, "Entering NeverNote.toggleNoteInformation");
3120         browserWindow.toggleInformation();
3121         menuBar.noteAttributes.setChecked(browserWindow.isExtended());
3122         logger.log(logger.HIGH, "Leaving NeverNote.toggleNoteInformation");
3123     }
3124         // Listener triggered when a print button is pressed
3125     @SuppressWarnings("unused")
3126         private void printNote() {
3127                 logger.log(logger.HIGH, "Entering NeverNote.printNote");
3128
3129         QPrintDialog dialog = new QPrintDialog();
3130         if (dialog.exec() == QDialog.DialogCode.Accepted.value()) {
3131                 QPrinter printer = dialog.printer();
3132                 browserWindow.getBrowser().print(printer);
3133         }
3134                 logger.log(logger.HIGH, "Leaving NeverNote.printNote");
3135
3136     }
3137     // Listener triggered when the email button is pressed
3138     @SuppressWarnings("unused")
3139         private void emailNote() {
3140         logger.log(logger.HIGH, "Entering NeverNote.emailNote");
3141         
3142         if (Desktop.isDesktopSupported()) {
3143             Desktop desktop = Desktop.getDesktop();
3144             
3145             String text2 = browserWindow.getContentsToEmail();
3146             QUrl url = new QUrl("mailto:");
3147             url.addQueryItem("subject", currentNote.getTitle());
3148             url.addQueryItem("body", QUrl.toPercentEncoding(text2).toString());
3149             QDesktopServices.openUrl(url);
3150         }
3151 /*            
3152             
3153             if (desktop.isSupported(Desktop.Action.MAIL)) {
3154                 URI uriMailTo = null;
3155                 try {
3156                         //String text = browserWindow.getBrowser().page().currentFrame().toPlainText();
3157                         String text = browserWindow.getContentsToEmail();
3158                         //text = "<b>" +text +"</b>";
3159                                         uriMailTo = new URI("mailto", "&SUBJECT="+currentNote.getTitle()
3160                                                         +"&BODY=" +text, null);
3161                                         uriMailTo = new URI("mailto", "&SUBJECT="+currentNote.getTitle()
3162                                                         +"&ATTACHMENT=d:/test.pdf", null);
3163                                         desktop.mail(uriMailTo);
3164                                 } catch (URISyntaxException e) {
3165                                         e.printStackTrace();
3166                                 } catch (IOException e) {
3167                                         e.printStackTrace();
3168                                 }
3169
3170             }
3171
3172         }     
3173  */     
3174         logger.log(logger.HIGH, "Leaving NeverNote.emailNote");
3175     }
3176         // Reindex all notes
3177     @SuppressWarnings("unused")
3178         private void fullReindex() {
3179         logger.log(logger.HIGH, "Entering NeverNote.fullReindex");
3180         // If we are deleting non-trash notes
3181         if (currentNote.getDeleted() == 0) { 
3182                 if (QMessageBox.question(this, "Confirmation", "This will cause all notes & attachments to be reindexed, "+
3183                                 "but please be aware that depending upon the size of your database updating all these records " +
3184                                 "can be time consuming and NeverNote will be unresponsive until it is complete.  Do you wish to continue?",
3185                                 QMessageBox.StandardButton.Yes, 
3186                                         QMessageBox.StandardButton.No)==StandardButton.No.value() && Global.verifyDelete() == true) {
3187                                                                 return;
3188                 }
3189         }
3190         waitCursor(true);
3191         setMessage("Marking notes for reindex.");
3192         conn.getNoteTable().reindexAllNotes();
3193         conn.getNoteTable().noteResourceTable.reindexAll(); 
3194         setMessage("Database will be reindexed.");
3195         waitCursor(false);
3196         logger.log(logger.HIGH, "Leaving NeverNote.fullRefresh");
3197     }
3198     // Listener when a user wants to reindex a specific note
3199     @SuppressWarnings("unused")
3200         private void reindexNote() {
3201         logger.log(logger.HIGH, "Entering NeverNote.reindexNote");
3202                 for (int i=0; i<selectedNoteGUIDs.size(); i++) {
3203                         conn.getNoteTable().setIndexNeeded(selectedNoteGUIDs.get(i), true);
3204                 }
3205                 if (selectedNotebookGUIDs.size() > 1)
3206                         setMessage("Notes will be reindexed.");
3207                 else
3208                         setMessage("Note will be reindexed.");
3209         logger.log(logger.HIGH, "Leaving NeverNote.reindexNote");
3210     }
3211     // Delete the note
3212     @SuppressWarnings("unused")
3213         private void deleteNote() {
3214         logger.log(logger.HIGH, "Entering NeverNote.deleteNote");
3215         if (currentNote == null) 
3216                 return;
3217         if (currentNoteGuid.equals(""))
3218                 return;
3219         
3220         // If we are deleting non-trash notes
3221         if (currentNote.isActive()) { 
3222                 if (Global.verifyDelete()) {
3223                         if (QMessageBox.question(this, "Confirmation", "Delete selected note(s)?",
3224                                         QMessageBox.StandardButton.Yes, 
3225                                         QMessageBox.StandardButton.No)==StandardButton.No.value() && Global.verifyDelete() == true) {
3226                                         return;
3227                         }
3228                 }
3229                 if (selectedNoteGUIDs.size() == 0 && !currentNoteGuid.equals("")) 
3230                         selectedNoteGUIDs.add(currentNoteGuid);
3231                 for (int i=0; i<selectedNoteGUIDs.size(); i++) {
3232                         listManager.deleteNote(selectedNoteGUIDs.get(i));
3233                 }
3234         } else { 
3235                 // If we are deleting from the trash.
3236                 if (Global.verifyDelete()) {
3237                         if (QMessageBox.question(this, "Confirmation", "Permanently delete selected note(s)?",
3238                                 QMessageBox.StandardButton.Yes, 
3239                                         QMessageBox.StandardButton.No)==StandardButton.No.value()) {
3240                                         return;
3241                         }
3242                 }
3243                 if (selectedNoteGUIDs.size() == 0 && !currentNoteGuid.equals("")) 
3244                         selectedNoteGUIDs.add(currentNoteGuid);
3245                 for (int i=selectedNoteGUIDs.size()-1; i>=0; i--) {
3246                         for (int j=noteTableView.model.rowCount()-1; j>=0; j--) {
3247                         QModelIndex modelIndex =  noteTableView.model.index(j, Global.noteTableGuidPosition);
3248                         if (modelIndex != null) {
3249                                 SortedMap<Integer, Object> ix = noteTableView.model.itemData(modelIndex);
3250                                 String tableGuid =  (String)ix.values().toArray()[0];
3251                                 if (tableGuid.equals(selectedNoteGUIDs.get(i))) {
3252                                         noteTableView.model.removeRow(j);
3253                                         j=-1;
3254                                 }
3255                         }
3256                 }
3257                         listManager.expungeNote(selectedNoteGUIDs.get(i));
3258                 }
3259         }
3260         currentNoteGuid = "";
3261         listManager.loadNotesIndex();
3262         noteIndexUpdated(false);
3263         refreshEvernoteNote(true);
3264         scrollToGuid(currentNoteGuid);
3265         logger.log(logger.HIGH, "Leaving NeverNote.deleteNote");
3266     }
3267     // Add a new note
3268     @SuppressWarnings("unused")
3269         private void addNote() {
3270         logger.log(logger.HIGH, "Inside NeverNote.addNote");
3271 //      browserWindow.setEnabled(true);
3272         browserWindow.setReadOnly(false);
3273         saveNote();
3274         Calendar currentTime = new GregorianCalendar();
3275         String noteString = new String("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
3276                 "<!DOCTYPE en-note SYSTEM \"http://xml.evernote.com/pub/enml2.dtd\">\n" +
3277                 "<en-note>\n<br clear=\"none\" /></en-note>");
3278         
3279         Long l = new Long(currentTime.getTimeInMillis());
3280         String randint = new String(Long.toString(l));
3281         
3282         // Find a notebook.  We first look for a selected notebook (the "All Notebooks" one doesn't count).  
3283         // Then we look
3284         // for the first non-archived notebook.  Finally, if nothing else we 
3285         // pick the first notebook in the list.
3286         String notebook = null;
3287         listManager.getNotebookIndex().get(0).getGuid();
3288         List<QTreeWidgetItem> selectedNotebook = notebookTree.selectedItems();
3289         if (selectedNotebook.size() > 0 && !selectedNotebook.get(0).text(0).equalsIgnoreCase("All Notebooks")) {
3290                 QTreeWidgetItem currentSelectedNotebook = selectedNotebook.get(0);
3291                 notebook = currentSelectedNotebook.text(2);
3292         } else {
3293                 boolean found = false;
3294                 List<Notebook> goodNotebooks = new ArrayList<Notebook>();
3295                 for (int i=0; i<listManager.getNotebookIndex().size(); i++) {
3296                         boolean match = false;
3297                         for (int j=0; j<listManager.getArchiveNotebookIndex().size(); j++) {
3298                                 if (listManager.getArchiveNotebookIndex().get(j).getGuid().equals(listManager.getNotebookIndex().get(i).getGuid())) {
3299                                         match = true;
3300                                         j = listManager.getArchiveNotebookIndex().size();
3301                                 }
3302                         }
3303                         if (!match)
3304                                 goodNotebooks.add(listManager.getNotebookIndex().get(i).deepCopy());
3305                 }
3306                 // Now we have a list of good notebooks, so we can look for the default
3307                 found = false;
3308                 for (int i=0; i<goodNotebooks.size(); i++) {
3309                         if (goodNotebooks.get(i).isDefaultNotebook()) {
3310                                 notebook = goodNotebooks.get(i).getGuid();
3311                                 found = true;
3312                                 i = goodNotebooks.size();
3313                         }
3314                 }
3315                 
3316                 if (goodNotebooks.size() > 0 && !found)
3317                         notebook = goodNotebooks.get(0).getGuid();
3318      
3319                 if (notebook==null)
3320                         notebook = listManager.getNotebookIndex().get(0).getGuid();             
3321         }
3322         
3323         Note newNote = new Note();
3324         newNote.setUpdateSequenceNum(0);
3325         newNote.setGuid(randint);
3326         newNote.setNotebookGuid(notebook);
3327         newNote.setTitle("");
3328         newNote.setContent(noteString);
3329         newNote.setDeleted(0);
3330         newNote.setCreated(System.currentTimeMillis());
3331         newNote.setUpdated(System.currentTimeMillis());
3332         newNote.setActive(true);
3333         NoteAttributes na = new NoteAttributes();
3334         na.setLatitude(0.0);
3335         na.setLongitude(0.0);
3336         na.setAltitude(0.0);
3337         newNote.setAttributes(new NoteAttributes());
3338         conn.getNoteTable().addNote(newNote, true);
3339         listManager.getUnsynchronizedNotes().add(newNote.getGuid());
3340         noteTableView.insertRow(listManager, newNote, true, -1);
3341         
3342         currentNote = newNote;
3343         currentNoteGuid = currentNote.getGuid();
3344         listManager.addNote(newNote);
3345         refreshEvernoteNote(true);
3346         listManager.countNotebookResults(listManager.getNoteIndex());
3347         browserWindow.titleLabel.setFocus();
3348         browserWindow.titleLabel.selectAll();
3349 //      notebookTree.updateCounts(listManager.getNotebookIndex(), listManager.getNotebookCounter());    
3350         logger.log(logger.HIGH, "Leaving NeverNote.addNote");
3351     }
3352     // Restore a note from the trash;
3353     @SuppressWarnings("unused")
3354         private void restoreNote() {
3355         waitCursor(true);
3356                 if (selectedNoteGUIDs.size() == 0 && !currentNoteGuid.equals("")) 
3357                         selectedNoteGUIDs.add(currentNoteGuid);
3358                 for (int i=0; i<selectedNoteGUIDs.size(); i++) {
3359                         listManager.restoreNote(selectedNoteGUIDs.get(i));
3360                 }
3361         currentNoteGuid = "";
3362         listManager.loadNotesIndex();
3363         noteIndexUpdated(false);
3364         waitCursor(false);
3365     }
3366     // Search a note for specific txt
3367     @SuppressWarnings("unused")
3368         private void findText() {
3369         find.show();
3370         find.setFocusOnTextField();
3371     }
3372     @SuppressWarnings("unused")
3373         private void doFindText() {
3374         browserWindow.getBrowser().page().findText(find.getText(), find.getFlags());
3375         find.setFocus();
3376     }
3377     // Signal received that note content has changed.  Normally we just need the guid to remove
3378     // it from the cache.
3379     @SuppressWarnings("unused")
3380         private void invalidateNoteCache(String guid, String content) {
3381         String v = noteCache.remove(guid);
3382         if (content != null) {
3383                 v = noteCache.put(guid, content);
3384         }
3385     }
3386     // Signal received that a note guid has changed
3387     @SuppressWarnings("unused")
3388         private void noteGuidChanged(String oldGuid, String newGuid) {
3389         if (noteCache.containsKey(oldGuid)) {
3390                 String cache = noteCache.get(oldGuid);
3391                 noteCache.put(newGuid, cache);
3392                 noteCache.remove(oldGuid);
3393         }
3394         listManager.updateNoteGuid(oldGuid, newGuid, false);
3395         if (currentNoteGuid.equals(oldGuid)) {
3396                 if (currentNote != null)
3397                         currentNote.setGuid(newGuid);
3398                 currentNoteGuid = newGuid;
3399         }
3400         for (int i=0; i<listManager.getNoteIndex().size(); i++) {
3401                 if (listManager.getNoteIndex().get(i).getGuid().equals(newGuid)) {
3402                         noteTableView.proxyModel.addGuid(newGuid);
3403                         i=listManager.getNoteIndex().size();
3404                 }
3405         }
3406                 updateListGuid(oldGuid, newGuid);
3407     }
3408     // Toggle the note editor button bar
3409     private void toggleEditorButtonBar() {
3410         if (browserWindow.buttonsVisible) {
3411                 browserWindow.hideButtons();
3412                 menuBar.showEditorBar.setChecked(browserWindow.buttonsVisible);
3413 //              Global.saveWindowVisible("editorButtonBar", browserWindow.buttonsVisible);
3414         } else {
3415                 browserWindow.buttonsVisible = true;
3416                 showEditorButtons();
3417         }
3418         Global.saveWindowVisible("editorButtonBar", browserWindow.buttonsVisible);
3419     }
3420     // Show editor buttons
3421     private void showEditorButtons() {
3422                 browserWindow.undoButton.setVisible(false);
3423                 browserWindow.redoButton.setVisible(false);
3424                 browserWindow.cutButton.setVisible(false);
3425                 browserWindow.copyButton.setVisible(false);
3426                 browserWindow.pasteButton.setVisible(false);
3427                 browserWindow.strikethroughButton.setVisible(false);
3428                 browserWindow.underlineButton.setVisible(false);
3429                 browserWindow.boldButton.setVisible(false);
3430                 browserWindow.italicButton.setVisible(false);
3431                 browserWindow.hlineButton.setVisible(false);
3432                 browserWindow.indentButton.setVisible(false);
3433                 browserWindow.outdentButton.setVisible(false);
3434                 browserWindow.fontList.setVisible(false);
3435                 browserWindow.fontSize.setVisible(false);
3436                 browserWindow.fontColor.setVisible(false);
3437                 browserWindow.fontHilight.setVisible(false);
3438                 browserWindow.leftAlignButton.setVisible(false);
3439                 browserWindow.centerAlignButton.setVisible(false);
3440                 browserWindow.rightAlignButton.setVisible(false);
3441                 browserWindow.indentButton.setVisible(false);
3442                 browserWindow.outdentButton.setVisible(false);
3443
3444                 browserWindow.undoButton.setVisible(Global.isEditorButtonVisible("undo"));
3445                 browserWindow.redoButton.setVisible(Global.isEditorButtonVisible("redo"));
3446                 browserWindow.cutButton.setVisible(Global.isEditorButtonVisible("cut"));
3447                 browserWindow.copyButton.setVisible(Global.isEditorButtonVisible("copy"));
3448                 browserWindow.pasteButton.setVisible(Global.isEditorButtonVisible("paste"));
3449                 browserWindow.strikethroughButton.setVisible(Global.isEditorButtonVisible("strikethrough"));
3450                 browserWindow.underlineButton.setVisible(Global.isEditorButtonVisible("underline"));
3451                 browserWindow.boldButton.setVisible(Global.isEditorButtonVisible("bold"));
3452                 browserWindow.italicButton.setVisible(Global.isEditorButtonVisible("italic"));
3453                 browserWindow.hlineButton.setVisible(Global.isEditorButtonVisible("hline"));
3454                 browserWindow.indentButton.setVisible(Global.isEditorButtonVisible("indent"));
3455                 browserWindow.outdentButton.setVisible(Global.isEditorButtonVisible("outdent"));
3456                 browserWindow.bulletListButton.setVisible(Global.isEditorButtonVisible("bulletList"));
3457                 browserWindow.numberListButton.setVisible(Global.isEditorButtonVisible("numberList"));
3458                 browserWindow.fontList.setVisible(Global.isEditorButtonVisible("font"));
3459                 browserWindow.fontSize.setVisible(Global.isEditorButtonVisible("fontSize"));
3460                 browserWindow.fontColor.setVisible(Global.isEditorButtonVisible("fontColor"));
3461                 browserWindow.fontHilight.setVisible(Global.isEditorButtonVisible("fontHilight"));
3462                 browserWindow.leftAlignButton.setVisible(Global.isEditorButtonVisible("alignLeft"));
3463                 browserWindow.centerAlignButton.setVisible(Global.isEditorButtonVisible("alignCenter"));
3464                 browserWindow.rightAlignButton.setVisible(Global.isEditorButtonVisible("alignRight"));
3465     }
3466     private void duplicateNote(String guid) {
3467                 
3468                 Calendar currentTime = new GregorianCalendar();
3469                 Long l = new Long(currentTime.getTimeInMillis());
3470                 String newGuid = new String(Long.toString(l));
3471                                         
3472                 Note oldNote = conn.getNoteTable().getNote(guid, true, true, false, false, false);
3473                 Note newNote = oldNote.deepCopy();
3474                 newNote.setGuid(newGuid);
3475                 List<Resource> resList = conn.getNoteTable().noteResourceTable.getNoteResources(guid, true);
3476                 oldNote.setResources(resList);
3477                 duplicateNote(oldNote);
3478         }
3479         private void duplicateNote(Note oldNote) {
3480                 waitCursor(true);
3481                 // Now that we have a good notebook guid, we need to move the conflicting note
3482                 // to the local notebook
3483                 Calendar currentTime = new GregorianCalendar();
3484                 Long l = new Long(currentTime.getTimeInMillis());
3485                 String newGuid = new String(Long.toString(l));
3486                                         
3487                 Note newNote = oldNote.deepCopy();
3488                 newNote.setUpdateSequenceNum(0);
3489                 newNote.setGuid(newGuid);
3490                 newNote.setDeleted(0);
3491                 newNote.setActive(true);
3492                 List<Resource> resList = oldNote.getResources();
3493                 if (resList == null)
3494                         resList = new ArrayList<Resource>();
3495                 long prevGuid = 0;
3496                 for (int i=0; i<resList.size(); i++) {
3497                         l = prevGuid;
3498                         while (l == prevGuid) {
3499                                 currentTime = new GregorianCalendar();
3500                                 l = new Long(currentTime.getTimeInMillis());
3501                         }
3502                         prevGuid = l;
3503                         String newResGuid = new String(Long.toString(l));
3504                         resList.get(i).setNoteGuid(newGuid);
3505                         resList.get(i).setGuid(newResGuid);
3506                         resList.get(i).setUpdateSequenceNum(0);
3507                         resList.get(i).setActive(true);
3508                         conn.getNoteTable().noteResourceTable.saveNoteResource(new Resource(resList.get(i).deepCopy()), true);
3509                 }
3510                 newNote.setResources(resList);
3511                 listManager.addNote(newNote);
3512                 conn.getNoteTable().addNote(newNote, true);
3513                 listManager.getUnsynchronizedNotes().add(newNote.getGuid());
3514                 noteTableView.insertRow(listManager, newNote, true, -1);
3515                 listManager.countNotebookResults(listManager.getNoteIndex());
3516                 waitCursor(false);
3517         }
3518         // Merge notes
3519         @SuppressWarnings("unused")
3520         private void mergeNotes() {
3521                 logger.log(logger.HIGH, "Merging notes");
3522                 waitCursor(true);
3523                 saveNote();
3524                 String masterGuid = null;
3525                 List<String> sources = new ArrayList<String>();
3526                 QModelIndex index;
3527                 for (int i=0; i<noteTableView.selectionModel().selectedRows().size(); i++) {
3528                         int r = noteTableView.selectionModel().selectedRows().get(i).row();
3529                         index = noteTableView.proxyModel.index(r, Global.noteTableGuidPosition);
3530                         SortedMap<Integer, Object> ix = noteTableView.proxyModel.itemData(index);
3531                 if (i == 0) 
3532                         masterGuid = (String)ix.values().toArray()[0];
3533                 else 
3534                         sources.add((String)ix.values().toArray()[0]);  
3535                 }
3536                 
3537                 logger.log(logger.EXTREME, "Master guid=" +masterGuid);
3538                 logger.log(logger.EXTREME, "Children count: "+sources.size());
3539                 mergeNoteContents(masterGuid, sources);
3540                 currentNoteGuid = masterGuid;
3541                 noteIndexUpdated(false);
3542                 refreshEvernoteNote(true);
3543                 waitCursor(false);
3544         }
3545         private void mergeNoteContents(String targetGuid, List<String> sources) {
3546                 Note target = conn.getNoteTable().getNote(targetGuid, true, false, false, false, false);
3547                 String newContent = target.getContent();
3548                 newContent = newContent.replace("</en-note>", "<br></br>");
3549                 
3550                 for (int i=0; i<sources.size(); i++) {
3551                         Note source = conn.getNoteTable().getNote(sources.get(i), true, true, false, false, false);
3552                         if (source.isSetTitle()) {
3553                                 newContent = newContent +("<table bgcolor=\"lightgrey\"><tr><td><font size=\"6\"><b>" +source.getTitle() +"</b></font></td></tr></table>");
3554                         }
3555                         String sourceContent = source.getContent();
3556                         logger.log(logger.EXTREME, "Merging contents into note");
3557                         logger.log(logger.EXTREME, sourceContent);
3558                         logger.log(logger.EXTREME, "End of content");
3559                         int startOfNote = sourceContent.indexOf("<en-note>");
3560                         sourceContent = sourceContent.substring(startOfNote+9);
3561                         int endOfNote = sourceContent.indexOf("</en-note>");
3562                         sourceContent = sourceContent.substring(0,endOfNote);
3563                         newContent = newContent + sourceContent;
3564                         logger.log(logger.EXTREME, "New note content");
3565                         logger.log(logger.EXTREME, newContent);
3566                         logger.log(logger.EXTREME, "End of content");
3567                         for (int j=0; j<source.getResourcesSize(); j++) {
3568                                 logger.log(logger.EXTREME, "Reassigning resource: "+source.getResources().get(j).getGuid());
3569                                 Resource r = source.getResources().get(j);
3570                                 Resource newRes = conn.getNoteTable().noteResourceTable.getNoteResource(r.getGuid(), true);
3571                                 
3572                                 Calendar currentTime = new GregorianCalendar();
3573                                 Long l = new Long(currentTime.getTimeInMillis());
3574                                                         
3575                                 long prevGuid = 0;
3576                                 l = prevGuid;
3577                                 while (l == prevGuid) {
3578                                         currentTime = new GregorianCalendar();
3579                                         l = new Long(currentTime.getTimeInMillis());
3580                                 }
3581                                 String newResGuid = new String(Long.toString(l));
3582                                 newRes.setNoteGuid(targetGuid);
3583                                 newRes.setGuid(newResGuid);
3584                                 newRes.setUpdateSequenceNum(0);
3585                                 newRes.setActive(true);
3586                                 conn.getNoteTable().noteResourceTable.saveNoteResource(newRes, true);
3587                         }
3588                 }
3589                 logger.log(logger.EXTREME, "Updating note");
3590                 conn.getNoteTable().updateNoteContent(targetGuid, newContent +"</en-note>");
3591                 for (int i=0; i<sources.size(); i++) {
3592                         logger.log(logger.EXTREME, "Deleting note " +sources.get(i));
3593                         listManager.deleteNote(sources.get(i));
3594                 }
3595                 logger.log(logger.EXTREME, "Exiting merge note");
3596         }
3597         // A resource within a note has had a guid change 
3598         @SuppressWarnings("unused")
3599         private void noteResourceGuidChanged(String noteGuid, String oldGuid, String newGuid) {
3600                 if (!oldGuid.equals(newGuid))
3601                         Global.resourceMap.put(oldGuid, newGuid);
3602         }
3603         // View a thumbnail of the note
3604         public void thumbnailView() {
3605                 
3606                 String thumbnailName = Global.currentDir+"res/thumbnail-"+currentNoteGuid+".png";
3607                 QFile thumbnail = new QFile(thumbnailName);
3608                 if (!thumbnail.exists()) {
3609                         
3610                         QImage img = new QImage();
3611                         img.loadFromData(conn.getNoteTable().getThumbnail(currentNoteGuid));
3612                         thumbnailViewer.setThumbnail(img);
3613                 } else
3614                         thumbnailViewer.setThumbnail(thumbnailName);
3615                 if (!thumbnailViewer.isVisible()) 
3616                         thumbnailViewer.showFullScreen();
3617         }
3618
3619         //**********************************************************
3620     //**********************************************************
3621     //* Online user actions
3622     //**********************************************************
3623     //**********************************************************
3624     private void setupOnlineMenu() {
3625         if (!Global.isConnected) {
3626                 menuBar.noteOnlineHistoryAction.setEnabled(false);
3627                 return;
3628         } else {
3629                 menuBar.noteOnlineHistoryAction.setEnabled(true);
3630         }
3631     }
3632     @SuppressWarnings("unused")
3633         private void viewNoteHistory() {
3634         if (currentNoteGuid == null || currentNoteGuid.equals("")) 
3635                 return;
3636         if (currentNote.getUpdateSequenceNum() == 0) {
3637                 setMessage("Note has never been synchronized.");
3638                         QMessageBox.information(this, "Error", "This note has never been sent to Evernote, so there is no history.");
3639                         return;
3640         }
3641         
3642         setMessage("Getting Note History");
3643         waitCursor(true);
3644         Note currentOnlineNote = null;
3645         versions = null;
3646         try {
3647                 if (Global.isPremium())
3648                         versions = syncRunner.noteStore.listNoteVersions(syncRunner.authToken, currentNoteGuid);
3649                 else
3650                         versions = new ArrayList<NoteVersionId>();
3651                 currentOnlineNote = syncRunner.noteStore.getNote(syncRunner.authToken, currentNoteGuid, true, true, false, false);
3652                 } catch (EDAMUserException e) {
3653                         setMessage("EDAMUserException: " +e.getMessage());
3654                         return;
3655                 } catch (EDAMSystemException e) {
3656                         setMessage("EDAMSystemException: " +e.getMessage());
3657                         return;
3658                 } catch (EDAMNotFoundException e) {
3659                         setMessage("Note not found on server.");
3660                         QMessageBox.information(this, "Error", "This note could not be found on Evernote's servers.");
3661                         return;
3662                 } catch (TException e) {
3663                         setMessage("EDAMTransactionException: " +e.getMessage());
3664                         return;
3665                 }
3666                 
3667                 // If we've gotten this far, we have a good note.
3668                 if (historyWindow == null) {
3669                         historyWindow = new OnlineNoteHistory(conn);
3670                         historyWindow.historyCombo.activated.connect(this, "reloadHistoryWindow(String)");
3671                         historyWindow.restoreAsNew.clicked.connect(this, "restoreHistoryNoteAsNew()");
3672                         historyWindow.restore.clicked.connect(this, "restoreHistoryNote()");
3673                 } else {
3674                         historyWindow.historyCombo.clear();
3675                 }
3676                 boolean isDirty = conn.getNoteTable().isNoteDirty(currentNoteGuid);
3677                 if (currentNote.getUpdateSequenceNum() != currentOnlineNote.getUpdateSequenceNum())
3678                         isDirty = true;
3679                 historyWindow.setCurrent(isDirty);
3680                 
3681                 loadHistoryWindowContent(currentOnlineNote);
3682                 historyWindow.load(versions);
3683                 setMessage("History retrieved");
3684                 waitCursor(false);
3685                 historyWindow.exec();
3686     }
3687     private Note reloadHistoryWindow(String selection) {
3688         waitCursor(true);
3689                 String fmt = Global.getDateFormat() + " " + Global.getTimeFormat();
3690                 String dateTimeFormat = new String(fmt);
3691                 SimpleDateFormat simple = new SimpleDateFormat(dateTimeFormat);
3692                 int index = -1;
3693                 int usn = 0;
3694                 
3695                 for (int i=0; i<versions.size(); i++) {
3696                         StringBuilder versionDate = new StringBuilder(simple.format(versions.get(i).getServiceUpdated()));
3697                         if (versionDate.toString().equals(selection))
3698                                 index = i;
3699                 }
3700                 
3701                 if (index > -1 || selection.indexOf("Current") > -1) {
3702                         Note historyNote = null;
3703                         try {
3704                                 if (index > -1) {
3705                                         usn = versions.get(index).getUpdateSequenceNum();
3706                                         historyNote = syncRunner.noteStore.getNoteVersion(syncRunner.authToken, currentNoteGuid, usn, true, true, true);
3707                                 } else
3708                                         historyNote = syncRunner.noteStore.getNote(syncRunner.authToken, currentNoteGuid, true,true,true,true);
3709                         } catch (EDAMUserException e) {
3710                                 setMessage("EDAMUserException: " +e.getMessage());
3711                                 waitCursor(false);
3712                                 return null;
3713                         } catch (EDAMSystemException e) {
3714                                 setMessage("EDAMSystemException: " +e.getMessage());
3715                                 waitCursor(false);
3716                                 return null;
3717                         } catch (EDAMNotFoundException e) {
3718                                 setMessage("EDAMNotFoundException: " +e.getMessage());
3719                                 waitCursor(false);
3720                                 return null;
3721                         } catch (TException e) {
3722                                 setMessage("EDAMTransactionException: " +e.getMessage());
3723                                 waitCursor(false);
3724                                 return null;
3725                         }
3726                         
3727                         waitCursor(false);
3728                         if (historyNote != null) 
3729                                 historyWindow.setContent(historyNote);
3730                         return historyNote;
3731                 }
3732                 waitCursor(false);
3733                 return null;
3734     }
3735     private void loadHistoryWindowContent(Note note) {
3736         note.setUpdateSequenceNum(0);
3737                 historyWindow.setContent(note); 
3738     }
3739     @SuppressWarnings("unused")
3740         private void restoreHistoryNoteAsNew() {
3741         setMessage("Restoring as new note.");
3742         duplicateNote(reloadHistoryWindow(historyWindow.historyCombo.currentText()));
3743         setMessage("Note has been restored as a new note.");
3744     }
3745     @SuppressWarnings("unused")
3746         private void restoreHistoryNote() {
3747         setMessage("Restoring note.");
3748         Note n = reloadHistoryWindow(historyWindow.historyCombo.currentText());
3749         conn.getNoteTable().expungeNote(n.getGuid(), true, false);
3750         n.setActive(true);
3751         n.setDeleted(0);
3752                 for (int i=0; i<n.getResourcesSize(); i++) {
3753                         n.getResources().get(i).setActive(true);
3754                         conn.getNoteTable().noteResourceTable.saveNoteResource(n.getResources().get(i), true);
3755                 }
3756         listManager.addNote(n);
3757         conn.getNoteTable().addNote(n, true);
3758         refreshEvernoteNote(true);
3759         setMessage("Note has been restored.");
3760     }
3761     
3762     
3763     
3764         //**********************************************************
3765         //**********************************************************
3766         //* XML Modifying methods
3767         //**********************************************************
3768         //**********************************************************
3769     // find the appropriate icon for an attachment
3770     private String findIcon(String appl) {
3771         logger.log(logger.HIGH, "Entering NeverNote.findIcon");
3772         appl = appl.toLowerCase();
3773         File f = new File(Global.getDirectoryPath()+"images"+File.separator +appl +".png");
3774         if (f.exists())
3775                 return appl+".png";
3776         logger.log(logger.HIGH, "Leaving NeverNote.findIcon");
3777         return "attachment.png";
3778     }
3779     // Modify the en-media tag into an attachment
3780     private void modifyApplicationTags(QDomDocument doc, QDomElement docElem, QDomElement enmedia, QDomAttr hash, String appl) {
3781         logger.log(logger.HIGH, "Entering NeverNote.modifyApplicationTags");
3782         if (appl.equalsIgnoreCase("vnd.evernote.ink"))
3783                 inkNote = true;
3784         String resGuid = conn.getNoteTable().noteResourceTable.getNoteResourceGuidByHashHex(currentNote.getGuid(), hash.value());
3785         Resource r = conn.getNoteTable().noteResourceTable.getNoteResource(resGuid, false);
3786         if (r == null || r.getData() == null) 
3787                 resourceErrorMessage();
3788                 if (r!= null) {
3789                         if (r.getData()!=null) {
3790                                 // Did we get a generic applicaiton?  Then look at the file name to 
3791                                 // try and find a good application type for the icon
3792                                 if (appl.equalsIgnoreCase("octet-stream")) {
3793                                         if (r.getAttributes() != null && r.getAttributes().getFileName() != null) {
3794                                                 String fn = r.getAttributes().getFileName();
3795                                                 int pos = fn.lastIndexOf(".");
3796                                                 if (pos > -1) {
3797                                                         appl = fn.substring(pos+1);
3798                                                 }
3799                                         }
3800                                 }
3801                                 
3802                                 String fileDetails = null;
3803                                 if (r.getAttributes() != null && r.getAttributes().getFileName() != null && !r.getAttributes().getFileName().equals(""))
3804                                         fileDetails = r.getAttributes().getFileName();
3805                                 String contextFileName;
3806                                 String pathPrefix = Global.currentDir;
3807                                 pathPrefix = Global.getDirectoryPath()+"res/";
3808                                 if (fileDetails != null && !fileDetails.equals("")) {
3809                                         enmedia.setAttribute("href", "nnres://" +r.getGuid() +Global.attachmentNameDelimeter +fileDetails);
3810                                         contextFileName = Global.currentDir +"res/" +r.getGuid() +Global.attachmentNameDelimeter +fileDetails;
3811                                 } else { 
3812                                         enmedia.setAttribute("href", "nnres://" +r.getGuid() +Global.attachmentNameDelimeter +appl);
3813                                         contextFileName = pathPrefix+r.getGuid() +Global.attachmentNameDelimeter +appl;
3814                                 }
3815                                 contextFileName = contextFileName.replace("\\", "/");
3816                                 enmedia.setAttribute("onContextMenu", "window.jambi.resourceContextMenu('" +contextFileName +"');");
3817                                 if (fileDetails == null || fileDetails.equals(""))
3818                                         fileDetails = "";
3819                                 enmedia.setAttribute("en-tag", "en-media");
3820                                 enmedia.setAttribute("guid", r.getGuid());
3821                                 enmedia.setTagName("a");
3822                                 QDomElement newText = doc.createElement("img");
3823                                 boolean goodPreview = false;
3824                                 String filePath = "";
3825                                 if (appl.equalsIgnoreCase("pdf") && Global.pdfPreview()) {
3826                                         String fileName;
3827                                         Resource res = conn.getNoteTable().noteResourceTable.getNoteResource(r.getGuid(), true);
3828                                         if (res.getAttributes() != null && 
3829                                                         res.getAttributes().getFileName() != null && 
3830                                                         !res.getAttributes().getFileName().trim().equals(""))
3831                                                 fileName = res.getGuid()+Global.attachmentNameDelimeter+res.getAttributes().getFileName();
3832                                         else
3833                                                 fileName = res.getGuid()+".pdf";
3834                                         QFile file = new QFile(Global.getDirectoryPath() +"res/"+fileName);
3835                                 QFile.OpenMode mode = new QFile.OpenMode();
3836                                 mode.set(QFile.OpenModeFlag.WriteOnly);
3837                                 file.open(mode);
3838                                 QDataStream out = new QDataStream(file);
3839                                 Resource resBinary = conn.getNoteTable().noteResourceTable.getNoteResource(res.getGuid(), true);
3840                                         QByteArray binData = new QByteArray(resBinary.getData().getBody());
3841                                         resBinary = null;
3842                                 out.writeBytes(binData.toByteArray());
3843                                 file.close();
3844                                 PDFPreview pdfPreview = new PDFPreview();
3845                                         goodPreview = pdfPreview.setupPreview(file.fileName(), appl,0);
3846                                         if (goodPreview) {
3847                                                 QDomElement span = doc.createElement("span");
3848                                                 QDomElement table = doc.createElement("table");
3849                                                 span.setAttribute("pdfNavigationTable", "true");
3850                                                 QDomElement tr = doc.createElement("tr");
3851                                                 QDomElement td = doc.createElement("td");
3852                                                 QDomElement left = doc.createElement("img");
3853                                                 left.setAttribute("onMouseDown", "window.jambi.nextPage('" +file.fileName() +"')");
3854                                                 left.setAttribute("onMouseDown", "window.jambi.nextPage('" +file.fileName() +"')");
3855                                                 left.setAttribute("onMouseOver", "style.cursor='hand'");
3856                                                 QDomElement right = doc.createElement("img");
3857                                                 right.setAttribute("onMouseDown", "window.jambi.nextPage('" +file.fileName() +"')");
3858                                                 left.setAttribute("onMouseDown", "window.jambi.previousPage('" +file.fileName() +"')");
3859                                                 left.setAttribute("src", Global.currentDir+"images/small_left.png");
3860                                                 right.setAttribute("src", Global.currentDir+"images/small_right.png");
3861                                                 right.setAttribute("onMouseOver", "style.cursor='hand'");
3862                                                 
3863                                                 table.appendChild(tr);
3864                                                 tr.appendChild(td);
3865                                                 td.appendChild(left);
3866                                                 td.appendChild(right);
3867                                                 span.appendChild(table);
3868                                                 enmedia.parentNode().insertBefore(span, enmedia);
3869                                         } 
3870                                         filePath = fileName+".png";
3871                                 }
3872                                 String icon = findIcon(appl);
3873                                 if (icon.equals("attachment.png"))
3874                                         icon = findIcon(fileDetails.substring(fileDetails.indexOf(".")+1));
3875                                 newText.setAttribute("src", Global.getDirectoryPath()+"images"+File.separator +icon);
3876                                 if (goodPreview) {
3877                                         newText.setAttribute("src", Global.getDirectoryPath()+"res/"+filePath);
3878                                         newText.setAttribute("style", "border-style:solid; border-color:green; padding:0.5mm 0.5mm 0.5mm 0.5mm;");
3879                                 }
3880                                 newText.setAttribute("title", fileDetails);
3881                                 enmedia.removeChild(enmedia.firstChild());
3882                                 
3883                                 enmedia.appendChild(newText);
3884                         }
3885                 }
3886                 logger.log(logger.HIGH, "Leaving NeverNote.modifyApplicationTags");
3887     }
3888     // Modify the en-to tag into an input field
3889     private void modifyTodoTags(QDomElement todo) {
3890         logger.log(logger.HIGH, "Entering NeverNote.modifyTodoTags");
3891                 todo.setAttribute("type", "checkbox");
3892                 String checked = todo.attribute("checked");
3893                 todo.removeAttribute("checked");
3894                 if (checked.equalsIgnoreCase("true"))
3895                         todo.setAttribute("checked", "");
3896                 else
3897                         todo.setAttribute("unchecked","");
3898                 todo.setAttribute("value", checked);
3899                 todo.setAttribute("onClick", "value=checked;window.jambi.contentChanged(); ");
3900                 todo.setTagName("input");
3901                 logger.log(logger.HIGH, "Leaving NeverNote.modifyTodoTags");
3902     }
3903     // Modify any cached todo tags that may have changed
3904     private String modifyCachedTodoTags(String note) {
3905         logger.log(logger.HIGH, "Entering NeverNote.modifyCachedTodoTags");
3906         StringBuffer html = new StringBuffer(note);
3907                 for (int i=html.indexOf("<input", 0); i>-1; i=html.indexOf("<input", i)) {
3908                         int endPos =html.indexOf(">",i+1);
3909                         String input = html.substring(i,endPos);
3910                         if (input.indexOf("value=\"true\"") > 0) 
3911                                 input = input.replace("unchecked=\"\"", "checked=\"\"");
3912                         else
3913                                 input = input.replace("checked=\"\"", "unchecked=\"\"");
3914                         html.replace(i, endPos, input);
3915                         i++;
3916                 }
3917                 logger.log(logger.HIGH, "Leaving NeverNote.modifyCachedTodoTags");
3918                 return html.toString();
3919     }
3920     // Modify the en-media tag into an image tag so it can be displayed.
3921     private void modifyImageTags(QDomElement docElem, QDomElement enmedia, QDomAttr hash) {
3922         logger.log(logger.HIGH, "Entering NeverNote.modifyImageTags");
3923         String type = enmedia.attribute("type");
3924         if (type.startsWith("image/"))
3925                 type = "."+type.substring(6);
3926         else
3927                 type="";
3928         
3929         String resGuid = conn.getNoteTable().noteResourceTable.getNoteResourceGuidByHashHex(currentNoteGuid, hash.value());
3930         QFile tfile = new QFile(Global.getDirectoryPath()+"res"+File.separator +resGuid+type);
3931         if (!tfile.exists()) {
3932                 Resource r = null;
3933                 if (resGuid != null)
3934                         r = conn.getNoteTable().noteResourceTable.getNoteResource(resGuid,true);
3935                         if (r==null || r.getData() == null || r.getData().getBody().length == 0)
3936                                 resourceErrorMessage();
3937                         if (r!= null && r.getData() != null && r.getData().getBody().length > 0) {
3938                                 tfile.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.WriteOnly));
3939                                 QByteArray binData = new QByteArray(r.getData().getBody());
3940                                 tfile.write(binData);
3941                                 tfile.close();
3942                                 enmedia.setAttribute("src", QUrl.fromLocalFile(tfile.fileName()).toString());
3943                                 enmedia.setAttribute("en-tag", "en-media");
3944                                 enmedia.setNodeValue("");
3945                         enmedia.setAttribute("guid", r.getGuid());
3946                         enmedia.setTagName("img");
3947                 }
3948         }
3949                 enmedia.setAttribute("src", QUrl.fromLocalFile(tfile.fileName()).toString());
3950                 enmedia.setAttribute("en-tag", "en-media");
3951                 enmedia.setAttribute("onContextMenu", "window.jambi.imageContextMenu('" +tfile.fileName()  +"');");
3952                 enmedia.setNodeValue("");
3953                 enmedia.setAttribute("guid", resGuid);
3954                 enmedia.setTagName("img");
3955
3956                 logger.log(logger.HIGH, "Leaving NeverNote.modifyImageTags");
3957     }
3958         // Modify tags from Evernote specific things to XHTML tags.
3959         private QDomDocument modifyTags(QDomDocument doc) {
3960                 logger.log(logger.HIGH, "Entering NeverNote.modifyTags");
3961                 if (tempFiles == null)
3962                         tempFiles = new ArrayList<QTemporaryFile>();
3963                 tempFiles.clear();
3964                 QDomElement docElem = doc.documentElement();
3965                 
3966                 // Modify en-media tags
3967                 QDomNodeList anchors = docElem.elementsByTagName("en-media");
3968                 int enMediaCount = anchors.length();
3969                 for (int i=enMediaCount-1; i>=0; i--) {
3970                         QDomElement enmedia = anchors.at(i).toElement();
3971                         if (enmedia.hasAttribute("type")) {
3972                                 QDomAttr attr = enmedia.attributeNode("type");
3973                                 QDomAttr hash = enmedia.attributeNode("hash");
3974                                 String[] type = attr.nodeValue().split("/");
3975                                 String appl = type[1];
3976                                 
3977                                 if (type[0] != null) {
3978                                         if (type[0].equals("image")) {
3979                                                 modifyImageTags(docElem, enmedia, hash);
3980                                         }
3981                                         if (!type[0].equals("image")) {
3982                                                 modifyApplicationTags(doc, docElem, enmedia, hash, appl);
3983                                         }
3984                                 }
3985                         }
3986                 }
3987                 
3988                 // Modify todo tags
3989                 anchors = docElem.elementsByTagName("en-todo");
3990                 int enTodoCount = anchors.length();
3991                 for (int i=enTodoCount-1; i>=0; i--) {
3992                         QDomElement enmedia = anchors.at(i).toElement();
3993                         modifyTodoTags(enmedia);
3994                 }
3995                 
3996                 // Modify en-crypt tags
3997                 anchors = docElem.elementsByTagName("en-crypt");
3998                 int enCryptLen = anchors.length();
3999                 for (int i=enCryptLen-1; i>=0; i--) {
4000                         QDomElement enmedia = anchors.at(i).toElement();
4001                         enmedia.setAttribute("contentEditable","false");
4002                         enmedia.setAttribute("src", Global.getDirectoryPath()+"images/encrypt.png");
4003                         enmedia.setAttribute("en-tag","en-crypt");
4004                         enmedia.setAttribute("alt", enmedia.text());
4005                         Global.cryptCounter++;
4006                         enmedia.setAttribute("id", "crypt"+Global.cryptCounter.toString());
4007                         String encryptedText = enmedia.text();
4008                         
4009                         // If the encryption string contains crlf at the end, remove them because they mess up the javascript.
4010                         if (encryptedText.endsWith("\n"))
4011                                 encryptedText = encryptedText.substring(0,encryptedText.length()-1);
4012                         if (encryptedText.endsWith("\r"))
4013                                 encryptedText = encryptedText.substring(0,encryptedText.length()-1);
4014                         
4015                         // Add the commands
4016                         String hint = enmedia.attribute("hint");
4017                         hint = hint.replace("'","&apos;");
4018                         enmedia.setAttribute("onClick", "window.jambi.decryptText('crypt"+Global.cryptCounter.toString()+"', '"+encryptedText+"', '"+hint+"');");
4019                         enmedia.setAttribute("onMouseOver", "style.cursor='hand'");
4020                         enmedia.setTagName("img");
4021                         enmedia.removeChild(enmedia.firstChild());   // Remove the actual encrypted text
4022                 }
4023
4024                 logger.log(logger.HIGH, "Leaving NeverNote.modifyTags");
4025                 return doc;
4026         }
4027         // Rebuild the note HTML to something usable
4028         private String rebuildNoteHTML(String noteGuid, String note) {
4029                 logger.log(logger.HIGH, "Entering NeverNote.rebuildNoteHTML");
4030                 logger.log(logger.EXTREME, "Note guid: " +noteGuid);
4031                 logger.log(logger.EXTREME, "Note Text:" +note);
4032                 QDomDocument doc = new QDomDocument();
4033                 QDomDocument.Result result = doc.setContent(note);
4034                 if (!result.success) {
4035                         logger.log(logger.MEDIUM, tr("Parse error when rebuilding HTML"));
4036                         logger.log(logger.MEDIUM, tr("Note guid: " +noteGuid));
4037                         logger.log(logger.EXTREME, tr("Start of unmodified note HTML"));
4038                         logger.log(logger.EXTREME, note);
4039                         logger.log(logger.EXTREME, tr("End of unmodified note HTML"));
4040                         return note;
4041                 }
4042
4043                 if (tempFiles == null)
4044                         tempFiles = new ArrayList<QTemporaryFile>();
4045                 tempFiles.clear();
4046                 
4047                 doc = modifyTags(doc);
4048                 doc = addHilight(doc);
4049                 QDomElement docElem = doc.documentElement();
4050                 docElem.setTagName("Body");
4051 //              docElem.setAttribute("bgcolor", "green");
4052                 logger.log(logger.EXTREME, "Rebuilt HTML:");
4053                 logger.log(logger.EXTREME, doc.toString());     
4054                 logger.log(logger.HIGH, "Leaving NeverNote.rebuildNoteHTML");
4055                 // Fix the stupid problem where inserting an <img> tag after an <a> tag (which is done
4056                 // to get the <en-media> application tag to work properly) causes spaces to be inserted
4057                 // between the <a> & <img>.  This messes things up later.  This is an ugly hack.
4058                 StringBuffer html = new StringBuffer(doc.toString());
4059                 for (int i=html.indexOf("<a en-tag=\"en-media\" ", 0); i>-1; i=html.indexOf("<a en-tag=\"en-media\" ", i)) {
4060                         i=html.indexOf(">\n",i+1);
4061                         int z = html.indexOf("<img",i);
4062                         for (int j=z-1; j>i; j--) 
4063                                 html.deleteCharAt(j);
4064                         i=html.indexOf("/>", z+1);
4065                         z = html.indexOf("</a>",i);
4066                         for (int j=z-1; j>i+1; j--) 
4067                                 html.deleteCharAt(j);
4068                 } 
4069                 return html.toString();
4070         }       
4071         // Scan and do hilighting of words
4072         private QDomDocument addHilight(QDomDocument doc) {
4073                 EnSearch e = listManager.getEnSearch();
4074                 if (e.hilightWords == null || e.hilightWords.size() == 0)
4075                         return doc;
4076                 XMLInsertHilight hilight = new XMLInsertHilight(doc, listManager.getEnSearch().hilightWords);
4077                 return hilight.getDoc();
4078         }
4079
4080         // An error has happended fetching a resource.  let the user know
4081         private void resourceErrorMessage() {
4082                 if (inkNote)
4083                         return;
4084                 QMessageBox.information(this, tr("DOUGH!!!"), tr("Well, this is embarrassing."+
4085                 "\n\nSome attachments or images for this note appear to be missing from my database.\n"+
4086                 "In a perfect world this wouldn't happen, but it has.\n" +
4087                 "It is embarasing when a program like me, designed to save all your\n"+
4088                 "precious data, has a problem finding data.\n\n" +
4089                 "I guess life isn't fair, but I'll survive.  Somehow...\n\n" +
4090                 "In the mean time, I'm not going to let you make changes to this note.\n" +
4091                 "Don't get angry.  I'm doing it to prevent you from messing up\n"+
4092                 "this note on the Evernote servers.  Sorry."+
4093                 "\n\nP.S. You might want to re-synchronize to see if it corrects this problem.\nWho knows, you might get lucky."));
4094                 inkNote = true;
4095 ////            browserWindow.setEnabled(false);
4096                 browserWindow.setReadOnly(true);
4097         }
4098
4099         
4100         
4101         
4102         //**********************************************************
4103         //**********************************************************
4104         //* Timer functions
4105         //**********************************************************
4106         //**********************************************************
4107         // We should now do a sync with Evernote
4108         private void syncTimer() {
4109                 logger.log(logger.EXTREME, "Entering NeverNote.syncTimer()");
4110                 syncRunner.syncNeeded = true;
4111                 syncRunner.disableUploads = Global.disableUploads;
4112                 syncStart();
4113                 logger.log(logger.EXTREME, "Leaving NeverNote.syncTimer()");
4114         }
4115         private void syncStart() {
4116                 logger.log(logger.EXTREME, "Entering NeverNote.syncStart()");
4117                 saveNote();
4118                 if (!syncRunning && Global.isConnected) {
4119                         syncRunner.setConnected(true);
4120                         syncRunner.setKeepRunning(Global.keepRunning);
4121                         syncRunner.syncDeletedContent = Global.synchronizeDeletedContent();
4122                         
4123                         if (syncThreadsReady > 0) {
4124                                 saveNoteIndexWidth();
4125                                 if (syncRunner.addWork("SYNC")) {
4126                                         syncRunning = true;
4127                                         syncRunner.syncNeeded = true;
4128                                         syncThreadsReady--;
4129                                 }                               
4130                         }
4131                 }
4132                 logger.log(logger.EXTREME, "Leaving NeverNote.syncStart");
4133         }
4134         @SuppressWarnings("unused")
4135         private void syncThreadComplete(Boolean refreshNeeded) {
4136                 setMessage("Finalizing Synchronization");
4137                 syncThreadsReady++;
4138                 syncRunning = false;
4139                 syncRunner.syncNeeded = false;
4140                 synchronizeAnimationTimer.stop();
4141                 noteIndexUpdated(true);
4142                 synchronizeButton.setIcon(synchronizeAnimation.get(0));
4143                 saveNote();
4144 //              noteTableView.selectionModel().selectionChanged.disconnect(this, "noteTableSelection()");
4145                 noteTableView.selectionModel().blockSignals(true);
4146                 scrollToGuid(currentNoteGuid);
4147                 noteTableView.selectionModel().blockSignals(false);
4148 //              noteTableView.selectionModel().selectionChanged.connect(this, "noteTableSelection()");
4149 //              indexRunner.setKeepRunning(Global.keepRunning);
4150                 
4151                 // Reload the unindexed table  If the dbthread is dead, we are probably shutting down.
4152                 if (!dbThread.isAlive())
4153                         return;
4154                 listManager.setUnsynchronizedNotes(conn.getNoteTable().getUnsynchronizedGUIDs());
4155                 for (int i=0; i<noteTableView.model.rowCount(); i++) {
4156                         QModelIndex modelIndex =  noteTableView.model.index(i, Global.noteTableGuidPosition);
4157                         if (modelIndex != null) {
4158                         SortedMap<Integer, Object> ix = noteTableView.model.itemData(modelIndex);
4159                                 String tableGuid =  (String)ix.values().toArray()[0];
4160                                 String synch = "true";
4161                                 for (int j=0; j<listManager.getUnsynchronizedNotes().size(); j++) {
4162                                         if (listManager.getUnsynchronizedNotes().get(j).equalsIgnoreCase(tableGuid)) {
4163                                                 synch = "false";
4164                                                 j = listManager.getUnsynchronizedNotes().size();
4165                                         }
4166                                 }
4167                                 noteTableView.model.setData(i, Global.noteTableSynchronizedPosition, synch);
4168                         }
4169                 }       
4170                 refreshEvernoteNote(false);
4171                 scrollToGuid(currentNoteGuid);
4172                 setMessage("Synchronization Complete");
4173                 logger.log(logger.MEDIUM, "Sync complete.");
4174         }   
4175 //      public void setSequenceDate(long t) {
4176 //              Global.setSequenceDate(t);
4177 //      }
4178         public void saveUploadAmount(long t) {
4179                 Global.saveUploadAmount(t);
4180         }
4181 //      public void setUpdateSequenceNumber(int t) {
4182 //              Global.setUpdateSequenceNumber(t);
4183 //      }
4184         public void saveUserInformation(User user) {
4185                 Global.saveUserInformation(user);
4186         }
4187         public void saveEvernoteUpdateCount(int i) {
4188                 Global.saveEvernoteUpdateCount(i);
4189         }
4190         public void refreshLists() {
4191                 logger.log(logger.EXTREME, "Entering NeverNote.refreshLists");
4192                 updateQuotaBar();
4193                 listManager.refreshLists(currentNote, noteDirty, browserWindow.getContent());
4194                 tagIndexUpdated(true);
4195                 notebookIndexUpdated();
4196                 savedSearchIndexUpdated();
4197                 listManager.loadNotesIndex();
4198
4199                 noteTableView.selectionModel().blockSignals(true);
4200         noteIndexUpdated(true);
4201                 noteTableView.selectionModel().blockSignals(false);
4202                 logger.log(logger.EXTREME, "Leaving NeverNote.refreshLists");
4203         }
4204
4205         
4206         @SuppressWarnings("unused")
4207         private void authTimer() {
4208         Calendar cal = Calendar.getInstance();
4209                 
4210         // If we are not connected let's get out of here
4211         if (!Global.isConnected)
4212                 return;
4213                 
4214                 // If this is the first time through, then we need to set this
4215  //             if (syncRunner.authRefreshTime == 0 || cal.getTimeInMillis() > syncRunner.authRefreshTime) 
4216 //                      syncRunner.authRefreshTime = cal.getTimeInMillis();
4217                 
4218 //              long now = new Date().getTime();
4219 //              if (now > Global.authRefreshTime && Global.isConnected) {
4220                         syncRunner.authRefreshNeeded = true;
4221                         syncStart();
4222 //              }
4223         }
4224         @SuppressWarnings("unused")
4225         private void authRefreshComplete(boolean goodSync) {
4226                 logger.log(logger.EXTREME, "Entering NeverNote.authRefreshComplete");
4227                 Global.isConnected = syncRunner.isConnected;
4228                 if (goodSync) {
4229 //                      authTimer.start((int)syncRunner.authTimeRemaining/4);
4230                         authTimer.start(1000*60*15);
4231                         logger.log(logger.LOW, "Authentication token has been renewed");
4232 //                      setMessage("Authentication token has been renewed.");
4233                 } else {
4234                         authTimer.start(1000*60*5);
4235                         logger.log(logger.LOW, "Authentication token renew has failed - retry in 5 minutes.");
4236 //                      setMessage("Authentication token renew has failed - retry in 5 minutes.");
4237                 }
4238                 logger.log(logger.EXTREME, "Leaving NeverNote.authRefreshComplete");
4239         }
4240         
4241         
4242         @SuppressWarnings("unused")
4243         private synchronized void indexTimer() {
4244                 logger.log(logger.EXTREME, "Index timer activated.  Sync running="+syncRunning);
4245                 if (syncRunning) 
4246                         return;
4247                 // Look for any unindexed notes.  We only refresh occasionally 
4248                 // and do one at a time to keep overhead down.
4249                 if (!indexDisabled && indexRunner.getWorkQueueSize() == 0) { 
4250                         List<String> notes = conn.getNoteTable().getNextUnindexed(1);
4251                         String unindexedNote = null;
4252                         if (notes.size() > 0)
4253                                 unindexedNote = notes.get(0);
4254                         if (unindexedNote != null && Global.keepRunning) {
4255                                 indexNoteContent(unindexedNote);
4256                         }
4257                         if (notes.size()>0) {
4258                                 indexTimer.setInterval(100);
4259                                 return;
4260                         }
4261                         List<String> unindexedResources = conn.getNoteTable().noteResourceTable.getNextUnindexed(1);
4262                         if (unindexedResources.size() > 0 && indexRunner.getWorkQueueSize() == 0) {
4263                                 String unindexedResource = unindexedResources.get(0);
4264                                 if (unindexedResource != null && Global.keepRunning) {
4265                                         indexNoteResource(unindexedResource);
4266                                 }
4267                         }
4268                         if (unindexedResources.size() > 0) {
4269                                 indexTimer.setInterval(100);
4270                                 return;
4271                         } else {
4272                                 indexTimer.setInterval(indexTime);
4273                         }
4274                         if (indexRunning) {
4275                                 setMessage("Index completed.");
4276                                 logger.log(logger.LOW, "Indexing has completed.");
4277                                 indexRunning = false;
4278                                 indexTimer.setInterval(indexTime);
4279                         }
4280                 }
4281                 logger.log(logger.EXTREME, "Leaving neverNote index timer");
4282         }
4283         private synchronized void indexNoteContent(String unindexedNote) {
4284                 logger.log(logger.EXTREME, "Entering NeverNote.indexNoteContent()");
4285                 logger.log(logger.MEDIUM, "Unindexed Note found: "+unindexedNote);
4286                 indexRunner.setIndexType(indexRunner.CONTENT);
4287                 indexRunner.addWork("CONTENT "+unindexedNote);
4288                 if (!indexRunning) {
4289                         setMessage("Indexing notes.");
4290                         logger.log(logger.LOW, "Beginning to index note contents.");
4291                         indexRunning = true;
4292                 }
4293                 logger.log(logger.EXTREME, "Leaving NeverNote.indexNoteContent()");
4294         }
4295         private synchronized void indexNoteResource(String unindexedResource) {
4296                 logger.log(logger.EXTREME, "Leaving NeverNote.indexNoteResource()");
4297                 indexRunner.addWork(new String("RESOURCE "+unindexedResource));
4298                 if (!indexRunning) {
4299                         setMessage("Indexing notes.");
4300                         indexRunning = true;
4301                 }
4302                 logger.log(logger.EXTREME, "Leaving NeverNote.indexNoteResource()");
4303         }
4304         @SuppressWarnings("unused")
4305         private void indexThreadComplete(String guid) {
4306                 logger.log(logger.MEDIUM, "Index complete for "+guid);
4307         }
4308         @SuppressWarnings("unused")
4309         private synchronized void toggleNoteIndexing() {
4310                 logger.log(logger.HIGH, "Entering NeverNote.toggleIndexing");
4311                 indexDisabled = !indexDisabled;
4312                 if (!indexDisabled)
4313                         setMessage("Indexing is now enabled.");
4314                 else
4315                         setMessage("Indexing is now disabled.");
4316                 menuBar.disableIndexing.setChecked(indexDisabled);
4317         logger.log(logger.HIGH, "Leaving NeverNote.toggleIndexing");
4318     }  
4319         
4320         @SuppressWarnings("unused")
4321         private void threadMonitorCheck() {
4322                 int MAX=3;
4323                 
4324                 
4325                 boolean alive;
4326                 alive = listManager.threadCheck(Global.tagCounterThreadId);
4327                 if (!alive) {
4328                         tagDeadCount++;
4329                         if (tagDeadCount > MAX)
4330                                 QMessageBox.information(this, "A thread his died.", "It appears as the tag counter thread has died.  I recommend "+
4331                                 "checking stopping NeverNote, saving the logs for later viewing, and restarting.  Sorry.");
4332                 } else
4333                         tagDeadCount=0;
4334                 
4335                 alive = listManager.threadCheck(Global.notebookCounterThreadId);
4336                 if (!alive) {
4337                         notebookThreadDeadCount++;
4338                         QMessageBox.information(this, "A thread his died.", "It appears as the notebook counter thread has died.  I recommend "+
4339                         "checking stopping NeverNote, saving the logs for later viewing, and restarting.  Sorry.");
4340                 } else
4341                         notebookThreadDeadCount=0;
4342                 
4343                 alive = listManager.threadCheck(Global.trashCounterThreadId);
4344                 if (!alive) {
4345                         trashDeadCount++;
4346                         QMessageBox.information(this, "A thread his died.", "It appears as the trash counter thread has died.  I recommend "+
4347                         "checking stopping NeverNote, saving the logs for later viewing, and restarting.  Sorry.");
4348                 } else
4349                         trashDeadCount = 0;
4350
4351                 alive = listManager.threadCheck(Global.saveThreadId);
4352                 if (!alive) {
4353                         saveThreadDeadCount++;
4354                         QMessageBox.information(this, "A thread his died.", "It appears as the note saver thread has died.  I recommend "+
4355                         "checking stopping NeverNote, saving the logs for later viewing, and restarting.  Sorry.");
4356                 } else
4357                         saveThreadDeadCount=0;
4358
4359                 if (!dbThread.isAlive()) {
4360                         dbThreadDeadCount++;
4361                         QMessageBox.information(this, "A thread his died.", "It appears as the database thread has died.  I recommend "+
4362                         "checking stopping NeverNote, saving the logs for later viewing, and restarting.  Sorry.");
4363                 } else
4364                         dbThreadDeadCount=0;
4365
4366                 if (!syncThread.isAlive()) {
4367                         syncThreadDeadCount++;
4368                         QMessageBox.information(this, "A thread his died.", "It appears as the synchronization thread has died.  I recommend "+
4369                         "checking stopping NeverNote, saving the logs for later viewing, and restarting.  Sorry.");
4370                 } else
4371                         syncThreadDeadCount=0;
4372
4373                 if (!indexThread.isAlive()) {
4374                         indexThreadDeadCount++;
4375                         QMessageBox.information(this, "A thread his died.", "It appears as the index thread has died.  I recommend "+
4376                         "checking stopping NeverNote, saving the logs for later viewing, and restarting.  Sorry.");
4377                 } else
4378                         indexThreadDeadCount=0;
4379
4380                 
4381         }
4382
4383         
4384         
4385         //**************************************************
4386         //* Backup & Restore
4387         //**************************************************
4388         @SuppressWarnings("unused")
4389         private void databaseBackup() {
4390                 QFileDialog fd = new QFileDialog(this);
4391                 fd.setFileMode(FileMode.AnyFile);
4392                 fd.setConfirmOverwrite(true);
4393                 fd.setWindowTitle("Backup Database");
4394                 fd.setFilter(tr("NeverNote Export (*.nnex);;All Files (*.*)"));
4395                 fd.setAcceptMode(AcceptMode.AcceptSave);
4396                 fd.setDirectory(System.getProperty("user.home"));
4397                 if (fd.exec() == 0 || fd.selectedFiles().size() == 0) {
4398                         return;
4399                 }
4400                 
4401                 
4402         waitCursor(true);
4403         setMessage("Backing up database");
4404         saveNote();
4405 //      conn.backupDatabase(Global.getUpdateSequenceNumber(), Global.getSequenceDate());
4406         
4407         ExportData noteWriter = new ExportData(conn, true);
4408         String fileName = fd.selectedFiles().get(0);
4409
4410         if (!fileName.endsWith(".nnex"))
4411                 fileName = fileName +".nnex";
4412         noteWriter.exportData(fileName);
4413         setMessage("Database backup completed.");
4414  
4415
4416         waitCursor(false);
4417         }
4418         @SuppressWarnings("unused")
4419         private void databaseRestore() {
4420                 if (QMessageBox.question(this, "Confirmation",
4421                                 "This is used to restore a database from backups.\n" +
4422                                 "It is HIGHLY recommened that this only be used to populate\n" +
4423                                 "an empty database.  Restoring into a database that\n already has data" +
4424                                 " can cause problems.\n\nAre you sure you want to continue?",
4425                                 QMessageBox.StandardButton.Yes, 
4426                                 QMessageBox.StandardButton.No)==StandardButton.No.value()) {
4427                                         return;
4428                                 }
4429                 
4430                 
4431                 QFileDialog fd = new QFileDialog(this);
4432                 fd.setFileMode(FileMode.ExistingFile);
4433                 fd.setConfirmOverwrite(true);
4434                 fd.setWindowTitle("Restore Database");
4435                 fd.setFilter(tr("NeverNote Export (*.nnex);;All Files (*.*)"));
4436                 fd.setAcceptMode(AcceptMode.AcceptOpen);
4437                 fd.setDirectory(System.getProperty("user.home"));
4438                 if (fd.exec() == 0 || fd.selectedFiles().size() == 0) {
4439                         return;
4440                 }
4441                 
4442                 
4443                 waitCursor(true);
4444                 setMessage("Restoring database");
4445         ImportData noteReader = new ImportData(conn, true);
4446         noteReader.importData(fd.selectedFiles().get(0));
4447         
4448         if (noteReader.lastError != 0) {
4449                 setMessage(noteReader.getErrorMessage());
4450                 logger.log(logger.LOW, "Restore problem: " +noteReader.lastError);
4451                 waitCursor(false);
4452                 return;
4453         }
4454         
4455         listManager.loadNoteTitleColors();
4456         refreshLists();
4457         refreshEvernoteNote(true);
4458         setMessage("Database has been restored.");
4459         waitCursor(false);
4460         }
4461         @SuppressWarnings("unused")
4462         private void exportNotes() {
4463                 QFileDialog fd = new QFileDialog(this);
4464                 fd.setFileMode(FileMode.AnyFile);
4465                 fd.setConfirmOverwrite(true);
4466                 fd.setWindowTitle("Backup Database");
4467                 fd.setFilter(tr("NeverNote Export (*.nnex);;All Files (*.*)"));
4468                 fd.setAcceptMode(AcceptMode.AcceptSave);
4469                 fd.setDirectory(System.getProperty("user.home"));
4470                 if (fd.exec() == 0 || fd.selectedFiles().size() == 0) {
4471                         return;
4472                 }
4473                 
4474                 
4475         waitCursor(true);
4476         setMessage("Exporting Notes");
4477         saveNote();
4478         
4479                 if (selectedNoteGUIDs.size() == 0 && !currentNoteGuid.equals("")) 
4480                         selectedNoteGUIDs.add(currentNoteGuid);
4481                 
4482         ExportData noteWriter = new ExportData(conn, false, selectedNoteGUIDs);
4483         String fileName = fd.selectedFiles().get(0);
4484
4485         if (!fileName.endsWith(".nnex"))
4486                 fileName = fileName +".nnex";
4487         noteWriter.exportData(fileName);
4488         setMessage("Export completed.");
4489  
4490
4491         waitCursor(false);
4492                 
4493         }
4494         @SuppressWarnings("unused")
4495         private void importNotes() {
4496                 QFileDialog fd = new QFileDialog(this);
4497                 fd.setFileMode(FileMode.ExistingFile);
4498                 fd.setConfirmOverwrite(true);
4499                 fd.setWindowTitle("Import Notes");
4500                 fd.setFilter(tr("NeverNote Export (*.nnex);;All Files (*.*)"));
4501                 fd.setAcceptMode(AcceptMode.AcceptOpen);
4502                 fd.setDirectory(System.getProperty("user.home"));
4503                 if (fd.exec() == 0 || fd.selectedFiles().size() == 0) {
4504                         return;
4505                 }
4506                 
4507                 
4508         waitCursor(true);
4509         setMessage("Importing Notes");
4510         saveNote();
4511         
4512                 if (selectedNoteGUIDs.size() == 0 && !currentNoteGuid.equals("")) 
4513                         selectedNoteGUIDs.add(currentNoteGuid);
4514                 
4515         ImportData noteReader = new ImportData(conn, false);
4516         String fileName = fd.selectedFiles().get(0);
4517
4518         if (!fileName.endsWith(".nnex"))
4519                 fileName = fileName +".nnex";
4520         if (selectedNotebookGUIDs != null && selectedNotebookGUIDs.size() > 0) 
4521                 noteReader.setNotebookGuid(selectedNotebookGUIDs.get(0));
4522         else
4523                 noteReader.setNotebookGuid(listManager.getNotebookIndex().get(0).getGuid());
4524   
4525         noteReader.importData(fileName);
4526         
4527         if (noteReader.lastError != 0) {
4528                 setMessage(noteReader.getErrorMessage());
4529                 logger.log(logger.LOW, "Import problem: " +noteReader.lastError);
4530                 waitCursor(false);
4531                 return;
4532         }
4533         
4534         listManager.loadNoteTitleColors();
4535         refreshLists();
4536         refreshEvernoteNote(false);
4537         setMessage("Notes have been imported.");
4538         waitCursor(false);
4539         
4540         setMessage("Import completed.");
4541  
4542
4543         waitCursor(false);
4544                 
4545         }
4546         
4547         //**************************************************
4548         //* Duplicate a note 
4549         //**************************************************
4550         @SuppressWarnings("unused")
4551         private void duplicateNote() {
4552                 saveNote();
4553                 duplicateNote(currentNoteGuid);
4554         }
4555
4556         
4557         
4558         //**************************************************
4559         //* Folder Imports
4560         //**************************************************
4561         public void setupFolderImports() {
4562                 List<WatchFolderRecord> records = conn.getWatchFolderTable().getAll();
4563                 
4564                 if (importKeepWatcher == null)
4565                         importKeepWatcher = new QFileSystemWatcher();
4566                 if (importDeleteWatcher == null) {
4567                         importDeleteWatcher = new QFileSystemWatcher();
4568                         for (int i=0; i<records.size(); i++) {
4569                                 if (!records.get(i).keep)
4570                                         folderImportDelete(records.get(i).folder); 
4571                         }
4572                 }
4573
4574                                 
4575                 
4576 //              importKeepWatcher.addPath(records.get(i).folder.replace('\\', '/'));
4577                 for (int i=0; i<records.size(); i++) {
4578                         if (records.get(i).keep) 
4579                                 importKeepWatcher.addPath(records.get(i).folder);
4580                         else
4581                                 importDeleteWatcher.addPath(records.get(i).folder);
4582                 }
4583                 
4584                 importKeepWatcher.directoryChanged.connect(this, "folderImportKeep(String)");
4585                 importDeleteWatcher.directoryChanged.connect(this, "folderImportDelete(String)");
4586                 
4587                 // Look at the files already there so we don't import them again if a new file is created
4588                 if (importedFiles == null) {
4589                         importedFiles = new ArrayList<String>();
4590                         for (int j=0; j<records.size(); j++) {
4591                                 QDir dir = new QDir(records.get(j).folder);
4592                                 List<QFileInfo> list = dir.entryInfoList();
4593                                 for (int k=0; k<list.size(); k++) {
4594                                         if (list.get(k).isFile())
4595                                                 importedFiles.add(list.get(k).absoluteFilePath());
4596                                 }
4597                         }
4598                 }
4599         }
4600         public void folderImport() {
4601                 List<WatchFolderRecord> recs = conn.getWatchFolderTable().getAll();
4602                 WatchFolder dialog = new WatchFolder(recs, listManager.getNotebookIndex());
4603                 dialog.exec();
4604                 if (!dialog.okClicked())
4605                         return;
4606                 
4607                 // We have some sort of update.
4608                 if (importKeepWatcher.directories().size() > 0)
4609                         importKeepWatcher.removePaths(importKeepWatcher.directories());
4610                 if (importDeleteWatcher.directories().size() > 0)
4611                         importDeleteWatcher.removePaths(importDeleteWatcher.directories());
4612                 
4613                 conn.getWatchFolderTable().expungeAll();
4614                 // Start building from the table
4615                 for (int i=0; i<dialog.table.rowCount(); i++) {
4616                         QTableWidgetItem item = dialog.table.item(i, 0);
4617                         String dir = item.text();
4618                         item = dialog.table.item(i, 1);
4619                         String notebook = item.text();
4620                         item = dialog.table.item(i, 2);
4621                         boolean keep;
4622                         if (item.text().equalsIgnoreCase("Keep"))
4623                                 keep = true;
4624                         else
4625                                 keep = false;
4626                         
4627                         String guid = conn.getNotebookTable().findNotebookByName(notebook);
4628                         conn.getWatchFolderTable().addWatchFolder(dir, guid, keep, 0);
4629                 }
4630                 setupFolderImports();
4631         }
4632         
4633         public void folderImportKeep(String dirName) throws NoSuchAlgorithmException {
4634                 
4635                 String whichOS = System.getProperty("os.name");
4636                 if (whichOS.contains("Windows")) 
4637                         dirName = dirName.replace('/','\\');
4638                 
4639                 FileImporter importer = new FileImporter(logger, conn);
4640                 
4641                 QDir dir = new QDir(dirName);
4642                 List<QFileInfo> list = dir.entryInfoList();
4643                 String notebook = conn.getWatchFolderTable().getNotebook(dirName);
4644
4645                 for (int i=0; i<list.size(); i++){
4646                         
4647                         boolean redundant = false;
4648                         // Check if we've already imported this one or if it existed before
4649                         for (int j=0; j<importedFiles.size(); j++) {
4650                                 if (importedFiles.get(j).equals(list.get(i).absoluteFilePath()))
4651                                         redundant = true;
4652                         }
4653                         
4654                         if (!redundant) {
4655                                 importer.setFileInfo(list.get(i));
4656                                 importer.setFileName(list.get(i).absoluteFilePath());
4657                         
4658                         
4659                                 if (list.get(i).isFile() && importer.isValidType()) {
4660                         
4661                                         if (!importer.importFile()) {
4662                                                 // If we can't get to the file, it is probably locked.  We'll try again later.
4663                                                 logger.log(logger.LOW, "Unable to save externally edited file.  Saving for later.");
4664                                                 importFilesKeep.add(list.get(i).absoluteFilePath());
4665                                                 return;
4666                                         }
4667
4668                                         Note newNote = importer.getNote();
4669                                         newNote.setNotebookGuid(notebook);
4670                                         newNote.setTitle(dir.at(i));
4671                                         listManager.addNote(newNote);
4672                                         conn.getNoteTable().addNote(newNote, true);
4673                                         listManager.getUnsynchronizedNotes().add(newNote.getGuid());
4674                                         noteTableView.insertRow(listManager, newNote, true, -1);
4675                                         listManager.updateNoteContent(newNote.getGuid(), importer.getNoteContent());
4676                                         listManager.countNotebookResults(listManager.getNoteIndex());
4677                                         importedFiles.add(list.get(i).absoluteFilePath());
4678                                 }
4679                         }
4680                 }
4681         
4682         
4683         }
4684         
4685         public void folderImportDelete(String dirName) {
4686                 
4687                 String whichOS = System.getProperty("os.name");
4688                 if (whichOS.contains("Windows")) 
4689                         dirName = dirName.replace('/','\\');
4690                 
4691                 FileImporter importer = new FileImporter(logger, conn);
4692                 QDir dir = new QDir(dirName);
4693                 List<QFileInfo> list = dir.entryInfoList();
4694                 String notebook = conn.getWatchFolderTable().getNotebook(dirName);
4695                 
4696                 for (int i=0; i<list.size(); i++){
4697                         importer.setFileInfo(list.get(i));
4698                         importer.setFileName(list.get(i).absoluteFilePath());
4699                         
4700                         if (list.get(i).isFile() && importer.isValidType()) {
4701                 
4702                                 if (!importer.importFile()) {
4703                                         // If we can't get to the file, it is probably locked.  We'll try again later.
4704                                         logger.log(logger.LOW, "Unable to save externally edited file.  Saving for later.");
4705                                         importFilesKeep.add(list.get(i).absoluteFilePath());
4706                                         return;
4707                                 }
4708                 
4709                                 Note newNote = importer.getNote();
4710                                 newNote.setNotebookGuid(notebook);
4711                                 newNote.setTitle(dir.at(i));
4712                                 listManager.addNote(newNote);
4713                                 conn.getNoteTable().addNote(newNote, true);
4714                                 listManager.getUnsynchronizedNotes().add(newNote.getGuid());
4715                                 noteTableView.insertRow(listManager, newNote, true, -1);
4716                                 listManager.updateNoteContent(newNote.getGuid(), importer.getNoteContent());
4717                                 listManager.countNotebookResults(listManager.getNoteIndex());
4718                                 dir.remove(dir.at(i));
4719                         }
4720                 }
4721         }
4722         
4723         
4724         //**************************************************
4725         //* External events
4726         //**************************************************
4727         private void externalFileEdited(String fileName) throws NoSuchAlgorithmException {
4728                 logger.log(logger.HIGH, "Entering exernalFileEdited");
4729                 
4730                 String dPath = Global.getDirectoryPath() + "res/";
4731                 dPath = dPath.replace('\\', '/');
4732                 String name = fileName.replace(dPath, "");
4733                 int pos = name.lastIndexOf('.');
4734                 String guid = name;
4735                 if (pos > -1) {
4736                         guid = guid.substring(0,pos);
4737                 }
4738                 pos = name.lastIndexOf(Global.attachmentNameDelimeter);
4739                 if (pos > -1) {
4740                         guid = name.substring(0, pos);
4741                 }
4742                 
4743                 QFile file = new QFile(fileName);
4744         if (!file.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.ReadOnly))) {
4745                 // If we can't get to the file, it is probably locked.  We'll try again later.
4746                 logger.log(logger.LOW, "Unable to save externally edited file.  Saving for later.");
4747                 externalFiles.add(fileName);
4748                 return;
4749                 }
4750                 QByteArray binData = file.readAll();
4751         file.close();
4752         if (binData.size() == 0) {
4753                 // If we can't get to the file, it is probably locked.  We'll try again later.
4754                 logger.log(logger.LOW, "Unable to save externally edited file.  Saving for later.");
4755                 externalFiles.add(fileName);
4756                 return;
4757         }
4758         
4759         Resource r = conn.getNoteTable().noteResourceTable.getNoteResource(guid, true);
4760         if (r==null)
4761                 r = conn.getNoteTable().noteResourceTable.getNoteResource(Global.resourceMap.get(guid), true);
4762         if (r == null || r.getData() == null || r.getData().getBody() == null)
4763                 return;
4764         String oldHash = Global.byteArrayToHexString(r.getData().getBodyHash());
4765         MessageDigest md = MessageDigest.getInstance("MD5");
4766                 md.update(binData.toByteArray());
4767                 byte[] hash = md.digest();
4768         String newHash = Global.byteArrayToHexString(hash);
4769         if (r.getNoteGuid().equalsIgnoreCase(currentNoteGuid)) {
4770                 updateResourceContentHash(r.getGuid(), oldHash, newHash);
4771         }
4772         conn.getNoteTable().updateResourceContentHash(r.getNoteGuid(), oldHash, newHash);
4773         Data data = r.getData();
4774         data.setBody(binData.toByteArray());
4775         data.setBodyHash(hash);
4776         logger.log(logger.LOW, "externalFileEdited: " +data.getSize() +" bytes");
4777         r.setData(data);
4778         conn.getNoteTable().noteResourceTable.updateNoteResource(r,true);
4779         
4780         if (r.getNoteGuid().equals(currentNoteGuid)) {
4781                         QWebSettings.setMaximumPagesInCache(0);
4782                         QWebSettings.setObjectCacheCapacities(0, 0, 0);
4783                         refreshEvernoteNote(true);
4784                         browserWindow.getBrowser().triggerPageAction(WebAction.Reload);
4785         }
4786         
4787                 logger.log(logger.HIGH, "Exiting externalFielEdited");
4788         }
4789         // This is a timer event that tries to save any external files that were edited.  This
4790         // is only needed if we couldn't save a file earlier.
4791         public void externalFileEditedSaver() {
4792                 for (int i=externalFiles.size()-1; i>=0; i--) {
4793                         try {
4794                                 logger.log(logger.MEDIUM, "Trying to save " +externalFiles.get(i));
4795                                 externalFileEdited(externalFiles.get(i));
4796                                 externalFiles.remove(i);
4797                         } catch (NoSuchAlgorithmException e) {e.printStackTrace();}
4798                 }
4799                 for (int i=0; i<importFilesKeep.size(); i++) {
4800                         try {
4801                                 logger.log(logger.MEDIUM, "Trying to save " +importFilesKeep.get(i));
4802                                 folderImportKeep(importFilesKeep.get(i));
4803                                 importFilesKeep.remove(i);
4804                         } catch (NoSuchAlgorithmException e) {e.printStackTrace();}
4805                 }
4806                 for (int i=0; i<importFilesDelete.size(); i++) {
4807                         logger.log(logger.MEDIUM, "Trying to save " +importFilesDelete.get(i));
4808                         folderImportDelete(importFilesDelete.get(i));
4809                         importFilesDelete.remove(i);
4810                 }
4811         }
4812         
4813         
4814         
4815         
4816         // If an attachment on the current note was edited, we need to update the current notes's hash
4817         // Update a note content's hash.  This happens if a resource is edited outside of NN
4818         public void updateResourceContentHash(String guid, String oldHash, String newHash) {
4819                 int position = browserWindow.getContent().indexOf("en-tag=\"en-media\" guid=\""+guid+"\" type=");
4820                 int endPos;
4821                 for (;position>-1;) {
4822                         endPos = browserWindow.getContent().indexOf(">", position+1);
4823                         String oldSegment = browserWindow.getContent().substring(position,endPos);
4824                         int hashPos = oldSegment.indexOf("hash=\"");
4825                         int hashEnd = oldSegment.indexOf("\"", hashPos+7);
4826                         String hash = oldSegment.substring(hashPos+6, hashEnd);
4827                         if (hash.equalsIgnoreCase(oldHash)) {
4828                                 String newSegment = oldSegment.replace(oldHash, newHash);
4829                                 String content = browserWindow.getContent().substring(0,position) +
4830                                                  newSegment +
4831                                                  browserWindow.getContent().substring(endPos);
4832                                 browserWindow.getBrowser().setContent(new QByteArray(content));;
4833                         }
4834                         
4835                         position = browserWindow.getContent().indexOf("en-tag=\"en-media\" guid=\""+guid+"\" type=", position+1);
4836                 }
4837         }
4838
4839
4840         
4841         
4842         //*************************************************
4843         //* Check database userid & passwords
4844         //*************************************************
4845         public boolean databaseCheck(String url,String userid, String userPassword, String cypherPassword) {
4846                         Connection connection;
4847                         
4848                         try {
4849                                 Class.forName("org.h2.Driver");
4850                         } catch (ClassNotFoundException e1) {
4851                                 e1.printStackTrace();
4852                                 System.exit(16);
4853                         }
4854
4855                         try {
4856                                 String passwordString = null;
4857                                 if (cypherPassword==null || cypherPassword.trim().equals(""))
4858                                         passwordString = userPassword;
4859                                 else
4860                                         passwordString = cypherPassword+" "+userPassword;
4861                                 connection = DriverManager.getConnection(url,userid,passwordString);
4862                         } catch (SQLException e) {
4863                                 return false;
4864                         }
4865                         try {
4866                                 connection.close();
4867                         } catch (SQLException e) {
4868                                 e.printStackTrace();
4869                         }
4870                         return true;
4871         }
4872
4873 }