blob: 9177a0ae0608139c723a04f8bf8cbe3180c068b6 [file] [log] [blame]
#include "filesystemwatcher.h"
#include "ui_filesystemwatcher.h"
FileSystemWatcher::FileSystemWatcher(QString dirPath, QWidget *parent) :
QMainWindow(parent),
m_ui(new Ui::FileSystemWatcher),
m_watcher(new QFileSystemWatcher()),
m_listViewModel(new QStringListModel()),
m_listView(new QListView()),
m_timer(new QTimer(this)),
m_dirPath(dirPath)
{
// setup user interface
m_ui->setupUi(this);
// add main directory to monitor
m_watcher->addPath(m_dirPath);
// create the view
m_listView->setModel(m_listViewModel);
setCentralWidget(m_listView);
// set title
setWindowTitle("ChronoShare");
// register signals (callback functions)
connect(m_watcher, SIGNAL(directoryChanged(QString)), this, SLOT(watcherCallbackSlot(QString)));
connect(m_timer, SIGNAL(timeout()), this, SLOT(timerCallbackSlot()));
// bootstrap
timerCallbackSlot();
// start timer
m_timer->start(60000);
}
FileSystemWatcher::~FileSystemWatcher()
{
// clean up
delete m_ui;
delete m_watcher;
delete m_listViewModel;
delete m_listView;
delete m_timer;
}
void FileSystemWatcher::watcherCallbackSlot(QString dirPath)
{
// watcher specific steps
qDebug() << endl << "[WATCHER] Triggered Path: " << dirPath;
handleCallback(dirPath);
}
void FileSystemWatcher::timerCallbackSlot()
{
// timer specific steps
qDebug() << endl << "[TIMER] Triggered Path: " << m_dirPath;
handleCallback(m_dirPath);
}
void FileSystemWatcher::handleCallback(QString dirPath)
{
// scan directory and populate file list
QHash<QString, sFileInfo> currentState = scanDirectory(dirPath);
// reconcile directory and report changes
QVector<sEventInfo> dirChanges = reconcileDirectory(currentState, dirPath);
// DEBUG: Print to Gui
printToGui(dirChanges);
}
QHash<QString, sFileInfo> FileSystemWatcher::scanDirectory(QString dirPath)
{
// list of files in directory
QHash<QString, sFileInfo> currentState;
// directory iterator (recursive)
QDirIterator dirIterator(dirPath, QDirIterator::Subdirectories |
QDirIterator::FollowSymlinks);
// iterate through directory recursively
while(dirIterator.hasNext())
{
// Get Next File/Dir
dirIterator.next();
// Get FileInfo
QFileInfo fileInfo = dirIterator.fileInfo();
// if not this directory or previous directory
if(fileInfo.absoluteFilePath() != ".." && fileInfo.absoluteFilePath() != ".")
{
QString absFilePath = fileInfo.absoluteFilePath();
// if this is a directory
if(fileInfo.isDir())
{
QStringList dirList = m_watcher->directories();
// if the directory is not already being watched
if (absFilePath.startsWith(m_dirPath) && !dirList.contains(absFilePath))
{
// add this directory to the watch list
m_watcher->addPath(absFilePath);
}
}
else
{
// construct struct
sFileInfo fileInfoStruct;
fileInfoStruct.fileInfo = fileInfo;
fileInfoStruct.hash = calcChecksum(absFilePath);
// add this file to the file list
currentState.insert(absFilePath, fileInfoStruct);
}
}
}
return currentState;
}
QVector<sEventInfo> FileSystemWatcher::reconcileDirectory(QHash<QString, sFileInfo> currentState, QString dirPath)
{
// list of files changed
QVector<sEventInfo> dirChanges;
// compare result (database/stored snapshot) to fileList (current snapshot)
QMutableHashIterator<QString, sFileInfo> i(m_storedState);
while(i.hasNext())
{
i.next();
QString absFilePath = i.key();
if(!absFilePath.startsWith(dirPath))
{
continue;
}
sFileInfo storedFileInfoStruct = i.value();
QFileInfo storedFileInfo = storedFileInfoStruct.fileInfo;
QByteArray storedHash = storedFileInfoStruct.hash;
// check file existence
if(currentState.contains(absFilePath))
{
sFileInfo currentFileInfoStruct = currentState.value(absFilePath);
QFileInfo currentFileInfo = currentFileInfoStruct.fileInfo;
QByteArray currentHash = currentFileInfoStruct.hash;
if((storedFileInfo != currentFileInfo) || (storedHash != currentHash))
{
// update stored state
i.setValue(currentFileInfoStruct);
// this file has been modified
sEventInfo eventInfo;
eventInfo.event = MODIFIED;
eventInfo.absFilePath = absFilePath;
dirChanges.push_back(eventInfo);
}
// delete this file from fileList we have processed it
currentState.remove(absFilePath);
}
else
{
// delete from stored state
i.remove();
// this file has been deleted
sEventInfo eventInfo;
eventInfo.event = DELETED;
eventInfo.absFilePath = absFilePath;
dirChanges.push_back(eventInfo);
}
}
// any files left in fileList have been added
for(QHash<QString, sFileInfo>::iterator i = currentState.begin(); i != currentState.end(); ++i)
{
QString absFilePath = i.key();
sFileInfo currentFileInfoStruct = i.value();
m_storedState.insert(absFilePath, currentFileInfoStruct);
// this file has been added
sEventInfo eventInfo;
eventInfo.event = ADDED;
eventInfo.absFilePath = absFilePath;
dirChanges.push_back(eventInfo);
}
return dirChanges;
}
QByteArray FileSystemWatcher::calcChecksum(QString absFilePath)
{
// initialize checksum
QCryptographicHash crypto(QCryptographicHash::Md5);
// open file
QFile file(absFilePath);
file.open(QFile::ReadOnly);
// calculate checksum
while(!file.atEnd())
{
crypto.addData(file.read(8192));
}
return crypto.result();
}
void FileSystemWatcher::printToGui(QVector<sEventInfo> dirChanges)
{
if(!dirChanges.isEmpty())
{
QStringList dirChangesList;
for(int i = 0; i < dirChanges.size(); i++)
{
QString tempString;
eEvent event = dirChanges[i].event;
QString absFilePath = dirChanges[i].absFilePath;
switch(event)
{
case ADDED:
tempString.append("ADDED: ");
break;
case MODIFIED:
tempString.append("MODIFIED: ");
break;
case DELETED:
tempString.append("DELETED: ");
break;
}
tempString.append(absFilePath);
dirChangesList.append(tempString);
qDebug() << "\t" << tempString;
}
m_listViewModel->setStringList(dirChangesList);
}
}
#if WAF
#include "filesystemwatcher.moc"
#include "filesystemwatcher.cpp.moc"
#endif