OSDN Git Service

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