OSDN Git Service

- Added the ability to do special refreshes for debugging. - Added the ability to...
[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.net.Authenticator;
25 import java.net.PasswordAuthentication;
26 import java.security.MessageDigest;
27 import java.security.NoSuchAlgorithmException;
28 import java.sql.Connection;
29 import java.sql.DriverManager;
30 import java.sql.SQLException;
31 import java.sql.Statement;
32 import java.text.SimpleDateFormat;
33 import java.util.ArrayList;
34 import java.util.Calendar;
35 import java.util.Collection;
36 import java.util.Collections;
37 import java.util.Comparator;
38 import java.util.Date;
39 import java.util.GregorianCalendar;
40 import java.util.HashMap;
41 import java.util.Iterator;
42 import java.util.List;
43 import java.util.SortedMap;
44 import java.util.Vector;
45
46 import org.apache.log4j.Level;
47 import org.apache.log4j.Logger;
48 import org.apache.thrift.TException;
49 import org.h2.tools.ChangeFileEncryption;
50
51 import com.evernote.edam.error.EDAMNotFoundException;
52 import com.evernote.edam.error.EDAMSystemException;
53 import com.evernote.edam.error.EDAMUserException;
54 import com.evernote.edam.notestore.NoteFilter;
55 import com.evernote.edam.notestore.NoteVersionId;
56 import com.evernote.edam.type.Data;
57 import com.evernote.edam.type.LinkedNotebook;
58 import com.evernote.edam.type.Note;
59 import com.evernote.edam.type.NoteAttributes;
60 import com.evernote.edam.type.Notebook;
61 import com.evernote.edam.type.Publishing;
62 import com.evernote.edam.type.QueryFormat;
63 import com.evernote.edam.type.Resource;
64 import com.evernote.edam.type.SavedSearch;
65 import com.evernote.edam.type.Tag;
66 import com.evernote.edam.type.User;
67 import com.trolltech.qt.QThread;
68 import com.trolltech.qt.core.QByteArray;
69 import com.trolltech.qt.core.QDateTime;
70 import com.trolltech.qt.core.QDir;
71 import com.trolltech.qt.core.QEvent;
72 import com.trolltech.qt.core.QFile;
73 import com.trolltech.qt.core.QFileInfo;
74 import com.trolltech.qt.core.QFileSystemWatcher;
75 import com.trolltech.qt.core.QIODevice;
76 import com.trolltech.qt.core.QIODevice.OpenModeFlag;
77 import com.trolltech.qt.core.QLocale;
78 import com.trolltech.qt.core.QModelIndex;
79 import com.trolltech.qt.core.QSize;
80 import com.trolltech.qt.core.QTemporaryFile;
81 import com.trolltech.qt.core.QTextCodec;
82 import com.trolltech.qt.core.QThreadPool;
83 import com.trolltech.qt.core.QTimer;
84 import com.trolltech.qt.core.QTranslator;
85 import com.trolltech.qt.core.QUrl;
86 import com.trolltech.qt.core.Qt;
87 import com.trolltech.qt.core.Qt.BGMode;
88 import com.trolltech.qt.core.Qt.ItemDataRole;
89 import com.trolltech.qt.core.Qt.SortOrder;
90 import com.trolltech.qt.core.Qt.WidgetAttribute;
91 import com.trolltech.qt.gui.QAbstractItemView;
92 import com.trolltech.qt.gui.QAbstractItemView.ScrollHint;
93 import com.trolltech.qt.gui.QAction;
94 import com.trolltech.qt.gui.QApplication;
95 import com.trolltech.qt.gui.QCloseEvent;
96 import com.trolltech.qt.gui.QColor;
97 import com.trolltech.qt.gui.QComboBox;
98 import com.trolltech.qt.gui.QCursor;
99 import com.trolltech.qt.gui.QDesktopServices;
100 import com.trolltech.qt.gui.QDialog;
101 import com.trolltech.qt.gui.QFileDialog;
102 import com.trolltech.qt.gui.QFileDialog.AcceptMode;
103 import com.trolltech.qt.gui.QFileDialog.FileMode;
104 import com.trolltech.qt.gui.QGridLayout;
105 import com.trolltech.qt.gui.QHBoxLayout;
106 import com.trolltech.qt.gui.QIcon;
107 import com.trolltech.qt.gui.QImage;
108 import com.trolltech.qt.gui.QKeySequence;
109 import com.trolltech.qt.gui.QLabel;
110 import com.trolltech.qt.gui.QMainWindow;
111 import com.trolltech.qt.gui.QMenu;
112 import com.trolltech.qt.gui.QMessageBox;
113 import com.trolltech.qt.gui.QMessageBox.StandardButton;
114 import com.trolltech.qt.gui.QPainter;
115 import com.trolltech.qt.gui.QPalette.ColorRole;
116 import com.trolltech.qt.gui.QPixmap;
117 import com.trolltech.qt.gui.QPrintDialog;
118 import com.trolltech.qt.gui.QPrinter;
119 import com.trolltech.qt.gui.QShortcut;
120 import com.trolltech.qt.gui.QSizePolicy;
121 import com.trolltech.qt.gui.QSizePolicy.Policy;
122 import com.trolltech.qt.gui.QSpinBox;
123 import com.trolltech.qt.gui.QSplashScreen;
124 import com.trolltech.qt.gui.QSplitter;
125 import com.trolltech.qt.gui.QStatusBar;
126 import com.trolltech.qt.gui.QSystemTrayIcon;
127 import com.trolltech.qt.gui.QTableWidgetItem;
128 import com.trolltech.qt.gui.QTextEdit;
129 import com.trolltech.qt.gui.QToolBar;
130 import com.trolltech.qt.gui.QTreeWidgetItem;
131 import com.trolltech.qt.network.QNetworkAccessManager;
132 import com.trolltech.qt.network.QNetworkReply;
133 import com.trolltech.qt.network.QNetworkRequest;
134 import com.trolltech.qt.webkit.QWebPage.WebAction;
135 import com.trolltech.qt.webkit.QWebSettings;
136
137 import cx.fbn.nevernote.config.InitializationException;
138 import cx.fbn.nevernote.config.StartupConfig;
139 import cx.fbn.nevernote.dialog.AccountDialog;
140 import cx.fbn.nevernote.dialog.ConfigDialog;
141 import cx.fbn.nevernote.dialog.DBEncryptDialog;
142 import cx.fbn.nevernote.dialog.DatabaseLoginDialog;
143 import cx.fbn.nevernote.dialog.DatabaseStatus;
144 import cx.fbn.nevernote.dialog.FindDialog;
145 import cx.fbn.nevernote.dialog.IgnoreSync;
146 import cx.fbn.nevernote.dialog.LogFileDialog;
147 import cx.fbn.nevernote.dialog.LoginDialog;
148 import cx.fbn.nevernote.dialog.NotebookArchive;
149 import cx.fbn.nevernote.dialog.NotebookEdit;
150 import cx.fbn.nevernote.dialog.OnlineNoteHistory;
151 import cx.fbn.nevernote.dialog.PublishNotebook;
152 import cx.fbn.nevernote.dialog.SavedSearchEdit;
153 import cx.fbn.nevernote.dialog.SetIcon;
154 import cx.fbn.nevernote.dialog.ShareNotebook;
155 import cx.fbn.nevernote.dialog.StackNotebook;
156 import cx.fbn.nevernote.dialog.TagEdit;
157 import cx.fbn.nevernote.dialog.TagMerge;
158 import cx.fbn.nevernote.dialog.ThumbnailViewer;
159 import cx.fbn.nevernote.dialog.UpgradeAvailableDialog;
160 import cx.fbn.nevernote.dialog.WatchFolder;
161 import cx.fbn.nevernote.filters.FilterEditorNotebooks;
162 import cx.fbn.nevernote.filters.FilterEditorTags;
163 import cx.fbn.nevernote.gui.AttributeTreeWidget;
164 import cx.fbn.nevernote.gui.BrowserWindow;
165 import cx.fbn.nevernote.gui.DateAttributeFilterTable;
166 import cx.fbn.nevernote.gui.ExternalBrowse;
167 import cx.fbn.nevernote.gui.MainMenuBar;
168 import cx.fbn.nevernote.gui.NotebookTreeWidget;
169 import cx.fbn.nevernote.gui.SavedSearchTreeWidget;
170 import cx.fbn.nevernote.gui.SearchPanel;
171 import cx.fbn.nevernote.gui.TableView;
172 import cx.fbn.nevernote.gui.TagTreeWidget;
173 import cx.fbn.nevernote.gui.Thumbnailer;
174 import cx.fbn.nevernote.gui.TrashTreeWidget;
175 import cx.fbn.nevernote.gui.controls.QuotaProgressBar;
176 import cx.fbn.nevernote.sql.DatabaseConnection;
177 import cx.fbn.nevernote.sql.WatchFolderRecord;
178 import cx.fbn.nevernote.threads.IndexRunner;
179 import cx.fbn.nevernote.threads.SyncRunner;
180 import cx.fbn.nevernote.threads.ThumbnailRunner;
181 import cx.fbn.nevernote.utilities.AESEncrypter;
182 import cx.fbn.nevernote.utilities.ApplicationLogger;
183 import cx.fbn.nevernote.utilities.FileImporter;
184 import cx.fbn.nevernote.utilities.FileUtils;
185 import cx.fbn.nevernote.utilities.ListManager;
186 import cx.fbn.nevernote.utilities.SyncTimes;
187 import cx.fbn.nevernote.xml.ExportData;
188 import cx.fbn.nevernote.xml.ImportData;
189 import cx.fbn.nevernote.xml.NoteFormatter;
190
191
192 public class NeverNote extends QMainWindow{
193         
194         QStatusBar                              statusBar;                                      // Application status bar
195         
196         DatabaseConnection              conn;
197         
198         MainMenuBar                             menuBar;                                        // Main menu bar
199         FindDialog                              find;                                           // Text search in note dialog
200         List<String>                    emitLog;                                        // Messages displayed in the status bar;
201         QSystemTrayIcon                 trayIcon;                                       // little tray icon
202         QMenu                                   trayMenu;                                       // System tray menu
203         QAction                                 trayExitAction;                         // Exit the application
204         QAction                                 trayShowAction;                         // toggle the show/hide action          
205         QAction                                 trayAddNoteAction;                      // Add a note from the system tray
206         QNetworkAccessManager   versionChecker;                         // Used when checking for new versions
207         
208     NotebookTreeWidget          notebookTree;                           // List of notebooks
209     AttributeTreeWidget         attributeTree;                          // List of note attributes
210     TagTreeWidget                       tagTree;                                        // list of user created tags
211     SavedSearchTreeWidget       savedSearchTree;                        // list of saved searches
212     TrashTreeWidget                     trashTree;                                      // Trashcan
213     TableView                           noteTableView;                          //      List of notes (the widget).
214
215     public BrowserWindow        browserWindow;                          // Window containing browser & labels
216     public QToolBar             toolBar;                                        // The tool bar under the menu
217     QComboBox                           searchField;                            // search filter bar on the toolbar;
218     QShortcut                           searchShortcut;                         // Shortcut to search bar
219     boolean                                     searchPerformed = false;        // Search was done?
220     QuotaProgressBar            quotaBar;                                       // The current quota usage
221     
222     ApplicationLogger           logger;
223     List<String>                        selectedNotebookGUIDs;          // List of notebook GUIDs
224     List<String>                        selectedTagGUIDs;                       // List of selected tag GUIDs
225     List<String>                        selectedNoteGUIDs;                      // List of selected notes
226     String                                      selectedSavedSearchGUID;        // Currently selected saved searches
227     private final HashMap<String, ExternalBrowse>       externalWindows;        // Notes being edited by an external window;
228     
229     NoteFilter                          filter;                                         // Note filter
230     String                                      currentNoteGuid;                        // GUID of the current note 
231     Note                                        currentNote;                            // The currently viewed note
232     boolean                                     noteDirty;                                      // Has the note been changed?
233     boolean                             inkNote;                   // if this is an ink note, it is read only
234     boolean                                     readOnly;                                       // Is this note read-only?
235         
236   
237     ListManager                         listManager;                                    // DB runnable task
238     
239     List<QTemporaryFile>        tempFiles;                                      // Array of temporary files;
240     
241     QTimer                                      indexTimer;                                     // timer to start the index thread
242     IndexRunner                         indexRunner;                            // thread to index notes
243     QThread                                     indexThread;
244     
245     QTimer                                      syncTimer;                                      // Sync on an interval
246     QTimer                                      syncDelayTimer;                         // Sync delay to free up database
247     SyncRunner                          syncRunner;                                     // thread to do a sync.
248     QThread                                     syncThread;                                     // Thread which talks to evernote
249     ThumbnailRunner                     thumbnailRunner;                        // Runner for thumbnail thread
250     QThread                                     thumbnailThread;                        // Thread that generates pretty pictures
251     QTimer                                      saveTimer;                                      // Timer to save note contents
252     
253     QTimer                                      authTimer;                                      // Refresh authentication
254     QTimer                                      externalFileSaveTimer;          // Save files altered externally
255     QTimer                                      thumbnailTimer;                         // Wakeup & scan for thumbnails
256     List<String>                        externalFiles;                          // External files to save later
257     List<String>                        importFilesKeep;                        // Auto-import files to save later
258     List<String>                        importFilesDelete;                      // Auto-import files to save later
259     
260     int                                         indexTime;                                      // how often to try and index
261     boolean                                     indexRunning;                           // Is indexing running?
262     boolean                                     indexDisabled;                          // Is indexing disabled?
263     
264     int                                         syncThreadsReady;                       // number of sync threads that are free
265     int                                         syncTime;                                       // Sync interval
266     boolean                                     syncRunning;                            // Is sync running?
267     boolean                                     automaticSync;                          // do sync automatically?
268     QTreeWidgetItem                     attributeTreeSelected;
269
270     QAction                             prevButton;                                     // Go to the previous item viewed
271     QAction                             nextButton;                                     // Go to the next item in the history
272     QAction                             downButton;                                     // Go to the next item in the list
273     QAction                             upButton;                                       // Go to the prev. item in the list;
274     QAction                             synchronizeButton;                      // Synchronize with Evernote
275     QAction                             allNotesButton;                         // Reset & view all notes
276     QTimer                              synchronizeAnimationTimer;      // Timer to change animation button
277     int                                 synchronizeIconAngle;           // Used to rotate sync icon
278     QAction                     printButton;                            // Print Button
279     QAction                             tagButton;                                      // Tag edit button
280     QAction                             attributeButton;                        // Attribute information button
281     QAction                     emailButton;                            // Email button
282     QAction                     deleteButton;                           // Delete button
283     QAction                             newButton;                                      // new Note Button;
284     QSpinBox                    zoomSpinner;                            // Zoom zoom
285     QAction                             searchClearButton;                      // Clear the search field
286     
287     SearchPanel                 searchLayout;                           // Widget to hold search field, zoom, & quota
288     
289     QSplitter                   mainLeftRightSplitter;          // main splitter for left/right side
290     QSplitter                   leftSplitter1;                          // first left hand splitter
291     QSplitter                   browserIndexSplitter;           // splitter between note index & note text
292     
293     QFileSystemWatcher  importKeepWatcher;                      // Watch & keep auto-import
294     QFileSystemWatcher  importDeleteWatcher;            // Watch & Delete auto-import
295     List<String>                importedFiles;                          // History of imported files (so we don't import twice)
296     
297     OnlineNoteHistory   historyWindow;                          // online history window 
298     List<NoteVersionId> versions;                                       // history versions
299     
300     QTimer                              threadMonitorTimer;                     // Timer to watch threads.
301     int                                 dbThreadDeadCount=0;            // number of consecutive dead times for the db thread
302     int                                 syncThreadDeadCount=0;          // number of consecutive dead times for the sync thread
303     int                                 indexThreadDeadCount=0;         // number of consecutive dead times for the index thread
304     int                                 notebookThreadDeadCount=0;      // number of consecutive dead times for the notebook thread
305     int                                 tagDeadCount=0;                         // number of consecutive dead times for the tag thread
306     int                                 trashDeadCount=0;                       // number of consecutive dead times for the trash thread
307     int                                 saveThreadDeadCount=0;          // number of consecutive dead times for the save thread
308     boolean                             disableTagThreadCheck=false;
309     boolean                             disableNotebookThreadCheck=false;
310     boolean                             disableTrashThreadCheck=false;
311     boolean                             disableSaveThreadCheck=false;
312     boolean                             disableSyncThreadCheck=false;
313     boolean                             disableIndexThreadCheck=false;
314     
315     HashMap<String, String>             noteCache;                      // Cash of note content 
316     HashMap<String, Boolean>    readOnlyCache;          // List of cashe notes that are read-only
317     HashMap<String, Boolean>    inkNoteCache;           // List of cache notes that are ink notes 
318     List<String>                historyGuids;                           // GUIDs of previously viewed items
319     int                                 historyPosition;                        // Position within the viewed items
320     boolean                             fromHistory;                            // Is this from the history queue?
321     String                              trashNoteGuid;                          // Guid to restore / set into or out of trash to save position
322     List<Thumbnailer>   thumbGenerators;                                // generate preview image
323     ThumbnailViewer             thumbnailViewer;                        // View preview thumbnail; 
324     boolean                             encryptOnShutdown;                      // should I encrypt when I close?
325     boolean                             decryptOnShutdown;                      // should I decrypt on shutdown;
326     String                              encryptCipher;                          // What cipher should I use?
327     Signal0                     minimizeToTray;
328     boolean                             windowMaximized = false;        // Keep track of the window state for restores
329     List<String>                pdfReadyQueue;                          // Queue of PDFs that are ready to be rendered.
330     List<QPixmap>               syncIcons;                                      // Array of icons used in sync animation
331     private boolean             closeAction = false;            // Used to say when to close or when to minimize
332     private static Logger log = Logger.getLogger(NeverNote.class); 
333     private String              saveLastPath;                           // last path we used
334     private final QTimer                messageTimer;                           // Timer to clear the status message.
335     private QTimer              blockTimer;
336     BrowserWindow               blockingWindow;
337     
338     String iconPath = new String("classpath:cx/fbn/nevernote/icons/");
339         
340         
341     //***************************************************************
342     //***************************************************************
343     //** Constructor & main entry point
344     //***************************************************************
345     //***************************************************************
346     // Application Constructor  
347         @SuppressWarnings("static-access")
348         public NeverNote(DatabaseConnection dbConn)  {
349                 conn = dbConn;          
350                 if (conn.getConnection() == null) {
351                         String msg = new String(tr("Unable to connect to the database.\n\nThe most probable reason is that some other process\n" +
352                                 "is accessing the database or NeverNote is already running.\n\n" +
353                                 "Please end any other process or shutdown the other NeverNote before starting.\n\nExiting program."));
354                         
355             QMessageBox.critical(null, tr("Database Connection Error") ,msg);
356                         System.exit(16);
357                 }
358                 setObjectName("mainWindow");
359 //              thread().setPriority(Thread.MAX_PRIORITY);
360                 
361                 logger = new ApplicationLogger("nevernote.log");
362                 logger.log(logger.HIGH, "Starting Application");
363                 
364                 decryptOnShutdown = false;
365                 encryptOnShutdown = false;
366                 conn.checkDatabaseVersion();
367                 
368                 
369                 
370                 // Start building the invalid XML tables
371                 Global.invalidElements = conn.getInvalidXMLTable().getInvalidElements();
372                 List<String> elements = conn.getInvalidXMLTable().getInvalidAttributeElements();
373                 
374                 for (int i=0; i<elements.size(); i++) {
375                         Global.invalidAttributes.put(elements.get(i), conn.getInvalidXMLTable().getInvalidAttributes(elements.get(i)));
376                 }
377                 
378                 logger.log(logger.EXTREME, "Starting GUI build");
379
380                 QTranslator nevernoteTranslator = new QTranslator();
381                 nevernoteTranslator.load(Global.getFileManager().getTranslateFilePath("nevernote_" + QLocale.system().name() + ".qm"));
382                 QApplication.instance().installTranslator(nevernoteTranslator);
383
384                 Global.originalPalette = QApplication.palette();
385                 QApplication.setStyle(Global.getStyle());
386                 if (Global.useStandardPalette())
387                         QApplication.setPalette(QApplication.style().standardPalette());
388         setWindowTitle("NeverNote");
389
390         mainLeftRightSplitter = new QSplitter();
391         setCentralWidget(mainLeftRightSplitter);
392         leftSplitter1 = new QSplitter();
393         leftSplitter1.setOrientation(Qt.Orientation.Vertical);
394                 
395         browserIndexSplitter = new QSplitter();
396         browserIndexSplitter.setOrientation(Qt.Orientation.Vertical);
397         
398         //* Setup threads & thread timers
399 //        int indexRunnerCount = Global.getIndexThreads();
400 //       indexRunnerCount = 1;
401         QThreadPool.globalInstance().setMaxThreadCount(Global.threadCount);     // increase max thread count
402
403                 logger.log(logger.EXTREME, "Building list manager");
404         listManager = new ListManager(conn, logger);
405         
406                 logger.log(logger.EXTREME, "Building index runners & timers");
407         indexRunner = new IndexRunner("indexRunner.log", 
408                         Global.getDatabaseUrl(), Global.getIndexDatabaseUrl(), Global.getResourceDatabaseUrl(),
409                         Global.getDatabaseUserid(), Global.getDatabaseUserPassword(), Global.cipherPassword);
410                 indexThread = new QThread(indexRunner, "Index Thread");
411         indexRunner.indexAttachmentsLocally = Global.indexAttachmentsLocally();
412         indexRunner.indexImageRecognition = Global.indexImageRecognition();
413         indexRunner.indexNoteBody = Global.indexNoteBody();
414         indexRunner.indexNoteTitle = Global.indexNoteTitle();
415         indexRunner.specialIndexCharacters = Global.getSpecialIndexCharacters();
416                 indexThread.start();
417                 
418         synchronizeAnimationTimer = new QTimer();
419         synchronizeAnimationTimer.timeout.connect(this, "updateSyncButton()");
420         
421                 indexTimer = new QTimer();
422                 indexTime = 1000*Global.getIndexThreadSleepInterval();  
423                 indexTimer.start(indexTime);  // Start indexing timer
424                 indexTimer.timeout.connect(this, "indexTimer()");
425                 indexDisabled = false;
426                 indexRunning = false;
427                                 
428                 logger.log(logger.EXTREME, "Setting sync thread & timers");
429                 syncThreadsReady=1;
430                 syncRunner = new SyncRunner("syncRunner.log", 
431                                 Global.getDatabaseUrl(), Global.getIndexDatabaseUrl(), Global.getResourceDatabaseUrl(),
432                                 Global.getDatabaseUserid(), Global.getDatabaseUserPassword(), Global.cipherPassword);
433                 syncTime = new SyncTimes().timeValue(Global.getSyncInterval());
434                 syncTimer = new QTimer();
435                 syncTimer.timeout.connect(this, "syncTimer()");
436         syncRunner.status.message.connect(this, "setMessage(String)");
437         syncRunner.syncSignal.finished.connect(this, "syncThreadComplete(Boolean)");
438         syncRunner.syncSignal.errorDisconnect.connect(this, "remoteErrorDisconnect()");
439         syncRunning = false;    
440                 if (syncTime > 0) {
441                         automaticSync = true;
442                         syncTimer.start(syncTime*60*1000);
443                 } else {
444                         automaticSync = false;
445                         syncTimer.stop();
446                 }
447                 syncRunner.setEvernoteUpdateCount(Global.getEvernoteUpdateCount());
448                 syncThread = new QThread(syncRunner, "Synchronization Thread");
449                 syncThread.start();
450                 
451                 
452                 logger.log(logger.EXTREME, "Starting thumnail thread");
453                 pdfReadyQueue = new ArrayList<String>();
454                 thumbnailRunner = new ThumbnailRunner("thumbnailRunner.log", 
455                                 Global.getDatabaseUrl(), Global.getIndexDatabaseUrl(), Global.getResourceDatabaseUrl(),
456                                 Global.getDatabaseUserid(), Global.getDatabaseUserPassword(), Global.cipherPassword);
457                 thumbnailThread = new QThread(thumbnailRunner, "Thumbnail Thread");
458                 thumbnailRunner.noteSignal.thumbnailPageReady.connect(this, "thumbnailHTMLReady(String,QByteArray,Integer)");
459                 thumbnailThread.start();
460                 thumbGenerators = new ArrayList<Thumbnailer>();
461                 thumbnailTimer = new QTimer();
462                 thumbnailTimer.timeout.connect(this, "thumbnailTimer()");
463                 thumbnailTimer();
464                 thumbnailTimer.setInterval(500*1000);  // Thumbnail every minute
465                 thumbnailTimer.start();
466                 
467                 logger.log(logger.EXTREME, "Starting authentication timer");
468                 authTimer = new QTimer();
469                 authTimer.timeout.connect(this, "authTimer()");
470                 authTimer.start(1000*60*15);
471                 syncRunner.syncSignal.authRefreshComplete.connect(this, "authRefreshComplete(boolean)");
472                 
473                 logger.log(logger.EXTREME, "Setting save note timer");
474                 saveTimer = new QTimer();
475                 saveTimer.timeout.connect(this, "saveNote()");
476                 if (Global.getAutoSaveInterval() > 0) {
477                         saveTimer.setInterval(1000*60*Global.getAutoSaveInterval()); 
478                         saveTimer.start();
479                 }
480                 listManager.saveRunner.noteSignals.noteSaveRunnerError.connect(this, "saveRunnerError(String, String)");
481                 
482                 logger.log(logger.EXTREME, "Starting external file monitor timer");
483                 externalFileSaveTimer = new QTimer();
484                 externalFileSaveTimer.timeout.connect(this, "externalFileEditedSaver()");
485                 externalFileSaveTimer.setInterval(1000*5);   // save every 5 seconds;
486                 externalFiles = new ArrayList<String>();
487                 importFilesDelete = new ArrayList<String>();
488                 importFilesKeep = new ArrayList<String>();
489                 externalFileSaveTimer.start();
490                 
491         notebookTree = new NotebookTreeWidget(conn);
492         attributeTree = new AttributeTreeWidget();
493         tagTree = new TagTreeWidget(conn);
494         savedSearchTree = new SavedSearchTreeWidget();
495         trashTree = new TrashTreeWidget();
496         noteTableView = new TableView(logger, listManager);
497         
498         
499         searchField = new QComboBox();
500         searchField.setObjectName("searchField");
501         //setStyleSheet("QComboBox#searchField { background-color: yellow }");
502         searchField.setEditable(true);
503         searchField.activatedIndex.connect(this, "searchFieldChanged()");
504         searchField.setDuplicatesEnabled(false);
505         searchField.editTextChanged.connect(this,"searchFieldTextChanged(String)");
506         searchShortcut = new QShortcut(this);
507         setupShortcut(searchShortcut, "Focus_Search");
508         searchShortcut.activated.connect(this, "focusSearch()");
509         
510         quotaBar = new QuotaProgressBar();
511         // Setup the zoom
512         zoomSpinner = new QSpinBox();
513         zoomSpinner.setMinimum(10);
514         zoomSpinner.setMaximum(1000);
515         zoomSpinner.setAccelerated(true);
516         zoomSpinner.setSingleStep(10);
517         zoomSpinner.setValue(100);
518         zoomSpinner.valueChanged.connect(this, "zoomChanged()");
519         
520         searchLayout = new SearchPanel(searchField, quotaBar, notebookTree, zoomSpinner);
521         
522         
523         QGridLayout leftGrid = new QGridLayout();
524         leftSplitter1.setContentsMargins(5, 0, 0, 7);
525         leftSplitter1.setLayout(leftGrid);
526         leftGrid.addWidget(searchLayout,1,1);
527         leftGrid.addWidget(tagTree,2,1);
528         leftGrid.addWidget(attributeTree,3,1);
529         leftGrid.addWidget(savedSearchTree,4,1);
530         leftGrid.addWidget(trashTree,5, 1);
531         
532         // Setup the browser window
533         noteCache = new HashMap<String,String>();
534         readOnlyCache = new HashMap<String, Boolean>();
535         inkNoteCache = new HashMap<String, Boolean>();
536         browserWindow = new BrowserWindow(conn);
537
538         mainLeftRightSplitter.addWidget(leftSplitter1);
539         mainLeftRightSplitter.addWidget(browserIndexSplitter);
540         
541         if (Global.getListView() == Global.View_List_Wide) {
542                 browserIndexSplitter.addWidget(noteTableView);
543                 browserIndexSplitter.addWidget(browserWindow); 
544         } else {
545                 mainLeftRightSplitter.addWidget(noteTableView);
546                 mainLeftRightSplitter.addWidget(browserWindow); 
547         }
548         
549         // Setup the thumbnail viewer
550         thumbnailViewer = new ThumbnailViewer();
551         thumbnailViewer.upArrow.connect(this, "upAction()");
552         thumbnailViewer.downArrow.connect(this, "downAction()");
553         thumbnailViewer.leftArrow.connect(this, "nextViewedAction()");
554         thumbnailViewer.rightArrow.connect(this, "previousViewedAction()");
555         
556         //Setup external browser manager
557         externalWindows = new HashMap<String, ExternalBrowse>();
558
559         listManager.loadNotesIndex();
560         initializeNotebookTree();
561         initializeTagTree();
562         initializeSavedSearchTree();
563         attributeTree.itemClicked.connect(this, "attributeTreeClicked(QTreeWidgetItem, Integer)");
564         attributeTreeSelected = null;
565         initializeNoteTable();    
566
567                 selectedNoteGUIDs = new ArrayList<String>();
568                 statusBar = new QStatusBar();
569                 setStatusBar(statusBar);
570                 menuBar = new MainMenuBar(this);
571                 emitLog = new ArrayList<String>();
572                 
573                 tagTree.setDeleteAction(menuBar.tagDeleteAction);
574                 tagTree.setMergeAction(menuBar.tagMergeAction);
575                 tagTree.setEditAction(menuBar.tagEditAction);
576                 tagTree.setAddAction(menuBar.tagAddAction);
577                 tagTree.setIconAction(menuBar.tagIconAction);
578                 tagTree.setVisible(Global.isWindowVisible("tagTree"));
579                 leftSplitter1.setVisible(Global.isWindowVisible("leftPanel"));
580                 tagTree.noteSignal.tagsAdded.connect(this, "tagsAdded(String, String)");
581                 menuBar.hideTags.setChecked(Global.isWindowVisible("tagTree"));
582                 listManager.tagSignal.listChanged.connect(this, "reloadTagTree()");
583                 
584                 if (!Global.isWindowVisible("zoom")) {
585                         searchLayout.hideZoom();
586                         menuBar.hideZoom.setChecked(false);
587                 } 
588         
589                 notebookTree.setDeleteAction(menuBar.notebookDeleteAction);
590                 notebookTree.setEditAction(menuBar.notebookEditAction);
591                 notebookTree.setAddAction(menuBar.notebookAddAction);
592                 notebookTree.setIconAction(menuBar.notebookIconAction);
593                 notebookTree.setStackAction(menuBar.notebookStackAction);
594                 notebookTree.setPublishAction(menuBar.notebookPublishAction);
595                 notebookTree.setShareAction(menuBar.notebookShareAction);
596                 notebookTree.setVisible(Global.isWindowVisible("notebookTree"));
597                 notebookTree.noteSignal.notebookChanged.connect(this, "updateNoteNotebook(String, String)");
598                 notebookTree.noteSignal.tagsChanged.connect(this, "updateNoteTags(String, List)");
599             notebookTree.noteSignal.tagsChanged.connect(this, "updateListTags(String, List)");
600                 menuBar.hideNotebooks.setChecked(Global.isWindowVisible("notebookTree"));
601
602                 savedSearchTree.setAddAction(menuBar.savedSearchAddAction);
603                 savedSearchTree.setEditAction(menuBar.savedSearchEditAction);
604                 savedSearchTree.setDeleteAction(menuBar.savedSearchDeleteAction);
605                 savedSearchTree.setIconAction(menuBar.savedSearchIconAction);
606                 savedSearchTree.itemSelectionChanged.connect(this, "updateSavedSearchSelection()");
607                 savedSearchTree.setVisible(Global.isWindowVisible("savedSearchTree"));
608                 menuBar.hideSavedSearches.setChecked(Global.isWindowVisible("savedSearchTree"));
609                         
610                 noteTableView.setAddAction(menuBar.noteAdd);
611                 noteTableView.setDeleteAction(menuBar.noteDelete);
612                 noteTableView.setRestoreAction(menuBar.noteRestoreAction);
613                 noteTableView.setNoteDuplicateAction(menuBar.noteDuplicateAction);
614                 noteTableView.setNoteHistoryAction(menuBar.noteOnlineHistoryAction);
615                 noteTableView.noteSignal.titleColorChanged.connect(this, "titleColorChanged(Integer)");
616                 noteTableView.setMergeNotesAction(menuBar.noteMergeAction);
617                 noteTableView.doubleClicked.connect(this, "listDoubleClick()");
618                 listManager.trashSignal.countChanged.connect(trashTree, "updateCounts(Integer)");
619                 
620                 quotaBar.setMouseClickAction(menuBar.accountAction);
621                 
622                 trashTree.load();
623         trashTree.itemSelectionChanged.connect(this, "trashTreeSelection()");
624                 trashTree.setEmptyAction(menuBar.emptyTrashAction);
625                 trashTree.setVisible(Global.isWindowVisible("trashTree"));
626                 menuBar.hideTrash.setChecked(Global.isWindowVisible("trashTree"));
627                 trashTree.updateCounts(listManager.getTrashCount());
628                 attributeTree.setVisible(Global.isWindowVisible("attributeTree"));
629                 menuBar.hideAttributes.setChecked(Global.isWindowVisible("attributeTree"));
630
631                 noteTableView.setVisible(Global.isWindowVisible("noteList"));
632                 menuBar.hideNoteList.setChecked(Global.isWindowVisible("noteList"));
633                 
634                 if (!Global.isWindowVisible("editorButtonBar"))
635                         toggleEditorButtonBar();
636                 if (!Global.isWindowVisible("leftPanel"))
637                         menuBar.hideLeftSide.setChecked(true);
638                 if (Global.isWindowVisible("noteInformation"))
639                         toggleNoteInformation();
640                 quotaBar.setVisible(Global.isWindowVisible("quota"));
641                 if (!quotaBar.isVisible())
642                         menuBar.hideQuota.setChecked(false);
643                 searchField.setVisible(Global.isWindowVisible("searchField"));
644                 if (!searchField.isVisible())
645                         menuBar.hideSearch.setChecked(false);
646                 
647                 if (searchField.isHidden() && quotaBar.isHidden() && zoomSpinner.isHidden() && notebookTree.isHidden())
648                         searchLayout.hide();
649                 
650                 setMenuBar(menuBar);
651                 setupToolBar();
652                 find = new FindDialog();
653                 find.getOkButton().clicked.connect(this, "doFindText()");
654                 
655                 // Setup the tray icon menu bar
656                 trayShowAction = new QAction("Show/Hide", this);
657                 trayExitAction = new QAction("Exit", this);
658                 trayAddNoteAction = new QAction("Add Note", this);
659                 
660                 trayExitAction.triggered.connect(this, "closeNeverNote()");
661                 trayAddNoteAction.triggered.connect(this, "addNote()");
662                 trayShowAction.triggered.connect(this, "trayToggleVisible()");
663                 
664                 trayMenu = new QMenu(this);
665                 trayMenu.addAction(trayAddNoteAction);
666                 trayMenu.addAction(trayShowAction);
667                 trayMenu.addAction(trayExitAction);
668                 
669                 
670                 trayIcon = new QSystemTrayIcon(this);
671                 trayIcon.setToolTip("NeverNote");
672                 trayIcon.setContextMenu(trayMenu);
673                 trayIcon.activated.connect(this, "trayActivated(com.trolltech.qt.gui.QSystemTrayIcon$ActivationReason)");
674
675                 currentNoteGuid="";
676                 currentNoteGuid = Global.getLastViewedNoteGuid();
677         historyGuids = new ArrayList<String>();
678         historyPosition = 0;
679         fromHistory = false;
680                 noteDirty = false;
681                 if (!currentNoteGuid.trim().equals("")) {
682                         currentNote = conn.getNoteTable().getNote(currentNoteGuid, true,true,false,false,true);
683                 }
684                 
685                 noteIndexUpdated(true);
686                 showColumns();
687                 menuBar.showEditorBar.setChecked(Global.isWindowVisible("editorButtonBar"));
688                 if (menuBar.showEditorBar.isChecked())
689                 showEditorButtons(browserWindow);
690                 tagIndexUpdated(true);
691                 savedSearchIndexUpdated();
692                 notebookIndexUpdated();
693                 updateQuotaBar();
694         setupSyncSignalListeners();        
695         setupBrowserSignalListeners();
696         setupIndexListeners();
697               
698         
699         tagTree.tagSignal.listChanged.connect(this, "tagIndexUpdated()");
700         tagTree.showAllTags(true);
701
702                 QIcon appIcon = new QIcon(iconPath+"nevernote.png");
703         setWindowIcon(appIcon);
704         trayIcon.setIcon(appIcon);
705         if (Global.showTrayIcon())
706                 trayIcon.show();
707         else
708                 trayIcon.hide();
709         
710         scrollToGuid(currentNoteGuid);
711         if (Global.automaticLogin()) {
712                 remoteConnect();
713                 if (Global.isConnected)
714                         syncTimer();
715         }
716         setupFolderImports();
717         
718         loadStyleSheet();
719         restoreWindowState(true);
720         
721         if (Global.mimicEvernoteInterface) {
722                 notebookTree.selectGuid("");
723         }
724         
725         threadMonitorTimer = new QTimer();
726         threadMonitorTimer.timeout.connect(this, "threadMonitorCheck()");
727         threadMonitorTimer.start(1000*10);  // Check for threads every 10 seconds;              
728         
729         historyGuids.add(currentNoteGuid);
730         historyPosition = 1;
731         
732         menuBar.blockSignals(true);
733         menuBar.narrowListView.blockSignals(true);
734         menuBar.wideListView.blockSignals(true);
735         if (Global.getListView() == Global.View_List_Narrow) { 
736                 menuBar.narrowListView.setChecked(true);
737         }
738         else{ 
739                 menuBar.wideListView.setChecked(true);
740         }
741         menuBar.blockSignals(false);
742         menuBar.narrowListView.blockSignals(false);
743         menuBar.wideListView.blockSignals(false);
744
745         if (Global.getListView() == Global.View_List_Wide) {
746                 browserIndexSplitter.addWidget(noteTableView);
747                 browserIndexSplitter.addWidget(browserWindow); 
748         } else {
749                 mainLeftRightSplitter.addWidget(noteTableView);
750                 mainLeftRightSplitter.addWidget(browserWindow); 
751         }
752         
753                 messageTimer = new QTimer();
754                 messageTimer.timeout.connect(this, "clearMessage()");
755                 messageTimer.setInterval(1000*15);
756                 clearMessage();
757         
758         int sortCol = Global.getSortColumn();
759                 int sortOrder = Global.getSortOrder();
760                 noteTableView.sortByColumn(sortCol, SortOrder.resolve(sortOrder));
761                 if (Global.checkVersionUpgrade())
762                         checkForUpdates();
763         }
764                 
765         // Main entry point
766         public static void main(String[] args) {
767                 log.setLevel(Level.FATAL);
768                 QApplication.initialize(args);
769                 QPixmap pixmap = new QPixmap("classpath:cx/fbn/nevernote/icons/splash_logo.png");
770                 QSplashScreen splash = new QSplashScreen(pixmap);
771                 boolean showSplash;
772                 
773                 DatabaseConnection dbConn;
774
775         try {
776             initializeGlobalSettings(args);
777
778             showSplash = Global.isWindowVisible("SplashScreen");
779             if (showSplash)
780                 splash.show();
781
782             dbConn = setupDatabaseConnection();
783
784             // Must be last stage of setup - only safe once DB is open hence we know we are the only instance running
785             Global.getFileManager().purgeResDirectory(true);
786
787         } catch (InitializationException e) {
788             // Fatal
789             e.printStackTrace();
790             QMessageBox.critical(null, "Startup error", "Aborting: " + e.getMessage());
791             return;
792         }
793
794         NeverNote application = new NeverNote(dbConn);
795
796                 application.setAttribute(WidgetAttribute.WA_DeleteOnClose, true);
797                 if (Global.startMinimized()) 
798                         application.showMinimized();
799                 else {
800                         if (Global.wasWindowMaximized())
801                                 application.showMaximized();
802                         else
803                                 application.show();
804                 }
805                 
806                 if (showSplash)
807                         splash.finish(application);
808                 QApplication.exec();
809                 System.out.println("Goodbye.");
810                 QApplication.exit();
811         }
812
813     /**
814      * Open the internal database, or create if not present
815      *
816      * @throws InitializationException when opening the database fails, e.g. because another process has it locked
817      */
818     private static DatabaseConnection setupDatabaseConnection() throws InitializationException {
819         ApplicationLogger logger = new ApplicationLogger("nevernote-database.log");
820         
821         File f = Global.getFileManager().getDbDirFile(Global.databaseName + ".h2.db");
822                 boolean dbExists = f.exists(); 
823                 if (!dbExists)
824                         Global.setDatabaseUrl("");
825         
826         if (Global.getDatabaseUrl().toUpperCase().indexOf("CIPHER=") > -1) {
827             boolean goodCheck = false;
828             while (!goodCheck) {
829                 DatabaseLoginDialog dialog = new DatabaseLoginDialog();
830                 dialog.exec();
831                 if (!dialog.okPressed())
832                     System.exit(0);
833                 Global.cipherPassword = dialog.getPassword();
834                 goodCheck = databaseCheck(Global.getDatabaseUrl(), Global.getDatabaseUserid(),
835                         Global.getDatabaseUserPassword(), Global.cipherPassword);
836             }
837         }
838         DatabaseConnection dbConn = new DatabaseConnection(logger,Global.getDatabaseUrl(), 
839                         Global.getIndexDatabaseUrl(), Global.getResourceDatabaseUrl(),
840                         Global.getDatabaseUserid(), Global.getDatabaseUserPassword(), Global.cipherPassword, 0);
841        return dbConn;
842     }
843     
844     // Encrypt the database upon shutdown
845     private void encryptOnShutdown() {
846         String dbPath= Global.getFileManager().getDbDirPath("");
847         String dbName = "NeverNote";
848         try {
849                 Statement st = conn.getConnection().createStatement();  
850                 st.execute("shutdown");
851                 if (QMessageBox.question(this, tr("Are you sure"), 
852                                 tr("Are you sure you wish to encrypt the database?"),
853                                 QMessageBox.StandardButton.Yes, 
854                                 QMessageBox.StandardButton.No) == StandardButton.Yes.value()) {
855                         ChangeFileEncryption.execute(dbPath, dbName, encryptCipher, null, Global.cipherPassword.toCharArray(), true);
856                         Global.setDatabaseUrl(Global.getDatabaseUrl() + ";CIPHER="+encryptCipher);
857                         QMessageBox.information(this, "Encryption Complete", "Encryption is complete");
858                 }
859         } catch (SQLException e) {
860                         e.printStackTrace();
861                 }       
862     }
863     
864     // Decrypt the database upon shutdown
865     private void decryptOnShutdown() {
866         String dbPath= Global.getFileManager().getDbDirPath("");
867         String dbName = "NeverNote";
868         try {
869                 Statement st = conn.getConnection().createStatement();  
870                 st.execute("shutdown");
871                 if (Global.getDatabaseUrl().toUpperCase().indexOf(";CIPHER=AES") > -1)
872                         encryptCipher = "AES";
873                 else
874                         encryptCipher = "XTEA";
875                 if (QMessageBox.question(this, tr("Confirmation"), tr("Are you sure", 
876                                 "Are you sure you wish to decrypt the database?"),
877                                 QMessageBox.StandardButton.Yes, 
878                                 QMessageBox.StandardButton.No) == StandardButton.Yes.value()) {
879
880                         ChangeFileEncryption.execute(dbPath, dbName, encryptCipher, Global.cipherPassword.toCharArray(), null, true);
881                         Global.setDatabaseUrl("");
882                         QMessageBox.information(this, tr("Decryption Complete"), tr("Decryption is complete"));
883                 }
884                 } catch (SQLException e) {
885                         e.printStackTrace();
886                 }       
887     }
888     /**
889      * Encrypt/Decrypt the local database
890      **/
891     public void doDatabaseEncrypt() {
892         // The database is not currently encrypted
893         if (Global.getDatabaseUrl().toUpperCase().indexOf("CIPHER=") == -1) {
894                 if (QMessageBox.question(this, tr("Confirmation"), tr("Encrypting the database is used" +
895                                 "to enhance security and is performed\nupon shutdown, but please be aware that if"+
896                                 " you lose the password your\nis lost forever.\n\nIt is highly recommended you " +
897                                 "perform a backup and/or fully synchronize\n prior to executing this funtction.\n\n" +
898                                 "Do you wish to proceed?"),
899                                 QMessageBox.StandardButton.Yes, 
900                                 QMessageBox.StandardButton.No)==StandardButton.No.value()) {
901                                 return;
902                 }
903                 DBEncryptDialog dialog = new DBEncryptDialog();
904                 dialog.exec();
905                 if (dialog.okPressed()) {
906                         Global.cipherPassword = dialog.getPassword();
907                         encryptOnShutdown  = true;
908                         encryptCipher = dialog.getEncryptionMethod();
909                 }
910         } else {
911             DBEncryptDialog dialog = new DBEncryptDialog();
912             dialog.setWindowTitle("Database Decryption");
913             dialog.hideEncryption();
914             dialog.exec();
915             if (dialog.okPressed()) {
916                 if (!dialog.getPassword().equals(Global.cipherPassword)) {
917                         QMessageBox.critical(null, tr("Incorrect Password"), tr("Incorrect Password"));
918                         return;
919                 }
920                 decryptOnShutdown  = true;
921                 encryptCipher = "";
922             }
923         }
924         return;
925     }
926
927         private static void initializeGlobalSettings(String[] args) throws InitializationException {
928                 StartupConfig   startupConfig = new StartupConfig();
929
930         for (String arg : args) {
931             String lower = arg.toLowerCase();
932             if (lower.startsWith("--name="))
933                startupConfig.setName(arg.substring(arg.indexOf('=') + 1));
934             if (lower.startsWith("--home="))
935                startupConfig.setHomeDirPath(arg.substring(arg.indexOf('=') + 1));
936             if (lower.startsWith("--disable-viewing"))
937                startupConfig.setDisableViewing(true);
938         }
939                 Global.setup(startupConfig);
940     }
941
942     // Exit point
943         @Override
944         public void closeEvent(QCloseEvent event) {     
945                 if (Global.minimizeOnClose() && !closeAction && Global.showTrayIcon()) {
946                         event.ignore();
947                         hide();
948                         return;
949                 }
950                 logger.log(logger.HIGH, "Entering NeverNote.closeEvent");
951                 waitCursor(true);
952                 
953                 if (currentNote!= null & browserWindow!=null) {
954                         if (!currentNote.getTitle().equals(browserWindow.getTitle()))
955                                 conn.getNoteTable().updateNoteTitle(currentNote.getGuid(), browserWindow.getTitle());
956                 }
957                 saveNote();
958                 setMessage(tr("Beginning shutdown."));
959                 
960                 // Close down external windows
961                 Collection<ExternalBrowse> windows = externalWindows.values();
962                 Iterator<ExternalBrowse> iterator = windows.iterator();
963                 while (iterator.hasNext()) {
964                         ExternalBrowse browser = iterator.next();
965                         browser.windowClosing.disconnect();
966                         browser.close();
967                 }
968                 
969                 
970                 externalFileEditedSaver();
971                 if (Global.isConnected && Global.synchronizeOnClose()) {
972                         setMessage(tr("Performing synchronization before closing."));
973                         syncRunner.syncNeeded = true;
974                         syncRunner.addWork("SYNC");
975                 } else {
976                         syncRunner.keepRunning = false;
977                 }
978                 syncRunner.addWork("STOP");
979                 setMessage("Closing Program.");
980                 threadMonitorTimer.stop();
981
982                 thumbnailRunner.addWork("STOP");
983                 indexRunner.addWork("STOP");
984                 saveNote();
985                 listManager.stop();
986                 saveWindowState();
987
988                 if (tempFiles != null)
989                         tempFiles.clear();
990
991                 browserWindow.noteSignal.tagsChanged.disconnect();
992                 browserWindow.noteSignal.titleChanged.disconnect();
993                 browserWindow.noteSignal.noteChanged.disconnect();
994                 browserWindow.noteSignal.notebookChanged.disconnect();
995                 browserWindow.noteSignal.createdDateChanged.disconnect();
996                 browserWindow.noteSignal.alteredDateChanged.disconnect();
997                 syncRunner.searchSignal.listChanged.disconnect();
998                 syncRunner.tagSignal.listChanged.disconnect();
999         syncRunner.notebookSignal.listChanged.disconnect();
1000         syncRunner.noteIndexSignal.listChanged.disconnect();
1001
1002                 if (isVisible())
1003                         Global.saveWindowVisible("toolBar", toolBar.isVisible());
1004                 saveNoteColumnPositions();
1005                 saveNoteIndexWidth();
1006                 
1007                 int width = notebookTree.columnWidth(0);
1008                 Global.setColumnWidth("notebookTreeName", width);
1009                 width = tagTree.columnWidth(0);
1010                 Global.setColumnWidth("tagTreeName", width);
1011                 
1012                 Global.saveWindowMaximized(isMaximized());
1013                 Global.saveCurrentNoteGuid(currentNoteGuid);
1014                         
1015                 int sortCol = noteTableView.proxyModel.sortColumn();
1016                 int sortOrder = noteTableView.proxyModel.sortOrder().value();
1017                 Global.setSortColumn(sortCol);
1018                 Global.setSortOrder(sortOrder);
1019                 
1020                 hide();
1021                 trayIcon.hide();
1022                 Global.keepRunning = false;
1023                 try {
1024                         logger.log(logger.MEDIUM, "Waiting for indexThread to stop");
1025                         if (indexRunner.thread().isAlive())
1026                                 indexRunner.thread().join(50);
1027                         if (!indexRunner.thread().isAlive())
1028                                 logger.log(logger.MEDIUM, "Index thread has stopped");
1029                         else {
1030                                 logger.log(logger.MEDIUM, "Index thread still running - interrupting");
1031                                 indexRunner.thread().interrupt();
1032                         }
1033                 } catch (InterruptedException e1) {
1034                         e1.printStackTrace();
1035                 }
1036                 
1037                 if (!syncRunner.thread().isAlive()) {
1038                         logger.log(logger.MEDIUM, "Waiting for syncThread to stop");
1039                         if (syncRunner.thread().isAlive()) {
1040                                 System.out.println(tr("Synchronizing.  Please be patient."));
1041                                 for(;syncRunner.thread().isAlive();) {
1042                                         try {
1043                                                 wait(10);
1044                                         } catch (InterruptedException e) {
1045                                                 e.printStackTrace();
1046                                         }
1047                                 }
1048                         }
1049                         logger.log(logger.MEDIUM, "Sync thread has stopped");
1050                 }
1051
1052                 if (encryptOnShutdown) {
1053                         encryptOnShutdown();
1054                 }
1055                 if (decryptOnShutdown) {
1056                         decryptOnShutdown();
1057                 }
1058                 try {
1059                         Global.getFileManager().purgeResDirectory(false);
1060                 } catch (InitializationException e) {
1061                         System.out.println(tr("Empty res directory purge failed"));
1062                         e.printStackTrace();
1063                 }
1064                 logger.log(logger.HIGH, "Leaving NeverNote.closeEvent");
1065         }
1066
1067         @SuppressWarnings("unused")
1068         private void closeNeverNote() {
1069                 closeAction = true;
1070                 close();
1071         }
1072         public void setMessage(String s) {
1073                 logger.log(logger.HIGH, "Entering NeverNote.setMessage");
1074                 
1075                 statusBar.show();
1076                 logger.log(logger.HIGH, "Message: " +s);
1077                 statusBar.showMessage(s);
1078                 emitLog.add(s);
1079                 
1080
1081                 messageTimer.stop();
1082                 messageTimer.setSingleShot(true);
1083                 messageTimer.start();
1084                 
1085                 
1086                 logger.log(logger.HIGH, "Leaving NeverNote.setMessage");
1087         }
1088         
1089         private void clearMessage() {
1090                 statusBar.clearMessage();
1091                 statusBar.hide();
1092         }
1093                 
1094         private void waitCursor(boolean wait) {
1095                 if (wait) {
1096                         if (QApplication.overrideCursor() == null)
1097                                 QApplication.setOverrideCursor(new QCursor(Qt.CursorShape.WaitCursor));
1098                 }
1099                 else {
1100                         while (QApplication.overrideCursor() != null)
1101                                 QApplication.restoreOverrideCursor();
1102                 }
1103                 listManager.refreshCounters();
1104         }
1105         
1106         private void setupIndexListeners() {
1107 //              indexRunner.noteSignal.noteIndexed.connect(this, "indexThreadComplete(String)");
1108 //              indexRunner.resourceSignal.resourceIndexed.connect(this, "indexThreadComplete(String)");
1109                 indexRunner.signal.indexStarted.connect(this, "indexStarted()");
1110                 indexRunner.signal.indexFinished.connect(this, "indexComplete()");
1111         }
1112         private void setupSyncSignalListeners() {
1113                 syncRunner.tagSignal.listChanged.connect(this, "tagIndexUpdated()");
1114         syncRunner.searchSignal.listChanged.connect(this, "savedSearchIndexUpdated()");
1115         syncRunner.notebookSignal.listChanged.connect(this, "notebookIndexUpdated()");
1116         syncRunner.noteIndexSignal.listChanged.connect(this, "noteIndexUpdated(boolean)");
1117         syncRunner.noteSignal.quotaChanged.connect(this, "updateQuotaBar()");
1118         
1119                 syncRunner.syncSignal.saveUploadAmount.connect(this,"saveUploadAmount(long)");
1120                 syncRunner.syncSignal.saveUserInformation.connect(this,"saveUserInformation(User)");
1121                 syncRunner.syncSignal.saveEvernoteUpdateCount.connect(this,"saveEvernoteUpdateCount(int)");
1122                 
1123                 syncRunner.noteSignal.guidChanged.connect(this, "noteGuidChanged(String, String)");
1124                 syncRunner.noteSignal.noteChanged.connect(this, "invalidateNoteCache(String, String)");
1125                 syncRunner.resourceSignal.resourceGuidChanged.connect(this, "noteResourceGuidChanged(String,String,String)");
1126                 syncRunner.noteSignal.noteDownloaded.connect(listManager, "noteDownloaded(Note)");
1127                 syncRunner.noteSignal.notebookChanged.connect(this, "updateNoteNotebook(String, String)");
1128                 
1129                 syncRunner.syncSignal.refreshLists.connect(this, "refreshLists()");
1130         }
1131         
1132         private void setupBrowserSignalListeners() {
1133                 setupBrowserWindowListeners(browserWindow, true);
1134         }
1135
1136         private void setupBrowserWindowListeners(BrowserWindow browser, boolean master) {
1137                 browser.fileWatcher.fileChanged.connect(this, "externalFileEdited(String)");
1138                 browser.noteSignal.tagsChanged.connect(this, "updateNoteTags(String, List)");
1139             browser.noteSignal.tagsChanged.connect(this, "updateListTags(String, List)");
1140             if (master) browser.noteSignal.noteChanged.connect(this, "setNoteDirty()");
1141             browser.noteSignal.titleChanged.connect(listManager, "updateNoteTitle(String, String)");
1142             browser.noteSignal.titleChanged.connect(this, "updateNoteTitle(String, String)");
1143             browser.noteSignal.notebookChanged.connect(this, "updateNoteNotebook(String, String)");
1144             browser.noteSignal.createdDateChanged.connect(listManager, "updateNoteCreatedDate(String, QDateTime)");
1145             browser.noteSignal.alteredDateChanged.connect(listManager, "updateNoteAlteredDate(String, QDateTime)");
1146             browser.noteSignal.subjectDateChanged.connect(listManager, "updateNoteSubjectDate(String, QDateTime)");
1147             browser.noteSignal.authorChanged.connect(listManager, "updateNoteAuthor(String, String)");
1148             browser.noteSignal.geoChanged.connect(listManager, "updateNoteGeoTag(String, Double,Double,Double)");
1149             browser.noteSignal.geoChanged.connect(this, "setNoteDirty()");
1150             browser.noteSignal.sourceUrlChanged.connect(listManager, "updateNoteSourceUrl(String, String)");
1151         browser.blockApplication.connect(this, "blockApplication(BrowserWindow)");
1152         browser.unblockApplication.connect(this, "unblockApplication()");
1153             if (master) browser.focusLost.connect(this, "saveNote()");
1154             browser.resourceSignal.contentChanged.connect(this, "externalFileEdited(String)");
1155         }
1156
1157         //**************************************************
1158         //* Setup shortcuts
1159         //**************************************************
1160         private void setupShortcut(QShortcut action, String text) {
1161                 if (!Global.shortcutKeys.containsAction(text))
1162                         return;
1163                 action.setKey(new QKeySequence(Global.shortcutKeys.getShortcut(text)));
1164         }
1165         
1166         //***************************************************************
1167         //***************************************************************
1168         //* Settings and look & feel
1169         //***************************************************************
1170         //***************************************************************
1171         @SuppressWarnings("unused")
1172         private void settings() {
1173                 logger.log(logger.HIGH, "Entering NeverNote.settings");
1174                 saveNoteColumnPositions();
1175                 saveNoteIndexWidth();
1176                 showColumns();
1177         ConfigDialog settings = new ConfigDialog(this);
1178         String dateFormat = Global.getDateFormat();
1179         String timeFormat = Global.getTimeFormat();
1180         
1181                 indexTime = 1000*Global.getIndexThreadSleepInterval();  
1182                 indexTimer.start(indexTime);  // reset indexing timer
1183         
1184         settings.exec();
1185         indexRunner.indexAttachmentsLocally = Global.indexAttachmentsLocally();
1186         indexRunner.indexNoteBody = Global.indexNoteBody();
1187         indexRunner.indexNoteTitle = Global.indexNoteTitle();
1188         indexRunner.specialIndexCharacters = Global.getSpecialIndexCharacters();
1189         indexRunner.indexImageRecognition = Global.indexImageRecognition();
1190         if (Global.showTrayIcon())
1191                 trayIcon.show();
1192         else
1193                 trayIcon.hide();
1194         showColumns();
1195         if (menuBar.showEditorBar.isChecked())
1196                 showEditorButtons(browserWindow);
1197         
1198         // Reset the save timer
1199         if (Global.getAutoSaveInterval() > 0)
1200                         saveTimer.setInterval(1000*60*Global.getAutoSaveInterval());
1201         else
1202                 saveTimer.stop();
1203         
1204         
1205         // Set special reloads
1206         if (settings.getDebugPage().reloadSharedNotebooksClicked()) {
1207                 conn.executeSql("Delete from LinkedNotebook");
1208                 conn.executeSql("Delete from Notebook where linked=true");
1209                 conn.executeSql("Insert into Sync (key, value) values ('FullLinkedNotebookSync', 'true')");
1210                 conn.executeSql("Insert into Sync (key, value) values ('FullSharedNotebookSync', 'true')");
1211         }
1212
1213         // Reload user data
1214         noteCache.clear();
1215         readOnlyCache.clear();
1216         inkNoteCache.clear();
1217         noteIndexUpdated(true);
1218                 
1219         logger.log(logger.HIGH, "Leaving NeverNote.settings");
1220         }
1221         // Restore things to the way they were
1222         private void restoreWindowState(boolean mainWindow) {
1223                 // We need to name things or this doesn't work.
1224                 setObjectName("NeverNote");
1225         restoreState(Global.restoreState(objectName()));
1226                 mainLeftRightSplitter.setObjectName("mainLeftRightSplitter");
1227                 browserIndexSplitter.setObjectName("browserIndexSplitter");
1228                 leftSplitter1.setObjectName("leftSplitter1");   
1229                 
1230                 // Restore the actual positions.
1231                 if (mainWindow)
1232                         restoreGeometry(Global.restoreGeometry(objectName()));
1233         mainLeftRightSplitter.restoreState(Global.restoreState(mainLeftRightSplitter.objectName()));
1234         browserIndexSplitter.restoreState(Global.restoreState(browserIndexSplitter.objectName()));
1235         leftSplitter1.restoreState(Global.restoreState(leftSplitter1.objectName()));
1236        
1237         }
1238         // Save window positions for the next start
1239         private void saveWindowState() {
1240                 Global.saveGeometry(objectName(), saveGeometry());
1241                 Global.saveState(mainLeftRightSplitter.objectName(), mainLeftRightSplitter.saveState());
1242                 Global.saveState(browserIndexSplitter.objectName(), browserIndexSplitter.saveState());
1243                 Global.saveState(leftSplitter1.objectName(), leftSplitter1.saveState());
1244                 Global.saveState(objectName(), saveState());
1245         }    
1246         // Load the style sheet
1247         private void loadStyleSheet() {
1248                 String styleSheetName = "default.qss";
1249                 if (Global.getStyle().equalsIgnoreCase("cleanlooks"))
1250                                 styleSheetName = "default-cleanlooks.qss";
1251                 String fileName = Global.getFileManager().getQssDirPathUser("default.qss");
1252                 QFile file = new QFile(fileName);
1253                 
1254                 // If a user default.qss doesn't exist, we use the one shipped with NeverNote
1255                 if (!file.exists()) {
1256                         fileName = Global.getFileManager().getQssDirPath(styleSheetName);
1257                         file = new QFile(fileName);
1258                 }
1259                 file.open(OpenModeFlag.ReadOnly);
1260                 String styleSheet = file.readAll().toString();
1261                 file.close();
1262                 setStyleSheet(styleSheet);
1263         }
1264         // Save column positions for the next time
1265         private void saveNoteColumnPositions() {
1266                 int position = noteTableView.header.visualIndex(Global.noteTableCreationPosition);
1267                 Global.setColumnPosition("noteTableCreationPosition", position);
1268                 position = noteTableView.header.visualIndex(Global.noteTableTagPosition);
1269                 Global.setColumnPosition("noteTableTagPosition", position);
1270                 position = noteTableView.header.visualIndex(Global.noteTableNotebookPosition);
1271                 Global.setColumnPosition("noteTableNotebookPosition", position);
1272                 position = noteTableView.header.visualIndex(Global.noteTableChangedPosition);
1273                 Global.setColumnPosition("noteTableChangedPosition", position);
1274                 position = noteTableView.header.visualIndex(Global.noteTableAuthorPosition);
1275                 Global.setColumnPosition("noteTableAuthorPosition", position);
1276                 position = noteTableView.header.visualIndex(Global.noteTableSourceUrlPosition);
1277                 Global.setColumnPosition("noteTableSourceUrlPosition", position);
1278                 position = noteTableView.header.visualIndex(Global.noteTableSubjectDatePosition);
1279                 Global.setColumnPosition("noteTableSubjectDatePosition", position);
1280                 position = noteTableView.header.visualIndex(Global.noteTableTitlePosition);
1281                 Global.setColumnPosition("noteTableTitlePosition", position);
1282                 position = noteTableView.header.visualIndex(Global.noteTableSynchronizedPosition);
1283                 Global.setColumnPosition("noteTableSynchronizedPosition", position);
1284                 position = noteTableView.header.visualIndex(Global.noteTableGuidPosition);
1285                 Global.setColumnPosition("noteTableGuidPosition", position);
1286                 position = noteTableView.header.visualIndex(Global.noteTableThumbnailPosition);
1287                 Global.setColumnPosition("noteTableThumbnailPosition", position);
1288
1289         }
1290         // Save column widths for the next time
1291         private void saveNoteIndexWidth() {
1292                 int width;
1293         width = noteTableView.getColumnWidth(Global.noteTableCreationPosition);
1294         Global.setColumnWidth("noteTableCreationPosition", width);
1295                 width = noteTableView.getColumnWidth(Global.noteTableChangedPosition);
1296                 Global.setColumnWidth("noteTableChangedPosition", width);
1297                 width = noteTableView.getColumnWidth(Global.noteTableGuidPosition);
1298                 Global.setColumnWidth("noteTableGuidPosition", width);
1299                 width = noteTableView.getColumnWidth(Global.noteTableNotebookPosition);
1300                 Global.setColumnWidth("noteTableNotebookPosition", width);
1301                 width = noteTableView.getColumnWidth(Global.noteTableTagPosition);
1302                 Global.setColumnWidth("noteTableTagPosition", width);
1303                 width = noteTableView.getColumnWidth(Global.noteTableTitlePosition);
1304                 Global.setColumnWidth("noteTableTitlePosition", width);
1305                 width = noteTableView.getColumnWidth(Global.noteTableSourceUrlPosition);
1306                 Global.setColumnWidth("noteTableSourceUrlPosition", width);
1307                 width = noteTableView.getColumnWidth(Global.noteTableAuthorPosition);
1308                 Global.setColumnWidth("noteTableAuthorPosition", width);
1309                 width = noteTableView.getColumnWidth(Global.noteTableSubjectDatePosition);
1310                 Global.setColumnWidth("noteTableSubjectDatePosition", width);
1311                 width = noteTableView.getColumnWidth(Global.noteTableSynchronizedPosition);
1312                 Global.setColumnWidth("noteTableSynchronizedPosition", width);
1313                 width = noteTableView.getColumnWidth(Global.noteTableThumbnailPosition);
1314                 Global.setColumnWidth("noteTableThumbnailPosition", width);
1315                 width = noteTableView.getColumnWidth(Global.noteTableGuidPosition);
1316                 Global.setColumnWidth("noteTableGuidPosition", width);
1317         }
1318         
1319         @SuppressWarnings("unused")
1320         private void toggleSearchWindow() {
1321                 logger.log(logger.HIGH, "Entering NeverNote.toggleSearchWindow");
1322         searchLayout.toggleSearchField();
1323         menuBar.hideSearch.setChecked(searchField.isVisible());
1324         Global.saveWindowVisible("searchField", searchField.isVisible());
1325         logger.log(logger.HIGH, "Leaving NeverNote.toggleSearchWindow");
1326     }   
1327         @SuppressWarnings("unused")
1328         private void toggleQuotaWindow() {
1329                 logger.log(logger.HIGH, "Entering NeverNote.toggleQuotaWindow");
1330         searchLayout.toggleQuotaBar();
1331         menuBar.hideQuota.setChecked(quotaBar.isVisible());
1332         Global.saveWindowVisible("quota", quotaBar.isVisible());
1333         logger.log(logger.HIGH, "Leaving NeverNote.toggleQuotaWindow");
1334     }   
1335         @SuppressWarnings("unused")
1336         private void toggleZoomWindow() {
1337                 logger.log(logger.HIGH, "Entering NeverNote.toggleZoomWindow");
1338         searchLayout.toggleZoom();
1339         menuBar.hideZoom.setChecked(zoomSpinner.isVisible());
1340         Global.saveWindowVisible("zoom", zoomSpinner.isVisible());
1341         logger.log(logger.HIGH, "Leaving NeverNote.toggleZoomWindow");
1342     }   
1343         
1344         
1345         
1346     //***************************************************************
1347     //***************************************************************
1348     //** These functions deal with Notebook menu items
1349     //***************************************************************
1350     //***************************************************************
1351     // Setup the tree containing the user's notebooks.
1352     private void initializeNotebookTree() {       
1353         logger.log(logger.HIGH, "Entering NeverNote.initializeNotebookTree");
1354 //      notebookTree.itemClicked.connect(this, "notebookTreeSelection()");
1355         notebookTree.selectionSignal.connect(this, "notebookTreeSelection()");
1356         listManager.notebookSignal.refreshNotebookTreeCounts.connect(notebookTree, "updateCounts(List, List)");
1357         logger.log(logger.HIGH, "Leaving NeverNote.initializeNotebookTree");
1358     }   
1359     // Listener when a notebook is selected
1360         private void notebookTreeSelection() {
1361                 logger.log(logger.HIGH, "Entering NeverNote.notebookTreeSelection");
1362
1363                 clearTrashFilter();
1364                 clearAttributeFilter();
1365                 clearSavedSearchFilter();
1366                 if (Global.mimicEvernoteInterface) {
1367                         clearTagFilter();
1368                         searchField.clear();
1369                 }
1370                 menuBar.noteRestoreAction.setVisible(false);            
1371         menuBar.notebookEditAction.setEnabled(true);
1372         menuBar.notebookDeleteAction.setEnabled(true);
1373         menuBar.notebookPublishAction.setEnabled(true);
1374         menuBar.notebookShareAction.setEnabled(true);
1375         menuBar.notebookIconAction.setEnabled(true);
1376         menuBar.notebookStackAction.setEnabled(true);
1377         List<QTreeWidgetItem> selections = notebookTree.selectedItems();
1378         selectedNotebookGUIDs.clear();
1379                 String guid = "";
1380                 String stackName = "";
1381                 if (selections.size() > 0) {
1382                 guid = (selections.get(0).text(2));
1383                 stackName = selections.get(0).text(0);
1384         }
1385                 if (!Global.mimicEvernoteInterface) {
1386                         // If no notebooks are selected, we make it look like the "all notebooks" one was selected
1387                         if (selections.size()==0) {
1388                                 selectedNotebookGUIDs.clear();
1389                                 for (int i=0; i < listManager.getNotebookIndex().size(); i++) {
1390                                         selectedNotebookGUIDs.add(listManager.getNotebookIndex().get(i).getGuid());
1391                                 }
1392                                 menuBar.notebookEditAction.setEnabled(false);
1393                                 menuBar.notebookDeleteAction.setEnabled(false);
1394                                 menuBar.notebookStackAction.setEnabled(false);
1395                                 menuBar.notebookIconAction.setEnabled(false);
1396                         }
1397                 }
1398         if (!guid.equals("") && !guid.equals("STACK")) {
1399                 selectedNotebookGUIDs.add(guid);
1400                 menuBar.notebookIconAction.setEnabled(true);
1401         } else {
1402                 menuBar.notebookIconAction.setEnabled(true);
1403                         for (int j=0; j<listManager.getNotebookIndex().size(); j++) {
1404                                 Notebook book = listManager.getNotebookIndex().get(j);
1405                                 if (book.getStack() != null && book.getStack().equalsIgnoreCase(stackName))
1406                                         selectedNotebookGUIDs.add(book.getGuid());
1407                         }
1408         }
1409         listManager.setSelectedNotebooks(selectedNotebookGUIDs);
1410         listManager.loadNotesIndex();
1411         noteIndexUpdated(false);
1412         refreshEvernoteNote(true);
1413         listManager.refreshCounters = true;
1414         listManager.refreshCounters();
1415                 logger.log(logger.HIGH, "Leaving NeverNote.notebookTreeSelection");
1416
1417     }
1418     private void clearNotebookFilter() {
1419         notebookTree.blockSignals(true);
1420         notebookTree.clearSelection();
1421                 menuBar.noteRestoreAction.setVisible(false);
1422         menuBar.notebookEditAction.setEnabled(false);
1423         menuBar.notebookDeleteAction.setEnabled(false);
1424         selectedNotebookGUIDs.clear();
1425         listManager.setSelectedNotebooks(selectedNotebookGUIDs);
1426         notebookTree.blockSignals(false);
1427     }
1428         // Triggered when the notebook DB has been updated
1429         private void notebookIndexUpdated() {
1430                 logger.log(logger.HIGH, "Entering NeverNote.notebookIndexUpdated");
1431         
1432                 // Get the possible icons
1433                 HashMap<String, QIcon> icons = conn.getNotebookTable().getAllIcons();
1434         notebookTree.setIcons(icons);
1435         
1436         if (selectedNotebookGUIDs == null)
1437                         selectedNotebookGUIDs = new ArrayList<String>();
1438                 List<Notebook> books = conn.getNotebookTable().getAll();
1439                 for (int i=books.size()-1; i>=0; i--) {
1440                         for (int j=0; j<listManager.getArchiveNotebookIndex().size(); j++) {
1441                                 if (listManager.getArchiveNotebookIndex().get(j).getGuid().equals(books.get(i).getGuid())) {
1442                                         books.remove(i);
1443                                         j=listManager.getArchiveNotebookIndex().size();
1444                                 }
1445                         }
1446                 }
1447                 
1448                 
1449                 listManager.countNotebookResults(listManager.getNoteIndex());
1450                 notebookTree.blockSignals(true);
1451         notebookTree.load(books, listManager.getLocalNotebooks());
1452         for (int i=selectedNotebookGUIDs.size()-1; i>=0; i--) {
1453                 boolean found = notebookTree.selectGuid(selectedNotebookGUIDs.get(i));
1454                 if (!found)
1455                         selectedNotebookGUIDs.remove(i);
1456         }
1457         listManager.refreshCounters = true;
1458         listManager.refreshCounters();
1459         notebookTree.blockSignals(false);
1460         
1461                 logger.log(logger.HIGH, "Leaving NeverNote.notebookIndexUpdated");
1462     }
1463     // Show/Hide note information
1464         @SuppressWarnings("unused")
1465         private void toggleNotebookWindow() {
1466                 logger.log(logger.HIGH, "Entering NeverNote.toggleNotebookWindow");
1467                 searchLayout.toggleNotebook();
1468         menuBar.hideNotebooks.setChecked(notebookTree.isVisible());
1469         Global.saveWindowVisible("notebookTree", notebookTree.isVisible());
1470         logger.log(logger.HIGH, "Leaving NeverNote.toggleNotebookWindow");
1471     }   
1472         // Add a new notebook
1473         @SuppressWarnings("unused")
1474         private void addNotebook() {
1475                 logger.log(logger.HIGH, "Inside NeverNote.addNotebook");
1476                 NotebookEdit edit = new NotebookEdit();
1477                 edit.setNotebooks(listManager.getNotebookIndex());
1478                 edit.exec();
1479         
1480                 if (!edit.okPressed())
1481                         return;
1482         
1483                 Calendar currentTime = new GregorianCalendar();
1484                 Long l = new Long(currentTime.getTimeInMillis());
1485                 String randint = new String(Long.toString(l));
1486         
1487                 Notebook newBook = new Notebook();
1488                 newBook.setUpdateSequenceNum(0);
1489                 newBook.setGuid(randint);
1490                 newBook.setName(edit.getNotebook());
1491                 newBook.setServiceCreated(new Date().getTime());
1492                 newBook.setServiceUpdated(new Date().getTime());
1493                 newBook.setDefaultNotebook(false);
1494                 newBook.setPublished(false);
1495                 
1496                 listManager.getNotebookIndex().add(newBook);
1497                 if (edit.isLocal())
1498                         listManager.getLocalNotebooks().add(newBook.getGuid());
1499                 conn.getNotebookTable().addNotebook(newBook, true, edit.isLocal());
1500                 notebookIndexUpdated();
1501                 listManager.countNotebookResults(listManager.getNoteIndex());
1502 //              notebookTree.updateCounts(listManager.getNotebookIndex(), listManager.getNotebookCounter());
1503                 logger.log(logger.HIGH, "Leaving NeverNote.addNotebook");
1504         }
1505         // Edit an existing notebook
1506         @SuppressWarnings("unused")
1507         private void stackNotebook() {
1508                 logger.log(logger.HIGH, "Entering NeverNote.stackNotebook");
1509                 StackNotebook edit = new StackNotebook();
1510                 
1511                 List<QTreeWidgetItem> selections = notebookTree.selectedItems();
1512                 QTreeWidgetItem currentSelection;
1513                 for (int i=0; i<selections.size(); i++) {
1514                         currentSelection = selections.get(0);
1515                         String guid = currentSelection.text(2);
1516                         if (guid.equalsIgnoreCase("")) {
1517                                  QMessageBox.critical(this, tr("Unable To Stack") ,tr("You can't stack the \"All Notebooks\" item."));
1518                                  return;
1519                         }
1520                         if (guid.equalsIgnoreCase("STACK")) {
1521                                  QMessageBox.critical(this, tr("Unable To Stack") ,tr("You can't stack a stack."));
1522                                  return;
1523                         }
1524                 }
1525
1526                 edit.setStackNames(conn.getNotebookTable().getAllStackNames());
1527
1528                 
1529                 edit.exec();
1530         
1531                 if (!edit.okPressed())
1532                         return;
1533         
1534                 String stack = edit.getStackName();
1535                 
1536                 for (int i=0; i<selections.size(); i++) {
1537                         currentSelection = selections.get(i);
1538                         String guid = currentSelection.text(2);
1539                         listManager.updateNotebookStack(guid, stack);
1540                 }
1541                 notebookIndexUpdated();
1542                 logger.log(logger.HIGH, "Leaving NeverNote.stackNotebook");
1543         }
1544         // Edit an existing notebook
1545         @SuppressWarnings("unused")
1546         private void editNotebook() {
1547                 logger.log(logger.HIGH, "Entering NeverNote.editNotebook");
1548                 NotebookEdit edit = new NotebookEdit();
1549                 
1550                 List<QTreeWidgetItem> selections = notebookTree.selectedItems();
1551                 QTreeWidgetItem currentSelection;
1552                 currentSelection = selections.get(0);
1553                 edit.setNotebook(currentSelection.text(0));
1554                 
1555                 String guid = currentSelection.text(2);
1556                 if (!guid.equalsIgnoreCase("STACK")) {
1557                         edit.setTitle(tr("Edit Notebook"));
1558                         edit.setNotebooks(listManager.getNotebookIndex());
1559                         edit.setLocalCheckboxEnabled(false);
1560                         for (int i=0; i<listManager.getNotebookIndex().size(); i++) {
1561                                 if (listManager.getNotebookIndex().get(i).getGuid().equals(guid)) {
1562                                         edit.setDefaultNotebook(listManager.getNotebookIndex().get(i).isDefaultNotebook());
1563                                         i=listManager.getNotebookIndex().size();
1564                                 }
1565                         }
1566                 } else {
1567                         edit.setTitle(tr("Edit Stack"));
1568                         edit.setStacks(conn.getNotebookTable().getAllStackNames());
1569                         edit.hideLocalCheckbox();
1570                         edit.hideDefaultCheckbox();
1571                 }
1572                 
1573                 edit.exec();
1574         
1575                 if (!edit.okPressed())
1576                         return;
1577         
1578                 
1579                 if (guid.equalsIgnoreCase("STACK")) {
1580                         conn.getNotebookTable().renameStacks(currentSelection.text(0), edit.getNotebook());
1581                         for (int j=0; j<listManager.getNotebookIndex().size(); j++) {
1582                                 if (listManager.getNotebookIndex().get(j).getStack() != null && 
1583                                         listManager.getNotebookIndex().get(j).getStack().equalsIgnoreCase(currentSelection.text(0)))
1584                                                 listManager.getNotebookIndex().get(j).setStack(edit.getNotebook());
1585                         }
1586                         conn.getNotebookTable().renameStacks(currentSelection.text(0), edit.getNotebook());
1587                         currentSelection.setText(0, edit.getNotebook());
1588                         return;
1589                 }
1590                 
1591                 updateListNotebookName(currentSelection.text(0), edit.getNotebook());
1592                 currentSelection.setText(0, edit.getNotebook());
1593                 
1594                 for (int i=0; i<listManager.getNotebookIndex().size(); i++) {
1595                         if (listManager.getNotebookIndex().get(i).getGuid().equals(guid)) {
1596                                 listManager.getNotebookIndex().get(i).setName(edit.getNotebook());
1597                                 if (!listManager.getNotebookIndex().get(i).isDefaultNotebook() && edit.isDefaultNotebook()) {
1598                                         for (int j=0; j<listManager.getNotebookIndex().size(); j++)
1599                                                 listManager.getNotebookIndex().get(j).setDefaultNotebook(false);
1600                                         listManager.getNotebookIndex().get(i).setDefaultNotebook(true);
1601                                         conn.getNotebookTable().setDefaultNotebook(listManager.getNotebookIndex().get(i).getGuid());
1602                                 }
1603                                 conn.getNotebookTable().updateNotebook(listManager.getNotebookIndex().get(i), true);
1604                                 if (conn.getNotebookTable().isLinked(listManager.getNotebookIndex().get(i).getGuid())) {
1605                                         LinkedNotebook linkedNotebook = conn.getLinkedNotebookTable().getByNotebookGuid(listManager.getNotebookIndex().get(i).getGuid());
1606                                         linkedNotebook.setShareName(edit.getNotebook());
1607                                         conn.getLinkedNotebookTable().updateNotebook(linkedNotebook, true);
1608                                 }
1609                                 i=listManager.getNotebookIndex().size();
1610                         }
1611                 }
1612                 
1613                 // Build a list of non-closed notebooks
1614                 List<Notebook> nbooks = new ArrayList<Notebook>();
1615                 for (int i=0; i<listManager.getNotebookIndex().size(); i++) {
1616                         boolean found=false;
1617                         for (int j=0; j<listManager.getArchiveNotebookIndex().size(); j++) {
1618                                 if (listManager.getArchiveNotebookIndex().get(j).getGuid().equals(listManager.getNotebookIndex().get(i).getGuid()))
1619                                         found = true;
1620                         }
1621                         if (!found)
1622                                 nbooks.add(listManager.getNotebookIndex().get(i));
1623                 }
1624                 
1625                 
1626                 FilterEditorNotebooks notebookFilter = new FilterEditorNotebooks(conn, logger);
1627                 List<Notebook> filteredBooks = notebookFilter.getValidNotebooks(currentNote, listManager.getNotebookIndex());
1628                 browserWindow.setNotebookList(filteredBooks);
1629                 Iterator<String> set = externalWindows.keySet().iterator();
1630                 while(set.hasNext())
1631                         externalWindows.get(set.next()).getBrowserWindow().setNotebookList(filteredBooks);
1632                 logger.log(logger.HIGH, "Leaving NeverNote.editNotebook");
1633         }
1634         // Publish a notebook
1635         @SuppressWarnings("unused")
1636         private void publishNotebook() {
1637                 List<QTreeWidgetItem> selections = notebookTree.selectedItems();
1638                 QTreeWidgetItem currentSelection;
1639                 currentSelection = selections.get(0);
1640                 String guid = currentSelection.text(2);
1641
1642                 if (guid.equalsIgnoreCase("STACK") || guid.equalsIgnoreCase(""))
1643                         return;
1644                 
1645                 Notebook n = null;
1646                 int position = 0;
1647                 for (int i=0; i<listManager.getNotebookIndex().size(); i++) {
1648                         if (guid.equals(listManager.getNotebookIndex().get(i).getGuid())) {
1649                                 n = listManager.getNotebookIndex().get(i);
1650                                 position = i;
1651                                 i = listManager.getNotebookIndex().size();
1652                         }
1653                 }
1654                 if (n == null)
1655                         return;
1656                 
1657                 PublishNotebook publish = new PublishNotebook(Global.username, Global.getServer(), n);
1658                 publish.exec();
1659                 
1660                 if (!publish.okClicked()) 
1661                         return;
1662                 
1663                 Publishing p = publish.getPublishing();
1664                 boolean isPublished = !publish.isStopPressed();
1665                 conn.getNotebookTable().setPublishing(n.getGuid(), isPublished, p);
1666                 n.setPublished(isPublished);
1667                 n.setPublishing(p);
1668                 listManager.getNotebookIndex().set(position, n);
1669                 notebookIndexUpdated();
1670         }
1671         // Publish a notebook
1672         @SuppressWarnings("unused")
1673         private void shareNotebook() {
1674                 List<QTreeWidgetItem> selections = notebookTree.selectedItems();
1675                 QTreeWidgetItem currentSelection;
1676                 currentSelection = selections.get(0);
1677                 String guid = currentSelection.text(2);
1678
1679                 if (guid.equalsIgnoreCase("STACK") || guid.equalsIgnoreCase(""))
1680                         return;
1681                 
1682                 Notebook n = null;;
1683                 for (int i=0; i<listManager.getNotebookIndex().size(); i++) {
1684                         if (guid.equals(listManager.getNotebookIndex().get(i).getGuid())) {
1685                                 n = listManager.getNotebookIndex().get(i);
1686                                 i = listManager.getNotebookIndex().size();
1687                         }
1688                 }
1689                                 
1690                 String authToken = null;
1691                 if (syncRunner.isConnected)
1692                         authToken = syncRunner.authToken;
1693                 ShareNotebook share = new ShareNotebook(n.getName(), conn, n, syncRunner);
1694                 share.exec();
1695                 
1696         }
1697
1698         // Delete an existing notebook
1699         @SuppressWarnings("unused")
1700         private void deleteNotebook() {
1701                 logger.log(logger.HIGH, "Entering NeverNote.deleteNotebook");
1702                 boolean stacksFound = false;
1703                 boolean notebooksFound = false;
1704                 boolean assigned = false;
1705                 // Check if any notes have this notebook
1706                 List<QTreeWidgetItem> selections = notebookTree.selectedItems();
1707         for (int i=0; i<selections.size(); i++) {
1708                 QTreeWidgetItem currentSelection;
1709                 currentSelection = selections.get(i);
1710                 String guid = currentSelection.text(2);
1711                 if (!guid.equalsIgnoreCase("STACK")) {
1712                         notebooksFound = true;
1713                         for (int j=0; j<listManager.getNoteIndex().size(); j++) {
1714                                 String noteGuid = listManager.getNoteIndex().get(j).getNotebookGuid();
1715                                 if (noteGuid.equals(guid)) {
1716                                         assigned = true;
1717                                         j=listManager.getNoteIndex().size();
1718                                         i=selections.size();
1719                                 }
1720                         }
1721                 } else {
1722                         stacksFound = true;
1723                 }
1724         }
1725                 if (assigned) {
1726                         QMessageBox.information(this, tr("Unable to Delete"), tr("Some of the selected notebook(s) contain notes.\n"+
1727                                         "Please delete the notes or move them to another notebook before deleting any notebooks."));
1728                         return;
1729                 }
1730                 
1731                 if (conn.getNotebookTable().getAll().size() == 1) {
1732                         QMessageBox.information(this, tr("Unable to Delete"), tr("You must have at least one notebook."));
1733                         return;
1734                 }
1735         
1736         // If all notebooks are clear, verify the delete
1737                 String msg1 = new String(tr("Delete selected notebooks?"));
1738                 String msg2 = new String(tr("Remove selected stacks (notebooks will not be deleted)?"));
1739                 String msg3 = new String(tr("Delete selected notebooks & remove stacks? Notebooks under the stacks are" +
1740                                 " not deleted unless selected?"));
1741                 String msg = "";
1742                 if (stacksFound && notebooksFound)
1743                         msg = msg3;
1744                 if (!stacksFound && notebooksFound)
1745                         msg = msg1;
1746                 if (stacksFound && !notebooksFound)
1747                         msg = msg2;
1748                 if (QMessageBox.question(this, tr("Confirmation"), msg,
1749                         QMessageBox.StandardButton.Yes, 
1750                         QMessageBox.StandardButton.No)==StandardButton.No.value()) {
1751                         return;
1752                 }
1753                 
1754                 // If confirmed, delete the notebook
1755         for (int i=selections.size()-1; i>=0; i--) {
1756                 QTreeWidgetItem currentSelection;
1757                 currentSelection = selections.get(i);
1758                 String guid = currentSelection.text(2);
1759                 if (currentSelection.text(2).equalsIgnoreCase("STACK")) {
1760                         conn.getNotebookTable().renameStacks(currentSelection.text(0), "");
1761                         listManager.renameStack(currentSelection.text(0), "");
1762                 } else {
1763                         conn.getNotebookTable().expungeNotebook(guid, true);
1764                         listManager.deleteNotebook(guid);
1765                 }
1766         }
1767
1768                 notebookIndexUpdated();
1769 //        notebookTreeSelection();
1770 //        notebookTree.load(listManager.getNotebookIndex(), listManager.getLocalNotebooks());
1771 //        listManager.countNotebookResults(listManager.getNoteIndex());
1772         logger.log(logger.HIGH, "Entering NeverNote.deleteNotebook");
1773         }
1774         // A note's notebook has been updated
1775         @SuppressWarnings("unused")
1776         private void updateNoteNotebook(String guid, String notebookGuid) {
1777                 
1778                 // Update the list manager
1779                 listManager.updateNoteNotebook(guid, notebookGuid);
1780                 listManager.countNotebookResults(listManager.getNoteIndex());
1781 //              notebookTree.updateCounts(listManager.getNotebookIndex(), listManager.getNotebookCounter());    
1782                 
1783                 // Find the name of the notebook
1784                 String notebookName = null;
1785                 for (int i=0; i<listManager.getNotebookIndex().size(); i++) {
1786                         if (listManager.getNotebookIndex().get(i).getGuid().equals(notebookGuid)) {
1787                                 notebookName = listManager.getNotebookIndex().get(i).getName();
1788                                 break;
1789                         }
1790                 }
1791                 
1792                 // If we found the name, update the browser window
1793                 if (notebookName != null) {
1794                         updateListNoteNotebook(guid, notebookName);
1795                         if (guid.equals(currentNoteGuid)) {
1796                                 int pos =  browserWindow.notebookBox.findText(notebookName);
1797                                 if (pos >=0)
1798                                         browserWindow.notebookBox.setCurrentIndex(pos);
1799                         }
1800                 }
1801                 
1802                 // If we're dealing with the current note, then we need to be sure and update the notebook there
1803                 if (guid.equals(currentNoteGuid)) {
1804                         if (currentNote != null) {
1805                                 currentNote.setNotebookGuid(notebookGuid);
1806                         }
1807                 }
1808         }
1809         // Open/close notebooks
1810         @SuppressWarnings("unused")
1811         private void closeNotebooks() {
1812                 NotebookArchive na = new NotebookArchive(listManager.getNotebookIndex(), listManager.getArchiveNotebookIndex());
1813                 na.exec();
1814                 if (!na.okClicked())
1815                         return;
1816                 
1817                 waitCursor(true);
1818                 listManager.getArchiveNotebookIndex().clear();
1819                 
1820                 for (int i=na.getClosedBookList().count()-1; i>=0; i--) {
1821                         String text = na.getClosedBookList().takeItem(i).text();
1822                         for (int j=0; j<listManager.getNotebookIndex().size(); j++) {
1823                                 if (listManager.getNotebookIndex().get(j).getName().equalsIgnoreCase(text)) {
1824                                         Notebook n = listManager.getNotebookIndex().get(j);
1825                                         conn.getNotebookTable().setArchived(n.getGuid(),true);
1826                                         listManager.getArchiveNotebookIndex().add(n);
1827                                         j=listManager.getNotebookIndex().size();
1828                                 }
1829                         }
1830                 }
1831                 
1832                 for (int i=na.getOpenBookList().count()-1; i>=0; i--) {
1833                         String text = na.getOpenBookList().takeItem(i).text();
1834                         for (int j=0; j<listManager.getNotebookIndex().size(); j++) {
1835                                 if (listManager.getNotebookIndex().get(j).getName().equalsIgnoreCase(text)) {
1836                                         Notebook n = listManager.getNotebookIndex().get(j);
1837                                         conn.getNotebookTable().setArchived(n.getGuid(),false);
1838                                         j=listManager.getNotebookIndex().size();
1839                                 }
1840                         }
1841                 }
1842                 notebookTreeSelection();
1843                 listManager.loadNotesIndex();
1844                 notebookIndexUpdated();
1845                 noteIndexUpdated(false);
1846                 reloadTagTree(true);
1847 //              noteIndexUpdated(false);
1848                 
1849                 // Build a list of non-closed notebooks
1850                 List<Notebook> nbooks = new ArrayList<Notebook>();
1851                 for (int i=0; i<listManager.getNotebookIndex().size(); i++) {
1852                         boolean found=false;
1853                         for (int j=0; j<listManager.getArchiveNotebookIndex().size(); j++) {
1854                                 if (listManager.getArchiveNotebookIndex().get(j).getGuid().equals(listManager.getNotebookIndex().get(i).getGuid()))
1855                                         found = true;
1856                         }
1857                         if (!found)
1858                                 nbooks.add(listManager.getNotebookIndex().get(i));
1859                 }
1860                 
1861                 FilterEditorNotebooks notebookFilter = new FilterEditorNotebooks(conn, logger);
1862                 List<Notebook> filteredBooks = notebookFilter.getValidNotebooks(currentNote, listManager.getNotebookIndex());
1863                 browserWindow.setNotebookList(filteredBooks);
1864                 
1865                 // Update any external windows
1866                 Iterator<String> set = externalWindows.keySet().iterator();
1867                 while(set.hasNext())
1868                         externalWindows.get(set.next()).getBrowserWindow().setNotebookList(filteredBooks);
1869                 
1870                 waitCursor(false);
1871         }
1872         // Change the notebook's icon
1873         @SuppressWarnings("unused")
1874         private void setNotebookIcon() {
1875                 boolean stackSelected = false;
1876                 boolean allNotebookSelected = false;
1877                 
1878                 QTreeWidgetItem currentSelection;
1879                 List<QTreeWidgetItem> selections = notebookTree.selectedItems();
1880                 if (selections.size() == 0)
1881                         return;
1882                 
1883                 currentSelection = selections.get(0);   
1884                 String guid = currentSelection.text(2);
1885                 if (guid.equalsIgnoreCase(""))
1886                         allNotebookSelected = true;
1887                 if (guid.equalsIgnoreCase("STACK"))
1888                         stackSelected = true;
1889
1890                 QIcon currentIcon = currentSelection.icon(0);
1891                 QIcon icon;
1892                 SetIcon dialog;
1893                 
1894                 if (!stackSelected && !allNotebookSelected) {
1895                         icon = conn.getNotebookTable().getIcon(guid);
1896                         if (icon == null) {
1897                                 dialog = new SetIcon(currentIcon, saveLastPath);
1898                                 dialog.setUseDefaultIcon(true);
1899                         } else {
1900                                 dialog = new SetIcon(icon, saveLastPath);
1901                                 dialog.setUseDefaultIcon(false);
1902                         }
1903                 } else {
1904                         if (stackSelected) {
1905                                 icon = conn.getSystemIconTable().getIcon(currentSelection.text(0), "STACK");
1906                         } else {
1907                                 icon = conn.getSystemIconTable().getIcon(currentSelection.text(0), "ALLNOTEBOOK");                              
1908                         }
1909                         if (icon == null) {
1910                                 dialog = new SetIcon(currentIcon, saveLastPath);
1911                                 dialog.setUseDefaultIcon(true);
1912                         } else {
1913                                 dialog = new SetIcon(icon, saveLastPath);
1914                                 dialog.setUseDefaultIcon(false);
1915                         }
1916                 }
1917                 dialog.exec();
1918                 if (dialog.okPressed()) {
1919                 saveLastPath = dialog.getPath();
1920
1921                         QIcon newIcon = dialog.getIcon();
1922                         if (stackSelected) {
1923                                 conn.getSystemIconTable().setIcon(currentSelection.text(0), "STACK", newIcon, dialog.getFileType());
1924                                 if (newIcon == null) {
1925                                         newIcon = new QIcon(iconPath+"books2.png");
1926                                 }
1927                                 currentSelection.setIcon(0,newIcon);
1928                                 return;
1929                         }
1930                         if (allNotebookSelected) {
1931                                 conn.getSystemIconTable().setIcon(currentSelection.text(0), "ALLNOTEBOOK", newIcon, dialog.getFileType());
1932                                 if (newIcon == null) {
1933                                         newIcon = new QIcon(iconPath+"notebook-green.png");
1934                                 }
1935                                 currentSelection.setIcon(0,newIcon);
1936                                 return;
1937                         }
1938                         conn.getNotebookTable().setIcon(guid, newIcon, dialog.getFileType());
1939                         if (newIcon == null) {
1940                                 boolean isPublished = false;;
1941                                 boolean found = false;
1942                                 for (int i=0; i<listManager.getNotebookIndex().size() && !found; i++) {
1943                                         if (listManager.getNotebookIndex().get(i).getGuid().equals(guid)) {
1944                                                 isPublished = listManager.getNotebookIndex().get(i).isPublished();
1945                                                 found = true;
1946                                         }
1947                                 }
1948                                 newIcon = notebookTree.findDefaultIcon(guid, currentSelection.text(1), listManager.getLocalNotebooks(), isPublished);
1949                         }
1950                         currentSelection.setIcon(0, newIcon);
1951                 }
1952         
1953         }
1954         
1955         
1956     //***************************************************************
1957     //***************************************************************
1958     //** These functions deal with Tag menu items
1959     //***************************************************************
1960     //***************************************************************
1961         // Add a new notebook
1962         @SuppressWarnings("unused")
1963         private void addTag() {
1964                 logger.log(logger.HIGH, "Inside NeverNote.addTag");
1965                 TagEdit edit = new TagEdit();
1966                 edit.setTagList(listManager.getTagIndex());
1967
1968                 List<QTreeWidgetItem> selections = tagTree.selectedItems();
1969                 QTreeWidgetItem currentSelection = null;
1970                 if (selections.size() > 0) {
1971                         currentSelection = selections.get(0);
1972                         edit.setParentTag(currentSelection.text(0));
1973                 }
1974
1975                 edit.exec();
1976         
1977                 if (!edit.okPressed())
1978                         return;
1979         
1980                 Calendar currentTime = new GregorianCalendar();
1981                 Long l = new Long(currentTime.getTimeInMillis());
1982                 String randint = new String(Long.toString(l));
1983         
1984                 Tag newTag = new Tag();
1985                 newTag.setUpdateSequenceNum(0);
1986                 newTag.setGuid(randint);
1987                 newTag.setName(edit.getTag());
1988                 if (edit.getParentTag().isChecked()) {
1989                         newTag.setParentGuid(currentSelection.text(2));
1990                         newTag.setParentGuidIsSet(true);
1991                         currentSelection.setExpanded(true);
1992                 }
1993                 conn.getTagTable().addTag(newTag, true);
1994                 listManager.getTagIndex().add(newTag);
1995                 reloadTagTree(true);
1996                 
1997                 logger.log(logger.HIGH, "Leaving NeverNote.addTag");
1998         }
1999         @SuppressWarnings("unused")
2000         private void reloadTagTree() {
2001                 reloadTagTree(false);
2002         }
2003         private void reloadTagTree(boolean reload) {
2004                 logger.log(logger.HIGH, "Entering NeverNote.reloadTagTree");
2005                 tagIndexUpdated(reload);
2006                 boolean filter = false;
2007                 if (reload)
2008                         listManager.countTagResults(listManager.getNoteIndex());
2009                 if (notebookTree.selectedItems().size() > 0 
2010                                                   && !notebookTree.selectedItems().get(0).text(0).equalsIgnoreCase("All Notebooks"))
2011                                                   filter = true;
2012                 if (tagTree.selectedItems().size() > 0)
2013                         filter = true;
2014                 tagTree.showAllTags(!filter);
2015                 logger.log(logger.HIGH, "Leaving NeverNote.reloadTagTree");
2016         }
2017         // Edit an existing tag
2018         @SuppressWarnings("unused")
2019         private void editTag() {
2020                 logger.log(logger.HIGH, "Entering NeverNote.editTag");
2021                 TagEdit edit = new TagEdit();
2022                 edit.setTitle("Edit Tag");
2023                 List<QTreeWidgetItem> selections = tagTree.selectedItems();
2024                 QTreeWidgetItem currentSelection;
2025                 currentSelection = selections.get(0);
2026                 edit.setTag(currentSelection.text(0));
2027                 edit.setTagList(listManager.getTagIndex());
2028                 edit.exec();
2029         
2030                 if (!edit.okPressed())
2031                         return;
2032         
2033                 String guid = currentSelection.text(2);
2034                 currentSelection.setText(0,edit.getTag());
2035                 
2036                 for (int i=0; i<listManager.getTagIndex().size(); i++) {
2037                         if (listManager.getTagIndex().get(i).getGuid().equals(guid)) {
2038                                 listManager.getTagIndex().get(i).setName(edit.getTag());
2039                                 conn.getTagTable().updateTag(listManager.getTagIndex().get(i), true);
2040                                 updateListTagName(guid);
2041                                 if (currentNote != null && currentNote.getTagGuids().contains(guid))
2042                                         browserWindow.setTag(getTagNamesForNote(currentNote));
2043                                 logger.log(logger.HIGH, "Leaving NeverNote.editTag");
2044                                 return;
2045                         }
2046                 }
2047                 browserWindow.setTag(getTagNamesForNote(currentNote));
2048                 logger.log(logger.HIGH, "Leaving NeverNote.editTag...");
2049         }
2050         // Delete an existing tag
2051         @SuppressWarnings("unused")
2052         private void deleteTag() {
2053                 logger.log(logger.HIGH, "Entering NeverNote.deleteTag");
2054                 
2055                 if (QMessageBox.question(this, tr("Confirmation"), tr("Delete the selected tags?"),
2056                         QMessageBox.StandardButton.Yes, 
2057                         QMessageBox.StandardButton.No)==StandardButton.No.value()) {
2058                                                         return;
2059                 }
2060                 
2061                 List<QTreeWidgetItem> selections = tagTree.selectedItems();
2062         for (int i=selections.size()-1; i>=0; i--) {
2063                 QTreeWidgetItem currentSelection;
2064                 currentSelection = selections.get(i);                   
2065                 removeTagItem(currentSelection.text(2));
2066         }
2067         tagIndexUpdated(true);
2068         tagTreeSelection();
2069         listManager.countTagResults(listManager.getNoteIndex());
2070 //              tagTree.updateCounts(listManager.getTagCounter());
2071         logger.log(logger.HIGH, "Leaving NeverNote.deleteTag");
2072         }
2073         // Remove a tag tree item.  Go recursively down & remove the children too
2074         private void removeTagItem(String guid) {
2075         for (int j=listManager.getTagIndex().size()-1; j>=0; j--) {             
2076                 String parent = listManager.getTagIndex().get(j).getParentGuid();
2077                 if (parent != null && parent.equals(guid)) {            
2078                         //Remove this tag's children
2079                         removeTagItem(listManager.getTagIndex().get(j).getGuid());
2080                 }
2081         }
2082         //Now, remove this tag
2083         removeListTagName(guid);
2084         conn.getTagTable().expungeTag(guid, true);                      
2085         for (int a=0; a<listManager.getTagIndex().size(); a++) {
2086                 if (listManager.getTagIndex().get(a).getGuid().equals(guid)) {
2087                         listManager.getTagIndex().remove(a);
2088                         return;
2089                 }
2090         }
2091         }
2092         // Setup the tree containing the user's tags
2093     private void initializeTagTree() {
2094         logger.log(logger.HIGH, "Entering NeverNote.initializeTagTree");
2095 //      tagTree.itemSelectionChanged.connect(this, "tagTreeSelection()");
2096 //      tagTree.itemClicked.connect(this, "tagTreeSelection()");
2097         tagTree.selectionSignal.connect(this, "tagTreeSelection()");
2098         listManager.tagSignal.refreshTagTreeCounts.connect(tagTree, "updateCounts(List)");
2099         logger.log(logger.HIGH, "Leaving NeverNote.initializeTagTree");
2100     }
2101     // Listener when a tag is selected
2102         private void tagTreeSelection() {
2103         logger.log(logger.HIGH, "Entering NeverNote.tagTreeSelection");
2104                 
2105         clearTrashFilter();
2106         clearAttributeFilter();
2107         clearSavedSearchFilter();
2108         
2109                 menuBar.noteRestoreAction.setVisible(false);
2110                 
2111         List<QTreeWidgetItem> selections = tagTree.selectedItems();
2112         QTreeWidgetItem currentSelection;
2113         selectedTagGUIDs.clear();
2114         for (int i=0; i<selections.size(); i++) {
2115                 currentSelection = selections.get(i);
2116                 selectedTagGUIDs.add(currentSelection.text(2));
2117         }
2118         if (selections.size() > 0) {
2119                 menuBar.tagEditAction.setEnabled(true);
2120                 menuBar.tagDeleteAction.setEnabled(true);
2121                 menuBar.tagIconAction.setEnabled(true);
2122         }
2123         else {
2124                 menuBar.tagEditAction.setEnabled(false);
2125                 menuBar.tagDeleteAction.setEnabled(false);
2126                 menuBar.tagIconAction.setEnabled(true);
2127         }
2128         if (selections.size() > 1)
2129                 menuBar.tagMergeAction.setEnabled(true);
2130         else
2131                 menuBar.tagMergeAction.setEnabled(false);
2132         listManager.setSelectedTags(selectedTagGUIDs);
2133         listManager.loadNotesIndex();
2134         noteIndexUpdated(false);
2135         refreshEvernoteNote(true);
2136         listManager.refreshCounters = true;
2137         listManager.refreshCounters();
2138         logger.log(logger.HIGH, "Leaving NeverNote.tagTreeSelection");
2139     }
2140     // trigger the tag index to be refreshed
2141     @SuppressWarnings("unused")
2142         private void tagIndexUpdated() {
2143         tagIndexUpdated(true);
2144     }
2145     private void tagIndexUpdated(boolean reload) {
2146         logger.log(logger.HIGH, "Entering NeverNote.tagIndexUpdated");
2147                 if (selectedTagGUIDs == null)
2148                         selectedTagGUIDs = new ArrayList<String>();
2149                 if (reload)
2150                         listManager.reloadTagIndex();
2151
2152                 tagTree.blockSignals(true);
2153                 if (reload) {
2154                         tagTree.setIcons(conn.getTagTable().getAllIcons());
2155                         tagTree.load(listManager.getTagIndex());
2156                 }
2157
2158         for (int i=selectedTagGUIDs.size()-1; i>=0; i--) {
2159                 boolean found = tagTree.selectGuid(selectedTagGUIDs.get(i));
2160                 if (!found)
2161                         selectedTagGUIDs.remove(i);
2162         }
2163         tagTree.blockSignals(false);
2164         
2165                 browserWindow.setTag(getTagNamesForNote(currentNote));
2166         logger.log(logger.HIGH, "Leaving NeverNote.tagIndexUpdated");
2167     }   
2168     // Show/Hide note information
2169         @SuppressWarnings("unused")
2170         private void toggleTagWindow() {
2171                 logger.log(logger.HIGH, "Entering NeverNote.toggleTagWindow");
2172         if (tagTree.isVisible())
2173                 tagTree.hide();
2174         else
2175                 tagTree.show();
2176         menuBar.hideTags.setChecked(tagTree.isVisible());
2177         Global.saveWindowVisible("tagTree", tagTree.isVisible());
2178         logger.log(logger.HIGH, "Leaving NeverNote.toggleTagWindow");
2179     }   
2180         // A note's tags have been updated
2181         @SuppressWarnings("unused")
2182         private void updateNoteTags(String guid, List<String> tags) {
2183                 // Save any new tags.  We'll need them later.
2184                 List<String> newTags = new ArrayList<String>();
2185                 for (int i=0; i<tags.size(); i++) {
2186                         if (conn.getTagTable().findTagByName(tags.get(i))==null) 
2187                                 newTags.add(tags.get(i));
2188                 }
2189                 
2190                 listManager.saveNoteTags(guid, tags);
2191                 listManager.countTagResults(listManager.getNoteIndex());
2192                 StringBuffer names = new StringBuffer("");
2193                 for (int i=0; i<tags.size(); i++) {
2194                         names = names.append(tags.get(i));
2195                         if (i<tags.size()-1) {
2196                                 names.append(Global.tagDelimeter + " ");
2197                         }
2198                 }
2199                 browserWindow.setTag(names.toString());
2200                 noteDirty = true;
2201                 
2202                 // Now, we need to add any new tags to the tag tree
2203                 for (int i=0; i<newTags.size(); i++) 
2204                         tagTree.insertTag(newTags.get(i), conn.getTagTable().findTagByName(newTags.get(i)));
2205         }
2206         // Get a string containing all tag names for a note
2207         private String getTagNamesForNote(Note n) {
2208                 logger.log(logger.HIGH, "Entering NeverNote.getTagNamesForNote");
2209                 if (n==null || n.getGuid() == null || n.getGuid().equals(""))
2210                         return "";
2211                 StringBuffer buffer = new StringBuffer(100);
2212                 Vector<String> v = new Vector<String>();
2213                 List<String> guids = n.getTagGuids();
2214                 
2215                 if (guids == null) 
2216                         return "";
2217                 
2218                 for (int i=0; i<guids.size(); i++) {
2219                         v.add(listManager.getTagNameByGuid(guids.get(i)));
2220                 }
2221                 Comparator<String> comparator = Collections.reverseOrder();
2222                 Collections.sort(v,comparator);
2223                 Collections.reverse(v);
2224                 
2225                 for (int i = 0; i<v.size(); i++) {
2226                         if (i>0) 
2227                                 buffer.append(", ");
2228                         buffer.append(v.get(i));
2229                 }
2230                 
2231                 logger.log(logger.HIGH, "Leaving NeverNote.getTagNamesForNote");
2232                 return buffer.toString();
2233         }       
2234         // Tags were added via dropping notes from the note list
2235         @SuppressWarnings("unused")
2236         private void tagsAdded(String noteGuid, String tagGuid) {
2237                 String tagName = null;
2238                 for (int i=0; i<listManager.getTagIndex().size(); i++) {
2239                         if (listManager.getTagIndex().get(i).getGuid().equals(tagGuid)) {
2240                                 tagName = listManager.getTagIndex().get(i).getName();
2241                                 i=listManager.getTagIndex().size();
2242                         }
2243                 }
2244                 if (tagName == null)
2245                         return;
2246                 
2247                 for (int i=0; i<listManager.getMasterNoteIndex().size(); i++) {
2248                         if (listManager.getMasterNoteIndex().get(i).getGuid().equals(noteGuid)) {
2249                                 List<String> tagNames = new ArrayList<String>();
2250                                 tagNames.add(new String(tagName));
2251                                 Note n = listManager.getMasterNoteIndex().get(i);
2252                                 for (int j=0; j<n.getTagNames().size(); j++) {
2253                                         tagNames.add(new String(n.getTagNames().get(j)));
2254                                 }
2255                                 listManager.getNoteTableModel().updateNoteTags(noteGuid, n.getTagGuids(), tagNames);
2256                                 if (n.getGuid().equals(currentNoteGuid)) {
2257                                         Collections.sort(tagNames);
2258                                         String display = "";
2259                                         for (int j=0; j<tagNames.size(); j++) {
2260                                                 display = display+tagNames.get(j);
2261                                                 if (j+2<tagNames.size()) 
2262                                                         display = display+Global.tagDelimeter+" ";
2263                                         }
2264                                         browserWindow.setTag(display);
2265                                 }
2266                                 i=listManager.getMasterNoteIndex().size();
2267                         }
2268                 }
2269                 
2270                 
2271                 listManager.getNoteTableModel().updateNoteSyncStatus(noteGuid, false);
2272         }
2273         private void clearTagFilter() {
2274                 tagTree.blockSignals(true);
2275                 tagTree.clearSelection();
2276                 menuBar.noteRestoreAction.setVisible(false);
2277                 menuBar.tagEditAction.setEnabled(false);
2278                 menuBar.tagMergeAction.setEnabled(false);
2279                 menuBar.tagDeleteAction.setEnabled(false);
2280                 menuBar.tagIconAction.setEnabled(false);
2281                 selectedTagGUIDs.clear();
2282         listManager.setSelectedTags(selectedTagGUIDs);
2283         tagTree.blockSignals(false);
2284         }
2285         // Change the icon for a tag
2286         @SuppressWarnings("unused")
2287         private void setTagIcon() {
2288                 QTreeWidgetItem currentSelection;
2289                 List<QTreeWidgetItem> selections = tagTree.selectedItems();
2290                 if (selections.size() == 0)
2291                         return;
2292                 
2293                 currentSelection = selections.get(0);   
2294                 String guid = currentSelection.text(2);
2295
2296                 QIcon currentIcon = currentSelection.icon(0);
2297                 QIcon icon = conn.getTagTable().getIcon(guid);
2298                 SetIcon dialog;
2299                 if (icon == null) {
2300                         dialog = new SetIcon(currentIcon, saveLastPath);
2301                         dialog.setUseDefaultIcon(true);
2302                 } else {
2303                         dialog = new SetIcon(icon, saveLastPath);
2304                         dialog.setUseDefaultIcon(false);
2305                 }
2306                 dialog.exec();
2307                 if (dialog.okPressed()) {
2308                 saveLastPath = dialog.getPath();
2309                         QIcon newIcon = dialog.getIcon();
2310                         conn.getTagTable().setIcon(guid, newIcon, dialog.getFileType());
2311                         if (newIcon == null) 
2312                                 newIcon = new QIcon(iconPath+"tag.png");
2313                         currentSelection.setIcon(0, newIcon);
2314                 }
2315         
2316         }
2317         // Merge tags
2318         @SuppressWarnings("unused")
2319         private void mergeTags() {
2320                 List<Tag> tags = new ArrayList<Tag>();
2321                 List<QTreeWidgetItem> selections = tagTree.selectedItems();
2322                 for (int i=0; i<selections.size(); i++) {
2323                         Tag record = new Tag();
2324                         record.setGuid(selections.get(i).text(2));
2325                         record.setName(selections.get(i).text(0));
2326                         tags.add(record);
2327                 }
2328
2329                 TagMerge mergeDialog = new TagMerge(tags);
2330                 mergeDialog.exec();
2331                 if (!mergeDialog.okClicked())
2332                         return;
2333                 String newGuid = mergeDialog.getNewTagGuid();
2334                 
2335                 for (int i=0; i<tags.size(); i++) {
2336                         if (!tags.get(i).getGuid().equals(newGuid)) {
2337                                 List<String> noteGuids = conn.getNoteTable().noteTagsTable.getTagNotes(tags.get(i).getGuid());
2338                                 for (int j=0; j<noteGuids.size(); j++) {
2339                                         String noteGuid = noteGuids.get(j);
2340                                         conn.getNoteTable().noteTagsTable.deleteNoteTag(noteGuid);
2341                                         if (!conn.getNoteTable().noteTagsTable.checkNoteNoteTags(noteGuid, newGuid))
2342                                                 conn.getNoteTable().noteTagsTable.saveNoteTag(noteGuid, newGuid);
2343                                 }
2344                         }
2345                 }
2346                 listManager.reloadIndexes();
2347         }
2348         
2349     //***************************************************************
2350     //***************************************************************
2351     //** These functions deal with Saved Search menu items
2352     //***************************************************************
2353     //***************************************************************
2354         // Add a new notebook
2355         @SuppressWarnings("unused")
2356         private void addSavedSearch() {
2357                 logger.log(logger.HIGH, "Inside NeverNote.addSavedSearch");
2358                 SavedSearchEdit edit = new SavedSearchEdit();
2359                 edit.setSearchList(listManager.getSavedSearchIndex());
2360                 edit.exec();
2361         
2362                 if (!edit.okPressed())
2363                         return;
2364         
2365                 Calendar currentTime = new GregorianCalendar();         
2366                 Long l = new Long(currentTime.getTimeInMillis());
2367                 String randint = new String(Long.toString(l));
2368         
2369                 SavedSearch search = new SavedSearch();
2370                 search.setUpdateSequenceNum(0);
2371                 search.setGuid(randint);
2372                 search.setName(edit.getName());
2373                 search.setQuery(edit.getQuery());
2374                 search.setFormat(QueryFormat.USER);
2375                 listManager.getSavedSearchIndex().add(search);
2376                 conn.getSavedSearchTable().addSavedSearch(search, true);
2377                 savedSearchIndexUpdated();
2378                 logger.log(logger.HIGH, "Leaving NeverNote.addSavedSearch");
2379         }
2380         // Edit an existing tag
2381         @SuppressWarnings("unused")
2382         private void editSavedSearch() {
2383                 logger.log(logger.HIGH, "Entering NeverNote.editSavedSearch");
2384                 SavedSearchEdit edit = new SavedSearchEdit();
2385                 edit.setTitle(tr("Edit Search"));
2386                 List<QTreeWidgetItem> selections = savedSearchTree.selectedItems();
2387                 QTreeWidgetItem currentSelection;
2388                 currentSelection = selections.get(0);
2389                 String guid = currentSelection.text(1);
2390                 SavedSearch s = conn.getSavedSearchTable().getSavedSearch(guid);
2391                 edit.setName(currentSelection.text(0));
2392                 edit.setQuery(s.getQuery());
2393                 edit.setSearchList(listManager.getSavedSearchIndex());
2394                 edit.exec();
2395         
2396                 if (!edit.okPressed())
2397                         return;
2398         
2399                 List<SavedSearch> list = listManager.getSavedSearchIndex();
2400                 SavedSearch search = null;
2401                 boolean found = false;
2402                 for (int i=0; i<list.size(); i++) {
2403                         search = list.get(i);
2404                         if (search.getGuid().equals(guid)) {
2405                                 i=list.size();
2406                                 found = true;
2407                         }
2408                 }
2409                 if (!found)
2410                         return;
2411                 search.setName(edit.getName());
2412                 search.setQuery(edit.getQuery());
2413                 conn.getSavedSearchTable().updateSavedSearch(search, true);
2414                 savedSearchIndexUpdated();
2415                 logger.log(logger.HIGH, "Leaving NeverNote.editSavedSearch");
2416         }
2417         // Delete an existing tag
2418         @SuppressWarnings("unused")
2419         private void deleteSavedSearch() {
2420                 logger.log(logger.HIGH, "Entering NeverNote.deleteSavedSearch");
2421                 
2422                 if (QMessageBox.question(this, "Confirmation", "Delete the selected search?",
2423                         QMessageBox.StandardButton.Yes, 
2424                         QMessageBox.StandardButton.No)==StandardButton.No.value()) {
2425                                                         return;
2426                 }
2427                 
2428                 List<QTreeWidgetItem> selections = savedSearchTree.selectedItems();
2429         for (int i=selections.size()-1; i>=0; i--) {
2430                 QTreeWidgetItem currentSelection;
2431                 currentSelection = selections.get(i);
2432                 for (int j=0; j<listManager.getSavedSearchIndex().size(); j++) {
2433                         if (listManager.getSavedSearchIndex().get(j).getGuid().equals(currentSelection.text(1))) {
2434                                 conn.getSavedSearchTable().expungeSavedSearch(listManager.getSavedSearchIndex().get(j).getGuid(), true);
2435                                 listManager.getSavedSearchIndex().remove(j);
2436                                 j=listManager.getSavedSearchIndex().size()+1;
2437                         }
2438                 }
2439                 selections.remove(i);
2440         }
2441         savedSearchIndexUpdated();
2442         logger.log(logger.HIGH, "Leaving NeverNote.deleteSavedSearch");
2443         }
2444     // Setup the tree containing the user's tags
2445     private void initializeSavedSearchTree() {
2446         logger.log(logger.HIGH, "Entering NeverNote.initializeSavedSearchTree");
2447         savedSearchTree.itemSelectionChanged.connect(this, "savedSearchTreeSelection()");
2448         logger.log(logger.HIGH, "Leaving NeverNote.initializeSavedSearchTree");
2449     }
2450     // Listener when a tag is selected
2451     @SuppressWarnings("unused")
2452         private void savedSearchTreeSelection() {
2453         logger.log(logger.HIGH, "Entering NeverNote.savedSearchTreeSelection");
2454
2455         clearNotebookFilter();
2456         clearTagFilter();
2457         clearTrashFilter();
2458         clearAttributeFilter();
2459         
2460         String currentGuid = selectedSavedSearchGUID;
2461         menuBar.savedSearchEditAction.setEnabled(true);
2462         menuBar.savedSearchDeleteAction.setEnabled(true);
2463         menuBar.savedSearchIconAction.setEnabled(true);
2464         List<QTreeWidgetItem> selections = savedSearchTree.selectedItems();
2465         QTreeWidgetItem currentSelection;
2466         selectedSavedSearchGUID = "";
2467         for (int i=0; i<selections.size(); i++) {
2468                 currentSelection = selections.get(i);
2469                 if (currentSelection.text(1).equals(currentGuid)) {
2470                         currentSelection.setSelected(false);
2471                 } else {
2472                         selectedSavedSearchGUID = currentSelection.text(1);
2473                 }
2474 //              i = selections.size() +1;
2475         }
2476         
2477         // There is the potential for no notebooks to be selected if this 
2478         // happens then we make it look like all notebooks were selecetd.
2479         // If that happens, just select the "all notebooks"
2480         if (selections.size()==0) {
2481                 clearSavedSearchFilter();
2482         }
2483         listManager.setSelectedSavedSearch(selectedSavedSearchGUID);
2484         
2485         logger.log(logger.HIGH, "Leaving NeverNote.savedSearchTreeSelection");
2486     }
2487     private void clearSavedSearchFilter() {
2488         menuBar.savedSearchEditAction.setEnabled(false);
2489         menuBar.savedSearchDeleteAction.setEnabled(false);
2490         menuBar.savedSearchIconAction.setEnabled(false);
2491         savedSearchTree.blockSignals(true);
2492         savedSearchTree.clearSelection();
2493         savedSearchTree.blockSignals(false);
2494         selectedSavedSearchGUID = "";
2495         searchField.setEditText("");
2496         searchPerformed = false;
2497         listManager.setSelectedSavedSearch(selectedSavedSearchGUID);
2498     }
2499     // trigger the tag index to be refreshed
2500         private void savedSearchIndexUpdated() { 
2501                 if (selectedSavedSearchGUID == null)
2502                         selectedSavedSearchGUID = new String();
2503                 savedSearchTree.blockSignals(true);
2504                 savedSearchTree.setIcons(conn.getSavedSearchTable().getAllIcons());
2505         savedSearchTree.load(listManager.getSavedSearchIndex());
2506         savedSearchTree.selectGuid(selectedSavedSearchGUID);
2507         savedSearchTree.blockSignals(false);
2508     }
2509     // trigger when the saved search selection changes
2510     @SuppressWarnings("unused")
2511         private void updateSavedSearchSelection() {
2512                 logger.log(logger.HIGH, "Entering NeverNote.updateSavedSearchSelection()");
2513                 
2514         menuBar.savedSearchEditAction.setEnabled(true);
2515         menuBar.savedSearchDeleteAction.setEnabled(true);
2516         menuBar.savedSearchIconAction.setEnabled(true);
2517         List<QTreeWidgetItem> selections = savedSearchTree.selectedItems();
2518
2519         if (selections.size() > 0) {
2520                 menuBar.savedSearchEditAction.setEnabled(true);
2521                 menuBar.savedSearchDeleteAction.setEnabled(true);
2522                 menuBar.savedSearchIconAction.setEnabled(true);
2523                 selectedSavedSearchGUID = selections.get(0).text(1);
2524                 SavedSearch s = conn.getSavedSearchTable().getSavedSearch(selectedSavedSearchGUID);
2525                 searchField.setEditText(s.getQuery());
2526         } else { 
2527                 menuBar.savedSearchEditAction.setEnabled(false);
2528                 menuBar.savedSearchDeleteAction.setEnabled(false);
2529                 menuBar.savedSearchIconAction.setEnabled(false);
2530                 selectedSavedSearchGUID = "";
2531                 searchField.setEditText("");
2532         }
2533         searchFieldChanged();
2534         
2535                 logger.log(logger.HIGH, "Leaving NeverNote.updateSavedSearchSelection()");
2536
2537         
2538     }
2539     // Show/Hide note information
2540         @SuppressWarnings("unused")
2541         private void toggleSavedSearchWindow() {
2542                 logger.log(logger.HIGH, "Entering NeverNote.toggleSavedSearchWindow");
2543         if (savedSearchTree.isVisible())
2544                 savedSearchTree.hide();
2545         else
2546                 savedSearchTree.show();
2547         menuBar.hideSavedSearches.setChecked(savedSearchTree.isVisible());
2548                                 
2549                 Global.saveWindowVisible("savedSearchTree", savedSearchTree.isVisible());
2550         logger.log(logger.HIGH, "Leaving NeverNote.toggleSavedSearchWindow");
2551     }
2552         // Change the icon for a saved search
2553         @SuppressWarnings("unused")
2554         private void setSavedSearchIcon() {
2555                 QTreeWidgetItem currentSelection;
2556                 List<QTreeWidgetItem> selections = savedSearchTree.selectedItems();
2557                 if (selections.size() == 0)
2558                         return;
2559                 
2560                 currentSelection = selections.get(0);   
2561                 String guid = currentSelection.text(1);
2562
2563                 QIcon currentIcon = currentSelection.icon(0);
2564                 QIcon icon = conn.getSavedSearchTable().getIcon(guid);
2565                 SetIcon dialog;
2566                 if (icon == null) {
2567                         dialog = new SetIcon(currentIcon, saveLastPath);
2568                         dialog.setUseDefaultIcon(true);
2569                 } else {
2570                         dialog = new SetIcon(icon, saveLastPath);
2571                         dialog.setUseDefaultIcon(false);
2572                 }
2573                 dialog.exec();
2574                 if (dialog.okPressed()) {
2575                 saveLastPath = dialog.getPath();
2576                         QIcon newIcon = dialog.getIcon();
2577                         conn.getSavedSearchTable().setIcon(guid, newIcon, dialog.getFileType());
2578                         if (newIcon == null) 
2579                                 newIcon = new QIcon(iconPath+"search.png");
2580                         currentSelection.setIcon(0, newIcon);
2581                 }
2582         
2583         }
2584         
2585         
2586         
2587         
2588     //***************************************************************
2589     //***************************************************************
2590     //** These functions deal with Help menu & tool menu items
2591     //***************************************************************
2592     //***************************************************************
2593         // Show database status
2594         @SuppressWarnings("unused")
2595         private void databaseStatus() {
2596                 waitCursor(true);
2597                 indexRunner.interrupt = true;
2598                 int dirty = conn.getNoteTable().getDirtyCount();
2599                 int unindexed = conn.getNoteTable().getUnindexedCount();
2600                 DatabaseStatus status = new DatabaseStatus();
2601                 status.setUnsynchronized(dirty);
2602                 status.setUnindexed(unindexed);
2603                 status.setNoteCount(conn.getNoteTable().getNoteCount());
2604                 status.setNotebookCount(listManager.getNotebookIndex().size());
2605                 status.setUnindexedResourceCount(conn.getNoteTable().noteResourceTable.getUnindexedCount());
2606                 status.setSavedSearchCount(listManager.getSavedSearchIndex().size());
2607                 status.setTagCount(listManager.getTagIndex().size());
2608                 status.setResourceCount(conn.getNoteTable().noteResourceTable.getResourceCount());
2609                 status.setWordCount(conn.getWordsTable().getWordCount());
2610                 waitCursor(false);
2611                 status.exec();
2612         }
2613         // Compact the database
2614         @SuppressWarnings("unused")
2615         private void compactDatabase() {
2616         logger.log(logger.HIGH, "Entering NeverNote.compactDatabase");
2617                 if (QMessageBox.question(this, tr("Confirmation"), tr("This will free unused space in the database, "+
2618                                 "but please be aware that depending upon the size of your database this can be time consuming " +
2619                                 "and NeverNote will be unresponsive until it is complete.  Do you wish to continue?"),
2620                                 QMessageBox.StandardButton.Yes, 
2621                                 QMessageBox.StandardButton.No)==StandardButton.No.value() && Global.verifyDelete() == true) {
2622                                                         return;
2623                 }
2624                 setMessage("Compacting database.");
2625                 waitCursor(true);
2626                 listManager.compactDatabase();
2627                 waitCursor(false);
2628                 setMessage("Database compact is complete.");            
2629         logger.log(logger.HIGH, "Leaving NeverNote.compactDatabase");
2630     }
2631         @SuppressWarnings("unused")
2632         private void accountInformation() {
2633                 logger.log(logger.HIGH, "Entering NeverNote.accountInformation");
2634                 AccountDialog dialog = new AccountDialog();
2635                 dialog.show();
2636                 logger.log(logger.HIGH, "Leaving NeverNote.accountInformation");
2637         }
2638         @SuppressWarnings("unused")
2639         private void releaseNotes() {
2640                 logger.log(logger.HIGH, "Entering NeverNote.releaseNotes");
2641                 QDialog dialog = new QDialog(this);
2642                 QHBoxLayout layout = new QHBoxLayout();
2643                 QTextEdit textBox = new QTextEdit();
2644                 layout.addWidget(textBox);
2645                 textBox.setReadOnly(true);
2646                 QFile file = new QFile(Global.getFileManager().getHomeDirPath("release.txt"));
2647                 if (!file.open(new QIODevice.OpenMode(QIODevice.OpenModeFlag.ReadOnly,
2648                 QIODevice.OpenModeFlag.Text)))
2649                         return;
2650                 textBox.setText(file.readAll().toString());
2651                 file.close();
2652                 dialog.setWindowTitle(tr("Release Notes"));
2653                 dialog.setLayout(layout);
2654                 dialog.show();
2655                 logger.log(logger.HIGH, "Leaving NeverNote.releaseNotes");
2656         }
2657         // Called when user picks Log from the help menu
2658         @SuppressWarnings("unused")
2659         private void logger() {
2660                 logger.log(logger.HIGH, "Entering NeverNote.logger");
2661                 LogFileDialog dialog = new LogFileDialog(emitLog);
2662                 dialog.exec();
2663                 logger.log(logger.HIGH, "Leaving NeverNote.logger");
2664         }
2665         // Menu option "help/about" was selected
2666         @SuppressWarnings("unused")
2667         private void about() {
2668                 logger.log(logger.HIGH, "Entering NeverNote.about");
2669                 QMessageBox.about(this, 
2670                                                 tr("About NeverNote"),
2671                                                 tr("<h4><center><b>NeverNote</b></center></h4><hr><center>Version ")
2672                                                 +Global.version
2673                                                 +tr("<hr></center>Evernote"
2674                                                                 +"An Open Source Evernote Client.<br><br>" 
2675                                                                 +"Licensed under GPL v2.  <br><hr><br>"
2676                                                                 +"Evernote is copyright 2001-2010 by Evernote Corporation<br>"
2677                                                                 +"Jambi and QT are the licensed trademark of Nokia Corporation<br>"
2678                                                                 +"PDFRenderer is licened under the LGPL<br>"
2679                                                                 +"JTidy is copyrighted under the World Wide Web Consortium<br>"
2680                                                                 +"Apache Common Utilities licensed under the Apache License Version 2.0<br>"
2681                                                                 +"Jazzy is licened under the LGPL<br>"
2682                                                                 +"Java is a registered trademark of Oracle Corporation.<br><hr>"
2683                                                                 +"Special thanks to:<br>BitRock InstallBuilder for the Windows installer"
2684                                                                 +"<br>CodeCogs (www.codecogs.com) for the LaTeX image rendering."));
2685                 logger.log(logger.HIGH, "Leaving NeverNote.about");
2686         }
2687         // Hide the entire left hand side
2688         @SuppressWarnings("unused")
2689         private void toggleLeftSide() {
2690                 boolean hidden;
2691                 
2692                 hidden = !menuBar.hideLeftSide.isChecked();
2693                 menuBar.hideLeftSide.setChecked(!hidden);
2694                 
2695                 if (!hidden) 
2696                         leftSplitter1.setHidden(true);
2697                 else
2698                         leftSplitter1.setHidden(false);
2699                 
2700                 Global.saveWindowVisible("leftPanel", hidden);
2701                 
2702         }
2703         public void checkForUpdates() {
2704                 // Send off thread to check for a new version
2705                 versionChecker = new QNetworkAccessManager(this);
2706                 versionChecker.finished.connect(this, "upgradeFileRead(QNetworkReply)");
2707                 QNetworkRequest request = new QNetworkRequest();
2708                 request.setUrl(new QUrl(Global.getUpdatesAvailableUrl()));
2709                 versionChecker.get(request);
2710         }
2711         @SuppressWarnings("unused")
2712         private void upgradeFileRead(QNetworkReply reply) {
2713                 if (!reply.isReadable())
2714                         return;
2715                 
2716                 String winVersion = Global.version;
2717                 String osxVersion = Global.version;
2718                 String linuxVersion = Global.version;
2719                 String linux64Version = Global.version;
2720                 String version = Global.version;
2721                 
2722                 // Determine the versions available
2723                 QByteArray data = reply.readLine();
2724                 while (data != null && !reply.atEnd()) {
2725                         String line = data.toString();
2726                         String lineVersion;
2727                         if (line.contains(":")) 
2728                                 lineVersion = line.substring(line.indexOf(":")+1).replace(" ", "").replace("\n", "");
2729                         else
2730                                 lineVersion = "";
2731                         if (line.toLowerCase().contains("windows")) 
2732                                 winVersion = lineVersion;
2733                         else if (line.toLowerCase().contains("os-x")) 
2734                                 osxVersion = lineVersion;
2735                         else if (line.toLowerCase().contains("linux amd64")) 
2736                                 linux64Version = lineVersion;
2737                         else if (line.toLowerCase().contains("linux i386")) 
2738                                 linuxVersion = lineVersion;
2739                         else if (line.toLowerCase().contains("default")) 
2740                                 version = lineVersion;
2741                         
2742                         // Read the next line
2743                         data = reply.readLine();
2744                 }
2745                 
2746                 // Now we need to determine what system we are on.
2747                 if (System.getProperty("os.name").toLowerCase().contains("windows"))
2748                         version = winVersion;
2749                 if (System.getProperty("os.name").toLowerCase().contains("mac os"))
2750                         version = osxVersion;
2751                 if (System.getProperty("os.name").toLowerCase().contains("Linux")) {
2752                         if (System.getProperty("os.arch").contains("amd64") ||
2753                                 System.getProperty("os.arch").contains("x86_64"))
2754                                         version = linux64Version;
2755                         else
2756                                 version = linuxVersion;
2757                 }
2758                 
2759                 
2760                 for (String validVersion : Global.validVersions) {
2761                         if (version.equals(validVersion))
2762                                 return;
2763                 }
2764                 
2765                 UpgradeAvailableDialog dialog = new UpgradeAvailableDialog();
2766                 dialog.exec();
2767                 if (dialog.remindMe())
2768                         Global.setCheckVersionUpgrade(true);
2769                 else
2770                         Global.setCheckVersionUpgrade(false);
2771         }
2772                 
2773         
2774     //***************************************************************
2775     //***************************************************************
2776     //** These functions deal with the Toolbar
2777     //***************************************************************
2778     //*************************************************************** 
2779         @SuppressWarnings("unused")
2780         private void focusSearch() {
2781                 searchField.setFocus();
2782         }
2783
2784         // Text in the search bar has been cleared
2785         private void searchFieldCleared() {
2786                 saveNote();
2787                 
2788                 // This is done because we want to force a reload of
2789                 // images.  Some images we may want to highlight the text.
2790                 readOnlyCache.clear();
2791                 inkNoteCache.clear();
2792                 noteCache.clear();
2793                 QWebSettings.setMaximumPagesInCache(0);
2794                 QWebSettings.setObjectCacheCapacities(0, 0, 0);
2795         
2796                 searchField.setEditText("");
2797                 saveNoteColumnPositions();
2798                 saveNoteIndexWidth();
2799                 noteIndexUpdated(true);
2800                 if (currentNote == null && listManager.getNoteIndex().size() > 0) {
2801                         currentNote = listManager.getNoteIndex().get(0);
2802                         currentNoteGuid = currentNote.getGuid();
2803                 }
2804                 refreshEvernoteNote(true);
2805                 if (currentNote != null)
2806                         loadNoteBrowserInformation(browserWindow);
2807         }
2808         // text in the search bar changed.  We only use this to tell if it was cleared, 
2809         // otherwise we trigger off searchFieldChanged.
2810         @SuppressWarnings("unused")
2811         private void searchFieldTextChanged(String text) {
2812                 QWebSettings.setMaximumPagesInCache(0);
2813                 QWebSettings.setObjectCacheCapacities(0, 0, 0);
2814
2815                 if (text.trim().equals("")) {
2816                         searchFieldCleared();
2817                         if (searchPerformed) {
2818
2819                                 // This is done because we want to force a reload of
2820                                 // images.  Some images we may want to highlight the text.
2821                                 noteCache.clear();
2822                                 readOnlyCache.clear();
2823                                 inkNoteCache.clear();
2824                                 
2825                                 listManager.setEnSearch("");
2826                                 listManager.loadNotesIndex();
2827                                 refreshEvernoteNote(true);
2828                                 noteIndexUpdated(false);
2829                                 refreshEvernoteNote(true);
2830                         }
2831                         searchPerformed = false;
2832                 }
2833         }
2834     // Text in the toolbar has changed
2835     private void searchFieldChanged() {
2836         logger.log(logger.HIGH, "Entering NeverNote.searchFieldChanged");
2837         noteCache.clear();
2838         readOnlyCache.clear();
2839         inkNoteCache.clear();
2840         saveNoteColumnPositions();
2841         saveNoteIndexWidth();
2842         String text = searchField.currentText();
2843         listManager.setEnSearch(text.trim());
2844         listManager.loadNotesIndex();
2845         noteIndexUpdated(false);
2846         refreshEvernoteNote(true);
2847         searchPerformed = true;
2848         logger.log(logger.HIGH, "Leaving NeverNote.searchFieldChanged");
2849     }
2850
2851     // Build the window tool bar
2852     private void setupToolBar() {
2853         logger.log(logger.HIGH, "Entering NeverNote.setupToolBar");
2854         toolBar = addToolBar(tr("Tool Bar"));   
2855         toolBar.setObjectName("toolBar");
2856         menuBar.setupToolBarVisible();
2857         if (!Global.isWindowVisible("toolBar"))
2858                 toolBar.setVisible(false);
2859         else
2860                 toolBar.setVisible(true);
2861
2862 //      toolBar.addWidget(menuBar);
2863 //      menuBar.setSizePolicy(Policy.Minimum, Policy.Minimum);
2864 //      toolBar.addSeparator();
2865         prevButton = toolBar.addAction("Previous");
2866         QIcon prevIcon = new QIcon(iconPath+"back.png");
2867         prevButton.setIcon(prevIcon);
2868         prevButton.triggered.connect(this, "previousViewedAction()");   
2869         togglePrevArrowButton(Global.isToolbarButtonVisible("prevArrow"));
2870         
2871         nextButton = toolBar.addAction("Next");
2872         QIcon nextIcon = new QIcon(iconPath+"forward.png");
2873         nextButton.setIcon(nextIcon);
2874         nextButton.triggered.connect(this, "nextViewedAction()");       
2875         toggleNextArrowButton(Global.isToolbarButtonVisible("nextArrow"));
2876         
2877         upButton = toolBar.addAction("Up");
2878         QIcon upIcon = new QIcon(iconPath+"up.png");
2879         upButton.setIcon(upIcon);
2880         upButton.triggered.connect(this, "upAction()");         
2881         toggleUpArrowButton(Global.isToolbarButtonVisible("upArrow"));
2882
2883         
2884         downButton = toolBar.addAction("Down");
2885         QIcon downIcon = new QIcon(iconPath+"down.png");
2886         downButton.setIcon(downIcon);
2887         downButton.triggered.connect(this, "downAction()");
2888         toggleDownArrowButton(Global.isToolbarButtonVisible("downArrow"));
2889         
2890         synchronizeButton = toolBar.addAction("Synchronize");
2891         synchronizeButton.setIcon(new QIcon(iconPath+"synchronize.png"));
2892         synchronizeIconAngle = 0;
2893         synchronizeButton.triggered.connect(this, "evernoteSync()");
2894         toggleSynchronizeButton(Global.isToolbarButtonVisible("synchronize"));
2895         
2896         printButton = toolBar.addAction("Print");
2897         QIcon printIcon = new QIcon(iconPath+"print.png");
2898         printButton.setIcon(printIcon);
2899         printButton.triggered.connect(this, "printNote()");
2900         togglePrintButton(Global.isToolbarButtonVisible("print"));
2901
2902         tagButton = toolBar.addAction("Tag"); 
2903         QIcon tagIcon = new QIcon(iconPath+"tag.png");
2904         tagButton.setIcon(tagIcon);
2905         tagButton.triggered.connect(browserWindow, "modifyTags()");
2906         toggleTagButton(Global.isToolbarButtonVisible("tag"));
2907
2908         attributeButton = toolBar.addAction("Attributes"); 
2909         QIcon attributeIcon = new QIcon(iconPath+"attribute.png");
2910         attributeButton.setIcon(attributeIcon);
2911         attributeButton.triggered.connect(this, "toggleNoteInformation()");
2912         toggleAttributeButton(Global.isToolbarButtonVisible("attribute"));
2913                 
2914         emailButton = toolBar.addAction("Email");
2915         QIcon emailIcon = new QIcon(iconPath+"email.png");
2916         emailButton.setIcon(emailIcon);
2917         emailButton.triggered.connect(this, "emailNote()");
2918         toggleEmailButton(Global.isToolbarButtonVisible("email"));
2919
2920         deleteButton = toolBar.addAction("Delete");     
2921         QIcon deleteIcon = new QIcon(iconPath+"delete.png");
2922         deleteButton.setIcon(deleteIcon);
2923         deleteButton.triggered.connect(this, "deleteNote()");
2924         toggleDeleteButton(Global.isToolbarButtonVisible("delete"));
2925
2926         newButton = toolBar.addAction("New");
2927         QIcon newIcon = new QIcon(iconPath+"new.png");
2928         newButton.triggered.connect(this, "addNote()");
2929         newButton.setIcon(newIcon);
2930         toggleNewButton(Global.isToolbarButtonVisible("new"));
2931         
2932         allNotesButton = toolBar.addAction("All Notes");
2933         QIcon allIcon = new QIcon(iconPath+"books.png");
2934         allNotesButton.triggered.connect(this, "allNotes()");
2935         allNotesButton.setIcon(allIcon);
2936         toggleAllNotesButton(Global.isToolbarButtonVisible("allNotes"));
2937         
2938         //toolBar.addSeparator();
2939         //toolBar.addWidget(new QLabel(tr("Quota:")));
2940         //toolBar.addWidget(quotaBar);
2941         //quotaBar.setSizePolicy(Policy.Minimum, Policy.Minimum);
2942         updateQuotaBar();
2943         //toolBar.addSeparator();
2944         
2945         //toolBar.addWidget(new QLabel(tr("Zoom")));
2946         //toolBar.addWidget(zoomSpinner);
2947         
2948         //toolBar.addWidget(new QLabel("                    "));
2949         //toolBar.addSeparator();
2950         //toolBar.addWidget(new QLabel(tr("  Search:")));
2951         //toolBar.addWidget(searchField);
2952         QSizePolicy sizePolicy = new QSizePolicy();
2953         sizePolicy.setHorizontalPolicy(Policy.MinimumExpanding);
2954         QLabel spacer = new QLabel("");
2955         spacer.setSizePolicy(sizePolicy);
2956         toolBar.addWidget(spacer);
2957         //searchField.setInsertPolicy(InsertPolicy.InsertAtTop);
2958
2959         //searchClearButton = toolBar.addAction("Search Clear");
2960         //QIcon searchClearIcon = new QIcon(iconPath+"searchclear.png");
2961         //searchClearButton.setIcon(searchClearIcon);
2962         //searchClearButton.triggered.connect(this, "searchFieldCleared()");
2963         //toggleSearchClearButton(Global.isToolbarButtonVisible("searchClear"));
2964
2965         logger.log(logger.HIGH, "Leaving NeverNote.setupToolBar");
2966     }
2967     // Update the sychronize button picture
2968     @Override
2969         public QMenu createPopupMenu() {
2970         QMenu contextMenu = super.createPopupMenu();
2971         
2972         contextMenu.addSeparator();
2973         QAction prevAction = addContextAction("prevArrow", tr("Previous Arrow"));
2974         contextMenu.addAction(prevAction);
2975         prevAction.triggered.connect(this, "togglePrevArrowButton(Boolean)");
2976
2977         QAction nextAction = addContextAction("nextArrow", tr("Next Arrow"));
2978         contextMenu.addAction(nextAction);
2979         nextAction.triggered.connect(this, "toggleNextArrowButton(Boolean)");
2980
2981         QAction upAction = addContextAction("upArrow", tr("Up Arrow"));
2982         contextMenu.addAction(upAction);
2983         upAction.triggered.connect(this, "toggleUpArrowButton(Boolean)");
2984
2985         QAction downAction = addContextAction("downArrow", tr("Down Arrow"));
2986         contextMenu.addAction(downAction);
2987         downAction.triggered.connect(this, "toggleDownArrowButton(Boolean)");
2988
2989         QAction synchronizeAction = addContextAction("synchronize", tr("Synchronize"));
2990         contextMenu.addAction(synchronizeAction);
2991         synchronizeAction.triggered.connect(this, "toggleSynchronizeButton(Boolean)");
2992
2993         QAction printAction = addContextAction("print", tr("Print"));
2994         contextMenu.addAction(printAction);
2995         printAction.triggered.connect(this, "togglePrintButton(Boolean)");
2996
2997         QAction tagAction = addContextAction("tag", tr("Tag"));
2998         contextMenu.addAction(tagAction);
2999         tagAction.triggered.connect(this, "toggleTagButton(Boolean)");
3000         
3001         QAction attributeAction = addContextAction("attribute", tr("Attribute"));
3002         contextMenu.addAction(attributeAction);
3003         attributeAction.triggered.connect(this, "toggleAttributeButton(Boolean)");
3004         
3005         QAction emailAction = addContextAction("email", tr("Email"));
3006         contextMenu.addAction(emailAction);
3007         emailAction.triggered.connect(this, "toggleEmailButton(Boolean)");
3008
3009         QAction deleteAction = addContextAction("delete", tr("Delete"));
3010         contextMenu.addAction(deleteAction);
3011         deleteAction.triggered.connect(this, "toggleDeleteButton(Boolean)");
3012
3013         QAction newAction = addContextAction("new", tr("Add"));
3014         contextMenu.addAction(newAction);
3015         newAction.triggered.connect(this, "toggleNewButton(Boolean)");
3016
3017         QAction allNotesAction = addContextAction("allNotes", tr("All Notes"));
3018         contextMenu.addAction(allNotesAction);
3019         allNotesAction.triggered.connect(this, "toggleAllNotesButton(Boolean)");
3020         
3021         QAction searchClearAction = addContextAction("searchClear", tr("Search Clear"));
3022         contextMenu.addAction(searchClearAction);
3023         searchClearAction.triggered.connect(this, "toggleSearchClearButton(Boolean)");
3024         
3025         return contextMenu;
3026         
3027     }
3028     private QAction addContextAction(String config, String name) {
3029         QAction newAction = new QAction(this);
3030                 newAction.setText(name);
3031                 newAction.setCheckable(true);
3032                 newAction.setChecked(Global.isToolbarButtonVisible(config));
3033                 return newAction;
3034     }
3035     private void togglePrevArrowButton(Boolean toggle) {
3036                 prevButton.setVisible(toggle);
3037                 Global.saveToolbarButtonsVisible("prevArrow", toggle);
3038     }
3039     private void toggleNextArrowButton(Boolean toggle) {
3040                 nextButton.setVisible(toggle);
3041                 Global.saveToolbarButtonsVisible("nextArrow", toggle);
3042     }
3043     private void toggleUpArrowButton(Boolean toggle) {
3044                 upButton.setVisible(toggle);
3045                 Global.saveToolbarButtonsVisible("upArrow", toggle);
3046     }
3047     private void toggleDownArrowButton(Boolean toggle) {
3048                 downButton.setVisible(toggle);
3049                 Global.saveToolbarButtonsVisible("downArrow", toggle);
3050     }
3051     private void toggleSynchronizeButton(Boolean toggle)&n