/*
* This file is part of NixNote
* Copyright 2009 Randy Baumgarte
*
* This file may be licensed under the terms of of the
* GNU General Public License Version 2 (the ``GPL'').
*
* Software distributed under the License is distributed
* on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
* express or implied. See the GPL for the specific language
* governing rights and limitations.
*
* You should have received a copy of the GPL along with this
* program. If not, go to http://www.gnu.org/licenses/gpl.html
* or write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package cx.fbn.nevernote.utilities;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;
import com.evernote.edam.type.LinkedNotebook;
import com.evernote.edam.type.Note;
import com.evernote.edam.type.Notebook;
import com.evernote.edam.type.SavedSearch;
import com.evernote.edam.type.Tag;
import com.trolltech.qt.QThread;
import com.trolltech.qt.core.QDateTime;
import com.trolltech.qt.gui.QImage;
import com.trolltech.qt.gui.QPixmap;
import com.trolltech.qt.sql.QSqlQuery;
import com.trolltech.qt.xml.QDomAttr;
import com.trolltech.qt.xml.QDomDocument;
import com.trolltech.qt.xml.QDomElement;
import com.trolltech.qt.xml.QDomNodeList;
import cx.fbn.nevernote.Global;
import cx.fbn.nevernote.evernote.NoteMetadata;
import cx.fbn.nevernote.filters.EnSearch;
import cx.fbn.nevernote.filters.NotebookCounter;
import cx.fbn.nevernote.filters.TagCounter;
import cx.fbn.nevernote.gui.NoteTableModel;
import cx.fbn.nevernote.signals.NotebookSignal;
import cx.fbn.nevernote.signals.StatusSignal;
import cx.fbn.nevernote.signals.TagSignal;
import cx.fbn.nevernote.signals.ThreadSignal;
import cx.fbn.nevernote.signals.TrashSignal;
import cx.fbn.nevernote.sql.DatabaseConnection;
import cx.fbn.nevernote.threads.CounterRunner;
import cx.fbn.nevernote.threads.SaveRunner;
public class ListManager {
private final ApplicationLogger logger;
DatabaseConnection conn;
QSqlQuery deleteWords;
QSqlQuery insertWords;
private List<Tag> tagIndex;
private List<Notebook> notebookIndex;
private List<Notebook> archiveNotebookIndex;
private List<String> localNotebookIndex;
private List<LinkedNotebook> linkedNotebookIndex;
private List<SavedSearch> searchIndex;
private List<String> selectedNotebooks;
private final NoteTableModel noteModel;
private List<String> selectedTags;
private String selectedSearch;
ThreadSignal signals;
public StatusSignal status;
private final CounterRunner notebookCounterRunner;
private final QThread notebookThread;
private final CounterRunner tagCounterRunner;
private final QThread tagThread;
private final CounterRunner trashCounterRunner;
private final QThread trashThread;
public TrashSignal trashSignal;
private List<NotebookCounter> notebookCounter; // count of displayed notes in each notebook
private List<TagCounter> tagCounter; // count of displayed notes for each tag
private EnSearch enSearch;
private boolean enSearchChanged;
public HashMap<String, String> wordMap;
public TagSignal tagSignal;
public NotebookSignal notebookSignal;
public boolean refreshCounters; // Used to control when to recount lists
private int trashCount;
public SaveRunner saveRunner; // Thread used to save content. Used because the xml conversion is slowwwww
QThread saveThread;
// private final HashMap<String, QImage> thumbnailList;
// Constructor
public ListManager(DatabaseConnection d, ApplicationLogger l) {
conn = d;
logger = l;
conn.getTagTable().cleanupTags();
status = new StatusSignal();
signals = new ThreadSignal();
// setup index locks
enSearchChanged = false;
// Setup arrays
noteModel = new NoteTableModel(this);
selectedTags = new ArrayList<String>();
notebookCounter = new ArrayList<NotebookCounter>();
tagCounter = new ArrayList<TagCounter>();
selectedNotebooks = new ArrayList<String>();
reloadIndexes();
notebookSignal = new NotebookSignal();
notebookCounterRunner = new CounterRunner("notebook_counter.log", CounterRunner.NOTEBOOK,
Global.getDatabaseUrl(), Global.getIndexDatabaseUrl(), Global.getResourceDatabaseUrl(),
Global.getDatabaseUserid(), Global.getDatabaseUserPassword(), Global.cipherPassword);
notebookCounterRunner.setNoteIndex(getNoteIndex());
notebookCounterRunner.notebookSignal.countsChanged.connect(this, "setNotebookCounter(List)");
notebookThread = new QThread(notebookCounterRunner, "Notebook Counter Thread");
notebookThread.start();
tagSignal = new TagSignal();
tagCounterRunner = new CounterRunner("tag_counter.log", CounterRunner.TAG,
Global.getDatabaseUrl(), Global.getIndexDatabaseUrl(), Global.getResourceDatabaseUrl(),
Global.getDatabaseUserid(), Global.getDatabaseUserPassword(), Global.cipherPassword);
tagCounterRunner.setNoteIndex(getNoteIndex());
tagCounterRunner.tagSignal.countsChanged.connect(this, "setTagCounter(List)");
tagThread = new QThread(tagCounterRunner, "Tag Counter Thread");
tagThread.start();
trashSignal = new TrashSignal();
trashCounterRunner = new CounterRunner("trash_counter.log", CounterRunner.TRASH,
Global.getDatabaseUrl(), Global.getIndexDatabaseUrl(), Global.getResourceDatabaseUrl(),
Global.getDatabaseUserid(), Global.getDatabaseUserPassword(), Global.cipherPassword);
trashCounterRunner.trashSignal.countChanged.connect(this, "trashSignalReceiver(Integer)");
trashThread = new QThread(trashCounterRunner, "Trash Counter Thread");
trashThread.start();
// reloadTrashCount();
wordMap = new HashMap<String, String>();
tagSignal = new TagSignal();
logger.log(logger.EXTREME, "Setting save thread");
saveRunner = new SaveRunner("saveRunner.log",
Global.getDatabaseUrl(), Global.getIndexDatabaseUrl(), Global.getResourceDatabaseUrl(),
Global.getDatabaseUserid(), Global.getDatabaseUserPassword(), Global.cipherPassword);
saveThread = new QThread(saveRunner, "Save Runner Thread");
saveThread.start();
// thumbnailList = conn.getNoteTable().getThumbnails();
// thumbnailList = new HashMap<String,QImage>();
linkedNotebookIndex = conn.getLinkedNotebookTable().getAll();
loadNoteTitleColors();
refreshCounters = true;
refreshCounters();
}
public void stop() {
saveRunner.addWork("stop", "");
tagCounterRunner.release(CounterRunner.EXIT);
notebookCounterRunner.release(CounterRunner.EXIT);
trashCounterRunner.release(CounterRunner.EXIT);
logger.log(logger.MEDIUM, "Waiting for notebookCounterThread to stop");
try {
notebookThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.log(logger.MEDIUM, "Waiting for tagCounterThread to stop");
try {
tagThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.log(logger.MEDIUM, "Waiting for trashThread to stop");
try {
trashThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.log(logger.MEDIUM, "Waiting for saveThread to stop");
try {
saveThread.join(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//***************************************************************
//***************************************************************
//* Refresh lists after a db sync
//***************************************************************
//***************************************************************
public void refreshLists(Note n, boolean dirty, String content) {
if (dirty) {
// conn.getNoteTable().updateNoteContent(n.getGuid(), n.getContent());
saveRunner.addWork(n.getGuid(), content);
conn.getNoteTable().updateNoteTitle(n.getGuid(), n.getTitle());
}
setSavedSearchIndex(conn.getSavedSearchTable().getAll());
setTagIndex(conn.getTagTable().getAll());
setNotebookIndex(conn.getNotebookTable().getAll());
List<Notebook> local = conn.getNotebookTable().getAllLocal();
localNotebookIndex = new ArrayList<String>();
for (int i=0; i<local.size(); i++)
localNotebookIndex.add(local.get(i).getGuid());
noteModel.setMasterNoteIndex(conn.getNoteTable().getAllNotes());
// For performance reasons, we didn't get the tags for every note individually. We now need to
// get them
List<cx.fbn.nevernote.sql.NoteTagsRecord> noteTags = conn.getNoteTable().noteTagsTable.getAllNoteTags();
for (int i=0; i<getMasterNoteIndex().size(); i++) {
List<String> tags = new ArrayList<String>();
List<String> names = new ArrayList<String>();
for (int j=0; j<noteTags.size(); j++) {
if (getMasterNoteIndex().get(i).getGuid().equals(noteTags.get(j).noteGuid)) {
tags.add(noteTags.get(j).tagGuid);
names.add(getTagNameByGuid(noteTags.get(j).tagGuid));
}
}
getMasterNoteIndex().get(i).setTagGuids(tags);
getMasterNoteIndex().get(i).setTagNames(names);
}
//setUnsynchronizedNotes(conn.getNoteTable().getUnsynchronizedGUIDs());
linkedNotebookIndex = conn.getLinkedNotebookTable().getAll();
enSearchChanged = true;
}
public void reloadTagIndex() {
setTagIndex(conn.getTagTable().getAll());
}
public void reloadIndexes() {
//setUnsynchronizedNotes(conn.getNoteTable().getUnsynchronizedGUIDs());
List<Notebook> local = conn.getNotebookTable().getAllLocal();
localNotebookIndex = new ArrayList<String>();
for (int i=0; i<local.size(); i++)
localNotebookIndex.add(local.get(i).getGuid());
reloadTagIndex();
// Load notebooks
setNotebookIndex(conn.getNotebookTable().getAll());
// load archived notebooks (if note using the EN interface)
setArchiveNotebookIndex(conn.getNotebookTable().getAllArchived());
// load saved search index
setSavedSearchIndex(conn.getSavedSearchTable().getAll());
// Load search helper utility
enSearch = new EnSearch(conn, logger, "", getTagIndex(), Global.getRecognitionWeight());
logger.log(logger.HIGH, "Building note index");
// if (getMasterNoteIndex() == null) {
noteModel.setMasterNoteIndex(conn.getNoteTable().getAllNotes());
// }
// For performance reasons, we didn't get the tags for every note individually. We now need to
// get them
List<cx.fbn.nevernote.sql.NoteTagsRecord> noteTags = conn.getNoteTable().noteTagsTable.getAllNoteTags();
for (int i=0; i<getMasterNoteIndex().size(); i++) {
List<String> tags = new ArrayList<String>();
List<String> names = new ArrayList<String>();
for (int j=0; j<noteTags.size(); j++) {
if (getMasterNoteIndex().get(i).getGuid().equals(noteTags.get(j).noteGuid)) {
tags.add(noteTags.get(j).tagGuid);
names.add(getTagNameByGuid(noteTags.get(j).tagGuid));
}
}
getMasterNoteIndex().get(i).setTagGuids(tags);
getMasterNoteIndex().get(i).setTagNames(names);
}
setNoteIndex(getMasterNoteIndex());
}
//***************************************************************
//***************************************************************
//* selected notebooks
//***************************************************************
//***************************************************************
// Return the selected notebook(s)
public List<String> getSelectedNotebooks() {
return selectedNotebooks;
}
// Set the current selected notebook(s)
public void setSelectedNotebooks(List <String> s) {
if (s == null)
s = new ArrayList<String>();
selectedNotebooks = s;
}
//***************************************************************
//***************************************************************
//** These functions deal with setting & retrieving the master lists
//***************************************************************
//***************************************************************
// Get the note table model
public NoteTableModel getNoteTableModel() {
return noteModel;
}
// save the saved search index
private void setSavedSearchIndex(List<SavedSearch> t) {
searchIndex = t;
}
// Retrieve the Tag index
public List<SavedSearch> getSavedSearchIndex() {
return searchIndex;
}
// save the tag index
private void setTagIndex(List<Tag> t) {
tagIndex = t;
}
// Retrieve the Tag index
public List<Tag> getTagIndex() {
return tagIndex;
}
private void setNotebookIndex(List<Notebook> t) {
notebookIndex = t;
}
private void setArchiveNotebookIndex(List<Notebook> t) {
archiveNotebookIndex = t;
}
// Retrieve the Notebook index
public List<Notebook> getNotebookIndex() {
return notebookIndex;
}
public List<LinkedNotebook> getLinkedNotebookIndex() {
return linkedNotebookIndex;
}
public List<Notebook> getArchiveNotebookIndex() {
return archiveNotebookIndex;
}
// Save the current note list
private void setNoteIndex(List<Note> n) {
noteModel.setNoteIndex(n);
refreshNoteMetadata();
}
public void refreshNoteMetadata() {
noteModel.setNoteMetadata(conn.getNoteTable().getNotesMetaInformation());
}
// Update a note's meta data
public void updateNoteMetadata(NoteMetadata meta) {
noteModel.metaData.remove(meta);
noteModel.metaData.put(meta.getGuid(), meta);
conn.getNoteTable().updateNoteMetadata(meta);
}
// Get the note index
public synchronized List<Note> getNoteIndex() {
return noteModel.getNoteIndex();
}
// Save the count of notes per notebook
public void setNotebookCounter(List<NotebookCounter> n) {
notebookCounter = n;
notebookSignal.refreshNotebookTreeCounts.emit(getNotebookIndex(), notebookCounter);
}
public List<NotebookCounter> getNotebookCounter() {
return notebookCounter;
}
// Save the count of notes for each tag
public void setTagCounter(List<TagCounter> n) {
tagCounter = n;
tagSignal.refreshTagTreeCounts.emit(tagCounter);
}
public List<TagCounter> getTagCounter() {
return tagCounter;
}
public List<String> getLocalNotebooks() {
return localNotebookIndex;
}
// public void setUnsynchronizedNotes(List<String> l) {
// noteModel.setUnsynchronizedNotes(l);
// }
// Return a count of items in the trash
public int getTrashCount() {
return trashCount;
}
// get the EnSearch variable
public EnSearch getEnSearch() {
return enSearch;
}
public List<Note> getMasterNoteIndex() {
return noteModel.getMasterNoteIndex();
}
// Thumbnails
// public HashMap<String, QImage> getThumbnails() {
// return thumbnailList;
// }
public HashMap<String, NoteMetadata> getNoteMetadata() {
return noteModel.metaData;
}
public QImage getThumbnail(String guid) {
// if (getThumbnails().containsKey(guid))
// return getThumbnails().get(guid);
QImage img = new QImage();
img = QImage.fromData(conn.getNoteTable().getThumbnail(guid));
if (img == null || img.isNull())
return null;
//getThumbnails().put(guid, img);
return img;
}
public QPixmap getThumbnailPixmap(String guid) {
// if (getThumbnails().containsKey(guid))
// return getThumbnails().get(guid);
QPixmap img = new QPixmap();
img.loadFromData(conn.getNoteTable().getThumbnail(guid));
if (img == null || img.isNull())
return null;
//getThumbnails().put(guid, img);
return img;
}
//***************************************************************
//***************************************************************
//** These functions deal with setting & retrieving filters
//***************************************************************
//***************************************************************
public void setEnSearch(String t) {
enSearch = new EnSearch(conn,logger, t, getTagIndex(), Global.getRecognitionWeight());
enSearchChanged = true;
}
// Save search tags
public void setSelectedTags(List<String> selectedTags) {
this.selectedTags = selectedTags;
}
// Save seleceted search
public void setSelectedSavedSearch(String s) {
this.selectedSearch = s;
}
// Get search tags
public List<String> getSelectedTags() {
return selectedTags;
}
// Get saved search
public String getSelectedSearch() {
return selectedSearch;
}
//***************************************************************
//***************************************************************
//** Note functions
//***************************************************************
//***************************************************************
// Save Note Tags
public void saveNoteTags(String noteGuid, List<String> tags, boolean isDirty) {
logger.log(logger.HIGH, "Entering ListManager.saveNoteTags");
String tagName;
conn.getNoteTable().noteTagsTable.deleteNoteTag(noteGuid);
List<String> tagGuids = new ArrayList<String>();
boolean newTagCreated = false;
for (int i=0; i<tags.size(); i++) {
tagName = tags.get(i);
boolean found = false;
for (int j=0; j<tagIndex.size(); j++) {
if (tagIndex.get(j).getName().equalsIgnoreCase(tagName)) {
conn.getNoteTable().noteTagsTable.saveNoteTag(noteGuid, tagIndex.get(j).getGuid(), isDirty);
tagGuids.add(tagIndex.get(j).getGuid());
j=tagIndex.size()+1;
found = true;
}
}
if (!found) {
Tag nTag = new Tag();
nTag.setName(tagName);
Calendar currentTime = new GregorianCalendar();
Long l = new Long(currentTime.getTimeInMillis());
long prevTime = l;
while (l==prevTime) {
currentTime = new GregorianCalendar();
l=currentTime.getTimeInMillis();
}
String randint = new String(Long.toString(l));
nTag.setUpdateSequenceNum(0);
nTag.setGuid(randint);
conn.getTagTable().addTag(nTag, true);
getTagIndex().add(nTag);
conn.getNoteTable().noteTagsTable.saveNoteTag(noteGuid, nTag.getGuid(), isDirty);
tagGuids.add(nTag.getGuid());
newTagCreated = true;
}
}
for (int i=0; i<getNoteIndex().size(); i++) {
if (getNoteIndex().get(i).getGuid().equals(noteGuid)) {
getNoteIndex().get(i).setTagNames(tags);
getNoteIndex().get(i).setTagGuids(tagGuids);
i=getNoteIndex().size()+1;
}
}
if (newTagCreated)
tagSignal.listChanged.emit();
logger.log(logger.HIGH, "Leaving ListManager.saveNoteTags");
}
// Delete a note
public void deleteNote(String guid) {
trashCounterRunner.abortCount = true;
Calendar currentTime = new GregorianCalendar();
Long l = new Long(currentTime.getTimeInMillis());
long prevTime = l;
while (l==prevTime) {
currentTime = new GregorianCalendar();
l=currentTime.getTimeInMillis();
}
for (int i=0; i<getMasterNoteIndex().size(); i++) {
if (getMasterNoteIndex().get(i).getGuid().equals(guid)) {
getMasterNoteIndex().get(i).setActive(false);
getMasterNoteIndex().get(i).setDeleted(l);
i=getMasterNoteIndex().size();
}
}
for (int i=0; i<getNoteIndex().size(); i++) {
if (getNoteIndex().get(i).getGuid().equals(guid)) {
getNoteIndex().get(i).setActive(false);
getNoteIndex().get(i).setDeleted(l);
i=getNoteIndex().size();
}
}
conn.getNoteTable().deleteNote(guid);
reloadTrashCount();
}
// Delete a note
public void restoreNote(String guid) {
trashCounterRunner.abortCount = true;
for (int i=0; i<getMasterNoteIndex().size(); i++) {
if (getMasterNoteIndex().get(i).getGuid().equals(guid)) {
getMasterNoteIndex().get(i).setActive(true);
getMasterNoteIndex().get(i).setDeleted(0);
i=getMasterNoteIndex().size();
}
}
for (int i=0; i<getNoteIndex().size(); i++) {
if (getNoteIndex().get(i).getGuid().equals(guid)) {
getNoteIndex().get(i).setActive(true);
getNoteIndex().get(i).setDeleted(0);
i=getNoteIndex().size();
}
}
conn.getNoteTable().restoreNote(guid);
reloadTrashCount();
}
public void updateNote(Note n) {
for (int i=0; i<getMasterNoteIndex().size(); i++) {
if (getMasterNoteIndex().get(i).getGuid().equals(n.getGuid())) {
getMasterNoteIndex().remove(i);
getMasterNoteIndex().add(n);
}
}
for (int i=0; i<getNoteIndex().size(); i++) {
if (getNoteIndex().get(i).getGuid().equals(n.getGuid())) {
getNoteIndex().get(i).setActive(true);
getNoteIndex().get(i).setDeleted(0);
i=getNoteIndex().size();
}
}
conn.getNoteTable().updateNote(n);
}
// Add a note.
public void addNote(Note n, NoteMetadata meta) {
noteModel.addNote(n, meta);
noteModel.metaData.put(n.getGuid(), meta);
}
// Expunge a note
public void expungeNote(String guid) {
trashCounterRunner.abortCount = true;
for (int i=0; i<getMasterNoteIndex().size(); i++) {
if (getMasterNoteIndex().get(i).getGuid().equals(guid)) {
getMasterNoteIndex().remove(i);
i=getMasterNoteIndex().size();
}
}
for (int i=0; i<getNoteIndex().size(); i++) {
if (getNoteIndex().get(i).getGuid().equals(guid)) {
getNoteIndex().remove(i);
i=getNoteIndex().size();
}
}
conn.getNoteTable().expungeNote(guid, false, true);
reloadTrashCount();
}
// Expunge a note
public void emptyTrash() {
trashCounterRunner.abortCount = true;
for (int i=getMasterNoteIndex().size()-1; i>=0; i--) {
if (!getMasterNoteIndex().get(i).isActive()) {
getMasterNoteIndex().remove(i);
}
}
for (int i=getNoteIndex().size()-1; i>=0; i--) {
if (!getNoteIndex().get(i).isActive()) {
getNoteIndex().remove(i);
}
}
conn.getNoteTable().expungeAllDeletedNotes();
reloadTrashCount();
}
// The trash counter thread has produced a result
@SuppressWarnings("unused")
private void trashSignalReceiver(Integer i) {
trashCount = i;
trashSignal.countChanged.emit(i);
}
// Update note contents
public void updateNoteContent(String guid, String content) {
logger.log(logger.HIGH, "Entering ListManager.updateNoteContent");
// EnmlConverter enml = new EnmlConverter(logger);
// String text = enml.convert(guid, content);
// Update the list tables
/* for (int i=0; i<masterNoteIndex.size(); i++) {
if (masterNoteIndex.get(i).getGuid().equals(guid)) {
masterNoteIndex.get(i).setContent(text);
i = masterNoteIndex.size();
}
}
// Update the list tables
for (int i=0; i<getNoteIndex().size(); i++) {
if (getNoteIndex().get(i).getGuid().equals(guid)) {
getNoteIndex().get(i).setContent(text);
i = getNoteIndex().size();
}
}
*/
// Check if any new tags were encountered
/* if (enml.saveInvalidXML) {
List<String> elements = Global.invalidElements;
for (int i=0; i<elements.size(); i++) {
conn.getInvalidXMLTable().addInvalidElement(elements.get(i));
}
for (String key : Global.invalidAttributes.keySet()) {
ArrayList<String> attributes = Global.invalidAttributes.get(key);
for (int i=0; i<attributes.size(); i++) {
conn.getInvalidXMLTable().addInvalidAttribute(key, attributes.get(i));
}
}
}
*/
saveRunner.addWork(guid, content);
// conn.getNoteTable().updateNoteContent(guid, content);
logger.log(logger.HIGH, "Leaving ListManager.updateNoteContent");
}
// Update a note creation date
public void updateNoteCreatedDate(String guid, QDateTime date) {
noteModel.updateNoteCreatedDate(guid, date);
conn.getNoteTable().updateNoteCreatedDate(guid, date);
}
// Subject date has been changed
public void updateNoteSubjectDate(String guid, QDateTime date) {
noteModel.updateNoteSubjectDate(guid, date);
conn.getNoteTable().updateNoteSubjectDate(guid, date);
}
// Author has changed
public void updateNoteAuthor(String guid, String author) {
noteModel.updateNoteAuthor(guid, author);
conn.getNoteTable().updateNoteAuthor(guid, author);
}
// Author has changed
public void updateNoteGeoTag(String guid, Double lon, Double lat, Double alt) {
for (int i=0; i<getMasterNoteIndex().size(); i++) {
if (getMasterNoteIndex().get(i).getGuid().equals(guid)) {
getMasterNoteIndex().get(i).getAttributes().setLongitude(lon);
getMasterNoteIndex().get(i).getAttributes().setLongitudeIsSet(true);
getMasterNoteIndex().get(i).getAttributes().setLatitude(lat);
getMasterNoteIndex().get(i).getAttributes().setLatitudeIsSet(true);
getMasterNoteIndex().get(i).getAttributes().setAltitude(alt);
getMasterNoteIndex().get(i).getAttributes().setAltitudeIsSet(true);
i = getMasterNoteIndex().size();
}
}
// Update the list tables
for (int i=0; i<getNoteIndex().size(); i++) {
if (getNoteIndex().get(i).getGuid().equals(guid)) {
getNoteIndex().get(i).getAttributes().setLongitude(lon);
getNoteIndex().get(i).getAttributes().setLongitudeIsSet(true);
getNoteIndex().get(i).getAttributes().setLatitude(lat);
getNoteIndex().get(i).getAttributes().setLatitudeIsSet(true);
getNoteIndex().get(i).getAttributes().setAltitude(alt);
getNoteIndex().get(i).getAttributes().setAltitudeIsSet(true);
i = getNoteIndex().size();
}
}
conn.getNoteTable().updateNoteGeoTags(guid, lon, lat, alt);
}
// Source URL changed
public void updateNoteSourceUrl(String guid, String url) {
noteModel.updateNoteSourceUrl(guid, url);
conn.getNoteTable().updateNoteSourceUrl(guid, url);
}
// Update a note last changed date
public void updateNoteAlteredDate(String guid, QDateTime date) {
noteModel.updateNoteChangedDate(guid, date);
conn.getNoteTable().updateNoteAlteredDate(guid, date);
}
// Update a note title
public void updateNoteTitle(String guid, String title) {
logger.log(logger.HIGH, "Entering ListManager.updateNoteTitle");
conn.getNoteTable().updateNoteTitle(guid, title);
noteModel.updateNoteTitle(guid, title);
logger.log(logger.HIGH, "Leaving ListManager.updateNoteTitle");
}
// Update a note's notebook
public void updateNoteNotebook(String guid, String notebookGuid) {
logger.log(logger.HIGH, "Entering ListManager.updateNoteNotebook");
noteModel.updateNoteNotebook(guid, notebookGuid);
conn.getNoteTable().updateNoteNotebook(guid, notebookGuid, true);
logger.log(logger.HIGH, "Leaving ListManager.updateNoteNotebook");
}
// Update a note sequence number
public void updateNoteSequence(String guid, int sequence) {
logger.log(logger.HIGH, "Entering ListManager.updateNoteSequence");
conn.getNoteTable().updateNoteSequence(guid, sequence);
for (int i=0; i<noteModel.getMasterNoteIndex().size(); i++) {
if (noteModel.getMasterNoteIndex().get(i).getGuid().equals(guid)) {
noteModel.getMasterNoteIndex().get(i).setUpdateSequenceNum(sequence);
i=noteModel.getMasterNoteIndex().size()+1;
}
}
for (int i=0; i<getNoteIndex().size(); i++) {
if (getNoteIndex().get(i).getGuid().equals(guid)) {
getNoteIndex().get(i).setUpdateSequenceNum(sequence);
i=getNoteIndex().size()+1;
}
}
logger.log(logger.HIGH, "Leaving ListManager.updateNoteSequence");
}
public void updateNoteGuid(String oldGuid, String newGuid, boolean updateDatabase) {
logger.log(logger.HIGH, "Entering ListManager.updateNoteGuid");
if (updateDatabase)
conn.getNoteTable().updateNoteGuid(oldGuid, newGuid);
noteModel.updateNoteGuid(oldGuid, newGuid);
logger.log(logger.HIGH, "Leaving ListManager.updateNoteGuid");
}
//************************************************************************************
//************************************************************************************
//** Tag functions
//************************************************************************************
//************************************************************************************
// Update a tag sequence number
public void updateTagSequence(String guid, int sequence) {
logger.log(logger.HIGH, "Entering ListManager.updateTagSequence");
conn.getTagTable().updateTagSequence(guid, sequence);
for (int i=0; i<tagIndex.size(); i++) {
if (tagIndex.get(i).getGuid().equals(guid)) {
getTagIndex().get(i).setUpdateSequenceNum(sequence);
i=tagIndex.size()+1;
}
}
logger.log(logger.HIGH, "Leaving ListManager.updateTagSequence");
}
// Update a tag guid number
public void updateTagGuid(String oldGuid, String newGuid) {
logger.log(logger.HIGH, "Entering ListManager.updateTagGuid");
conn.getTagTable().updateTagGuid(oldGuid, newGuid);
for (int i=0; i<tagIndex.size(); i++) {
if (tagIndex.get(i).getGuid().equals(oldGuid)) {
tagIndex.get(i).setGuid(newGuid);
i=tagIndex.size()+1;
}
}
logger.log(logger.HIGH, "Leaving ListManager.updateTagGuid");
}
// Find all children for a tag
public List<Tag> findAllChildren(String guid) {
List<Tag> tags = new ArrayList<Tag>();
return findAllChildrenRecursive(guid, tags);
}
public List<Tag> findAllChildrenRecursive(String guid, List<Tag> tags) {
// Start looping through the tags. If we find a tag which has a parent that
// matches guid, then we add it to the list of tags & search for its children.
for (int i=0; i<getTagIndex().size(); i++) {
if (getTagIndex().get(i).getParentGuid() != null && getTagIndex().get(i).getParentGuid().equals(guid)) {
tags.add(getTagIndex().get(i));
tags = findAllChildrenRecursive(getTagIndex().get(i).getGuid(), tags);
}
}
return tags;
}
// Give a list of tags, does any of them match a child tag?
public boolean checkNoteForChildTags(String guid, List<String> noteTags) {
boolean returnValue = false;
List<Tag> children = findAllChildren(guid);
for (int i=0; i<noteTags.size(); i++) {
String noteTag = noteTags.get(i);
for (int j=0; j<children.size(); j++) {
if (noteTag.equals(children.get(j).getGuid()))
return true;
}
}
return returnValue;
}
//************************************************************************************
//************************************************************************************
//** Notebook functions
//************************************************************************************
//************************************************************************************
// Delete a notebook
public void deleteNotebook(String guid) {
for (int i=0; i<getNotebookIndex().size(); i++) {
if (getNotebookIndex().get(i).getGuid().equals(guid)) {
getNotebookIndex().remove(i);
i=getMasterNoteIndex().size();
}
}
conn.getNotebookTable().expungeNotebook(guid, true);
}
// Rename a stack
public void renameStack(String oldName, String newName) {
for (int i=0; i<getNotebookIndex().size(); i++) {
if (getNotebookIndex().get(i).getStack() != null &&
getNotebookIndex().get(i).getStack().equalsIgnoreCase(oldName)) {
getNotebookIndex().get(i).setStack(newName);
}
}
}
// Update a notebook sequence number
public void updateNotebookSequence(String guid, int sequence) {
logger.log(logger.HIGH, "Entering ListManager.updateNotebookSequence");
conn.getNotebookTable().updateNotebookSequence(guid, sequence);
for (int i=0; i<notebookIndex.size(); i++) {
if (notebookIndex.get(i).getGuid().equals(guid)) {
notebookIndex.get(i).setUpdateSequenceNum(sequence);
i=notebookIndex.size()+1;
}
}
logger.log(logger.HIGH, "Leaving ListManager.updateNotebookSequence");
}
// Update a notebook Guid number
public void updateNotebookGuid(String oldGuid, String newGuid) {
logger.log(logger.HIGH, "Entering ListManager.updateNotebookGuid");
conn.getNotebookTable().updateNotebookGuid(oldGuid, newGuid);
for (int i=0; i<notebookIndex.size(); i++) {
if (notebookIndex.get(i).getGuid().equals(oldGuid)) {
notebookIndex.get(i).setGuid(newGuid);
i=notebookIndex.size()+1;
}
}
logger.log(logger.HIGH, "Leaving ListManager.updateNotebookGuid");
}
// Update a notebook Guid number
public void updateNotebookStack(String oldGuid, String stack) {
logger.log(logger.HIGH, "Entering ListManager.updateNotebookGuid");
conn.getNotebookTable().setStack(oldGuid, stack);
for (int i=0; i<notebookIndex.size(); i++) {
if (notebookIndex.get(i).getGuid().equals(oldGuid)) {
notebookIndex.get(i).setStack(stack);
i=notebookIndex.size()+1;
}
}
logger.log(logger.HIGH, "Leaving ListManager.updateNotebookGuid");
}
//************************************************************************************
//************************************************************************************
//** Load and filter the note index
//************************************************************************************
//************************************************************************************
public void noteDownloaded(Note n) {
boolean found = false;
for (int i=0; i<getMasterNoteIndex().size(); i++) {
if (getMasterNoteIndex().get(i).getGuid().equals(n.getGuid())) {
getMasterNoteIndex().set(i,n);
found = true;
i=getMasterNoteIndex().size();
}
}
if (!found)
getMasterNoteIndex().add(n);
for (int i=0; i<getNoteIndex().size(); i++) {
if (getNoteIndex().get(i).getGuid().equals(n.getGuid())) {
if (filterRecord(getNoteIndex().get(i)))
getNoteIndex().add(n);
getNoteIndex().remove(i);
i=getNoteIndex().size();
}
}
if (filterRecord(n))
getNoteIndex().add(n);
}
// Check if a note matches the currently selected notebooks, tags, or attribute searches.
public boolean filterRecord(Note n) {
boolean goodNotebook = false;
boolean goodTag = false;
boolean goodStatus = false;
// Check note status
if (!n.isActive() && Global.showDeleted)
return true;
else {
if (n.isActive() && !Global.showDeleted)
goodStatus = true;
}
// Begin filtering results
if (goodStatus)
goodNotebook = filterByNotebook(n.getNotebookGuid());
if (goodNotebook)
goodTag = filterByTag(n.getTagGuids());
if (goodTag) {
boolean goodCreatedBefore = false;
boolean goodCreatedSince = false;
boolean goodChangedBefore = false;
boolean goodChangedSince = false;
boolean goodContains = false;
if (!Global.createdBeforeFilter.hasSelection())
goodCreatedBefore = true;
else
goodCreatedBefore = Global.createdBeforeFilter.check(n);
if (!Global.createdSinceFilter.hasSelection())
goodCreatedSince = true;
else
goodCreatedSince = Global.createdSinceFilter.check(n);
if (!Global.changedBeforeFilter.hasSelection())
goodChangedBefore = true;
else
goodChangedBefore = Global.changedBeforeFilter.check(n);
if (!Global.changedSinceFilter.hasSelection())
goodChangedSince = true;
else
goodChangedSince = Global.changedSinceFilter.check(n);
if (!Global.containsFilter.hasSelection())
goodContains = true;
else
goodContains = Global.containsFilter.check(conn.getNoteTable(), n);
if (goodCreatedSince && goodCreatedBefore && goodChangedSince && goodChangedBefore && goodContains)
return true;
}
return false;
}
// Trigger a recount of counters
public void refreshCounters() {
// refreshCounters= false;
if (!refreshCounters)
return;
refreshCounters = false;
tagCounterRunner.abortCount = true;
notebookCounterRunner.abortCount = true;
trashCounterRunner.abortCount = true;
countNotebookResults(getNoteIndex());
countTagResults(getNoteIndex());
reloadTrashCount();
}
// Load the note index based upon what the user wants.
public void loadNotesIndex() {
logger.log(logger.EXTREME, "Entering ListManager.loadNotesIndex()");
List<Note> matches;
if (enSearchChanged || getMasterNoteIndex() == null)
matches = enSearch.matchWords();
else
matches = getMasterNoteIndex();
if (matches == null)
matches = getMasterNoteIndex();
setNoteIndex(new ArrayList<Note>());
for (int i=0; i<matches.size(); i++) {
if (filterRecord(matches.get(i)))
getNoteIndex().add(matches.get(i));
}
refreshCounters = true;
enSearchChanged = false;
logger.log(logger.EXTREME, "Leaving ListManager.loadNotesIndex()");
}
public void countNotebookResults(List<Note> index) {
logger.log(logger.EXTREME, "Entering ListManager.countNotebookResults()");
notebookCounterRunner.abortCount = true;
if (!Global.mimicEvernoteInterface)
notebookCounterRunner.setNoteIndex(index);
else
notebookCounterRunner.setNoteIndex(getMasterNoteIndex());
notebookCounterRunner.release(CounterRunner.NOTEBOOK);
logger.log(logger.EXTREME, "Leaving ListManager.countNotebookResults()");
}
public void countTagResults(List<Note> index) {
logger.log(logger.EXTREME, "Entering ListManager.countTagResults");
trashCounterRunner.abortCount = true;
if (!Global.tagBehavior().equalsIgnoreCase("DoNothing"))
tagCounterRunner.setNoteIndex(index);
else
tagCounterRunner.setNoteIndex(getMasterNoteIndex());
tagCounterRunner.release(CounterRunner.TAG);
logger.log(logger.EXTREME, "Leaving ListManager.countTagResults()");
}
// Update the count of items in the trash
public void reloadTrashCount() {
logger.log(logger.EXTREME, "Entering ListManager.reloadTrashCount");
trashCounterRunner.abortCount = true;
trashCounterRunner.setNoteIndex(getMasterNoteIndex());
trashCounterRunner.release(CounterRunner.TRASH);
logger.log(logger.EXTREME, "Leaving ListManager.reloadTrashCount");
}
private boolean filterByNotebook(String guid) {
boolean good = false;
if (selectedNotebooks.size() == 0)
good = true;
if (!good && selectedNotebooks.contains(guid))
good = true;
for (int i=0; i<getArchiveNotebookIndex().size() && good; i++) {
if (guid.equals(getArchiveNotebookIndex().get(i).getGuid())) {
good = false;
return good;
}
}
return good;
}
private boolean filterByTag(List<String> noteTags) {
// If either the note has no tags or there are
// no selected tags, then any note is good.
if (noteTags == null || selectedTags == null)
return true;
// If there are no tags selected, then any note is good
if (selectedTags.size() == 0)
return true;
// If ALL tags must be matched, then check ALL note tags,
// otherwise we match on any criteria.
if (!Global.anyTagSelectionMatch()) {
for (int i=0; i<selectedTags.size(); i++) {
String selectedGuid = selectedTags.get(i);
boolean childMatch = false;
// If we should include children in the results
if (Global.includeTagChildren()) {
childMatch = checkNoteForChildTags(selectedGuid, noteTags);
// Do we have a match with this tag or any children
if (!noteTags.contains(selectedGuid)&& !childMatch)
return false;
} else {
// Does this note have a matching tag
if (!noteTags.contains(selectedGuid))
return false;
}
}
return true;
} else {
// Any match is displayed.
for (int i=0; i<selectedTags.size(); i++) {
String selectedGuid = selectedTags.get(i);
// If we have a simple match, then we're good
if (noteTags.contains(selectedGuid))
return true;
// If we have a match with one of the children tags && we should include child tags
if (Global.includeTagChildren() && checkNoteForChildTags(selectedGuid, noteTags))
return true;
}
return false;
}
}
public void setNoteSynchronized(String guid, boolean value) {
getNoteTableModel().updateNoteSyncStatus(guid, value);
}
public void updateNoteTitleColor(String guid, Integer color) {
NoteMetadata meta = getNoteMetadata().get(guid);
if (meta != null) {
noteModel.updateNoteTitleColor(guid, color);
meta.setColor(color);
conn.getNoteTable().updateNoteMetadata(meta);
}
}
public void loadNoteTitleColors() {
noteModel.setMetaData(getNoteMetadata());
}
//********************************************************************************
//********************************************************************************
//* Support signals from the index thread
//********************************************************************************
//********************************************************************************
// Reset a flag if an index is needed
public void setIndexNeeded(String guid, String type, Boolean b) {
if (Global.keepRunning && type.equalsIgnoreCase("content"))
conn.getNoteTable().setIndexNeeded(guid, false);
if (Global.keepRunning && type.equalsIgnoreCase("resource")) {
conn.getNoteTable().noteResourceTable.setIndexNeeded(guid, b);
}
}
public boolean threadCheck(int id) {
if (id == Global.notebookCounterThreadId)
return notebookThread.isAlive();
if (id == Global.tagCounterThreadId)
return tagThread.isAlive();
if (id == Global.trashCounterThreadId)
return trashThread.isAlive();
if (id == Global.saveThreadId)
return saveThread.isAlive();
return false;
}
//********************************************************************************
//********************************************************************************
//* Utility Functions
//********************************************************************************
//********************************************************************************
public void compactDatabase() {
conn.compactDatabase();
// IndexConnection idx = new IndexConnection(logger, "nevernote-compact");
// idx.dbSetup();
// idx.dbShutdown();
}
// Rebuild the note HTML to something usable
public List<String> scanNoteForResources(Note n) {
logger.log(logger.HIGH, "Entering ListManager.scanNoteForResources");
logger.log(logger.EXTREME, "Note guid: " +n.getGuid());
QDomDocument doc = new QDomDocument();
QDomDocument.Result result = doc.setContent(n.getContent());
if (!result.success) {
logger.log(logger.MEDIUM, "Parse error when scanning note for resources.");
logger.log(logger.MEDIUM, "Note guid: " +n.getGuid());
return null;
}
List<String> returnArray = new ArrayList<String>();
QDomNodeList anchors = doc.elementsByTagName("en-media");
for (int i=0; i<anchors.length(); i++) {
QDomElement enmedia = anchors.at(i).toElement();
if (enmedia.hasAttribute("type")) {
QDomAttr hash = enmedia.attributeNode("hash");
returnArray.add(hash.value().toString());
}
}
logger.log(logger.HIGH, "Leaving ListManager.scanNoteForResources");
return returnArray;
}
// Given a list of tags, produce a string list of tag names
public String getTagNamesForNote(Note n) {
StringBuffer buffer = new StringBuffer(100);
Vector<String> v = new Vector<String>();
List<String> guids = n.getTagGuids();
if (guids == null)
return "";
for (int i=0; i<guids.size(); i++) {
v.add(getTagNameByGuid(guids.get(i)));
}
Comparator<String> comparator = Collections.reverseOrder();
Collections.sort(v,comparator);
Collections.reverse(v);
for (int i = 0; i<v.size(); i++) {
if (i>0)
buffer.append(", ");
buffer.append(v.get(i));
}
return buffer.toString();
}
// Get a tag name when given a tag guid
public String getTagNameByGuid(String guid) {
for (int i=0; i<getTagIndex().size(); i++) {
String s = getTagIndex().get(i).getGuid();
if (s.equals(guid)) {
return getTagIndex().get(i).getName();
}
}
return "";
}
// For a notebook guid, return the name
public String getNotebookNameByGuid(String guid) {
if (notebookIndex == null)
return null;
for (int i=0; i<notebookIndex.size(); i++) {
String s = notebookIndex.get(i).getGuid();
if (s.equals(guid)) {
return notebookIndex.get(i).getName();
}
}
return "";
}
// Reload the note's tag names. This is called when a tag's name changes by
// the user. It updates all notes with that tag to the new tag name.
public void reloadNoteTagNames(String tagGuid, String newName) {
// Set the master index
for (int i=0; i<getMasterNoteIndex().size(); i++) {
for (int j=0; j<getMasterNoteIndex().get(i).getTagGuids().size(); j++) {
if (getMasterNoteIndex().get(i).getTagGuids().get(j).equals(tagGuid)) {
getMasterNoteIndex().get(i).getTagNames().set(j, newName);
}
}
}
// Set the current index
for (int i=0; i<getNoteIndex().size(); i++) {
for (int j=0; j<getNoteIndex().get(i).getTagGuids().size(); j++) {
if (getNoteIndex().get(i).getTagGuids().get(j).equals(tagGuid)) {
getNoteIndex().get(i).getTagNames().set(j, newName);
}
}
}
}
}