SyncEngine & UI: Progress notifications for update phase

For each directory (local and remote, we have UI update throtting code)
a signal is emitted.
It is used by the settings dialog and the tray menu.
This commit is contained in:
Markus Goetz 2014-08-15 15:00:10 +02:00
parent b40b670639
commit ce2741cebc
14 changed files with 88 additions and 26 deletions

View file

@ -134,23 +134,6 @@ enum csync_ftw_type_e {
CSYNC_FTW_TYPE_SKIP CSYNC_FTW_TYPE_SKIP
}; };
enum csync_notify_type_e {
CSYNC_NOTIFY_INVALID,
CSYNC_NOTIFY_START_SYNC_SEQUENCE,
CSYNC_NOTIFY_START_DOWNLOAD,
CSYNC_NOTIFY_START_UPLOAD,
CSYNC_NOTIFY_PROGRESS,
CSYNC_NOTIFY_FINISHED_DOWNLOAD,
CSYNC_NOTIFY_FINISHED_UPLOAD,
CSYNC_NOTIFY_FINISHED_SYNC_SEQUENCE,
CSYNC_NOTIFY_START_DELETE,
CSYNC_NOTIFY_END_DELETE,
CSYNC_NOTIFY_ERROR,
CSYNC_NOTIFY_START_LOCAL_UPDATE,
CSYNC_NOTIFY_FINISHED_LOCAL_UPDATE,
CSYNC_NOTIFY_START_REMOTE_UPDATE,
CSYNC_NOTIFY_FINISHED_REMOTE_UPDATE
};
/** /**
* CSync File Traversal structure. * CSync File Traversal structure.
@ -203,6 +186,9 @@ typedef void (*csync_log_callback) (int verbosity,
const char *buffer, const char *buffer,
void *userdata); void *userdata);
typedef void (*csync_update_callback) (bool local,
const char *dirUrl,
void *userdata);
/** /**
* @brief Allocate a csync context. * @brief Allocate a csync context.

View file

@ -24,6 +24,8 @@
#include <inttypes.h> #include <inttypes.h>
#include "csync_private.h"
/* /*
* helper method to build up a user text for SSL problems, called from the * helper method to build up a user text for SSL problems, called from the
@ -537,6 +539,8 @@ static struct listdir_context *fetch_resource_list(csync_owncloud_ctx_t *ctx, co
} }
} }
ctx->csync_ctx->callbacks.update_callback(false, curi, ctx->csync_ctx->callbacks.update_callback_userdata);
fetchCtx = c_malloc( sizeof( struct listdir_context )); fetchCtx = c_malloc( sizeof( struct listdir_context ));
if (!fetchCtx) { if (!fetchCtx) {
errno = ENOMEM; errno = ENOMEM;

View file

@ -54,6 +54,7 @@ struct listdir_context *get_listdir_context_from_recursive_cache(csync_owncloud_
DEBUG_WEBDAV("get_listdir_context_from_recursive_cache No element %s in cache found", curi); DEBUG_WEBDAV("get_listdir_context_from_recursive_cache No element %s in cache found", curi);
return NULL; return NULL;
} }
ctx->csync_ctx->callbacks.update_callback(false, curi, ctx->csync_ctx->callbacks.update_callback_userdata);
/* Out of the element, create a listdir_context.. if we could be sure that it is immutable, we could ref instead.. need to investigate */ /* Out of the element, create a listdir_context.. if we could be sure that it is immutable, we could ref instead.. need to investigate */
fetchCtx = c_malloc( sizeof( struct listdir_context )); fetchCtx = c_malloc( sizeof( struct listdir_context ));
@ -141,6 +142,12 @@ static void propfind_results_recursive_callback(void *userdata,
element->parent = NULL; element->parent = NULL;
c_rbtree_insert(ctx->propfind_recursive_cache, element); c_rbtree_insert(ctx->propfind_recursive_cache, element);
/* DEBUG_WEBDAV("results_recursive Added collection %s", newres->uri); */ /* DEBUG_WEBDAV("results_recursive Added collection %s", newres->uri); */
// We do this here and in get_listdir_context_from_recursive_cache because
// a recursive PROPFIND might take some time but we still want to
// be informed. Later when get_listdir_context_from_recursive_cache is
// called the DB queries might be the problem causing slowness, so do it again there then.
ctx->csync_ctx->callbacks.update_callback(false, path, ctx->csync_ctx->callbacks.update_callback_userdata);
} }
} }
@ -189,6 +196,7 @@ void fetch_resource_list_recursive(csync_owncloud_ctx_t *ctx, const char *uri, c
int depth = NE_DEPTH_INFINITE; int depth = NE_DEPTH_INFINITE;
DEBUG_WEBDAV("fetch_resource_list_recursive Starting recursive propfind %s %s", uri, curi); DEBUG_WEBDAV("fetch_resource_list_recursive Starting recursive propfind %s %s", uri, curi);
ctx->csync_ctx->callbacks.update_callback(false, curi, ctx->csync_ctx->callbacks.update_callback_userdata);
/* do a propfind request and parse the results in the results function, set as callback */ /* do a propfind request and parse the results in the results function, set as callback */
hdl = ne_propfind_create(ctx->dav_session.ctx, curi, depth); hdl = ne_propfind_create(ctx->dav_session.ctx, curi, depth);

View file

@ -87,6 +87,8 @@ struct csync_s {
struct { struct {
csync_auth_callback auth_function; csync_auth_callback auth_function;
void *userdata; void *userdata;
csync_update_callback update_callback;
void *update_callback_userdata;
} callbacks; } callbacks;
c_strlist_t *excludes; c_strlist_t *excludes;

View file

@ -48,6 +48,7 @@ csync_vio_handle_t *csync_vio_opendir(CSYNC *ctx, const char *name) {
return owncloud_opendir(ctx, name); return owncloud_opendir(ctx, name);
break; break;
case LOCAL_REPLICA: case LOCAL_REPLICA:
ctx->callbacks.update_callback(ctx->replica, name, ctx->callbacks.update_callback_userdata);
return csync_vio_local_opendir(name); return csync_vio_local_opendir(name);
break; break;
default: default:

View file

@ -573,6 +573,14 @@ void AccountSettings::slotSetProgress(const QString& folder, const Progress::Inf
QStandardItem *item = itemForFolder( folder ); QStandardItem *item = itemForFolder( folder );
if( !item ) return; if( !item ) return;
// switch on extra space.
item->setData( QVariant(true), FolderStatusDelegate::AddProgressSpace );
if (!progress._currentDiscoveredFolder.isEmpty()) {
item->setData( tr("Discovering %1").arg(progress._currentDiscoveredFolder) , FolderStatusDelegate::SyncProgressItemString );
return;
}
if(!progress._lastCompletedItem.isEmpty() if(!progress._lastCompletedItem.isEmpty()
&& Progress::isWarningKind(progress._lastCompletedItem._status)) { && Progress::isWarningKind(progress._lastCompletedItem._status)) {
int warnCount = item->data(FolderStatusDelegate::WarningCount).toInt(); int warnCount = item->data(FolderStatusDelegate::WarningCount).toInt();
@ -600,8 +608,7 @@ void AccountSettings::slotSetProgress(const QString& folder, const Progress::Inf
QString itemFileName = shortenFilename(folder, curItem._file); QString itemFileName = shortenFilename(folder, curItem._file);
QString kindString = Progress::asActionString(curItem); QString kindString = Progress::asActionString(curItem);
// switch on extra space.
item->setData( QVariant(true), FolderStatusDelegate::AddProgressSpace );
QString fileProgressString; QString fileProgressString;
if (Progress::isSizeDependent(curItem._instruction)) { if (Progress::isSizeDependent(curItem._instruction)) {

View file

@ -591,6 +591,7 @@ void Folder::startSync(const QStringList &pathList)
//direct connection so the message box is blocking the sync. //direct connection so the message box is blocking the sync.
connect(_engine.data(), SIGNAL(aboutToRemoveAllFiles(SyncFileItem::Direction,bool*)), connect(_engine.data(), SIGNAL(aboutToRemoveAllFiles(SyncFileItem::Direction,bool*)),
SLOT(slotAboutToRemoveAllFiles(SyncFileItem::Direction,bool*))); SLOT(slotAboutToRemoveAllFiles(SyncFileItem::Direction,bool*)));
connect(_engine.data(), SIGNAL(folderDiscovered(bool,QString)), this, SLOT(slotFolderDiscovered(bool,QString)));
connect(_engine.data(), SIGNAL(transmissionProgress(Progress::Info)), this, SLOT(slotTransmissionProgress(Progress::Info))); connect(_engine.data(), SIGNAL(transmissionProgress(Progress::Info)), this, SLOT(slotTransmissionProgress(Progress::Info)));
connect(_engine.data(), SIGNAL(jobCompleted(SyncFileItem)), this, SLOT(slotJobCompleted(SyncFileItem))); connect(_engine.data(), SIGNAL(jobCompleted(SyncFileItem)), this, SLOT(slotJobCompleted(SyncFileItem)));
@ -688,6 +689,13 @@ void Folder::slotEmitFinishedDelayed()
} }
void Folder::slotFolderDiscovered(bool local, QString folderName)
{
Progress::Info pi;
pi._currentDiscoveredFolder = folderName;
ProgressDispatcher::instance()->setProgressInfo(alias(), pi);
}
// the progress comes without a folder and the valid path set. Add that here // the progress comes without a folder and the valid path set. Add that here
// and hand the result over to the progress dispatcher. // and hand the result over to the progress dispatcher.

View file

@ -157,6 +157,7 @@ private slots:
void slotCsyncUnavailable(); void slotCsyncUnavailable();
void slotSyncFinished(); void slotSyncFinished();
void slotFolderDiscovered(bool local, QString folderName);
void slotTransmissionProgress(const Progress::Info& pi); void slotTransmissionProgress(const Progress::Info& pi);
void slotJobCompleted(const SyncFileItem&); void slotJobCompleted(const SyncFileItem&);

View file

@ -246,7 +246,7 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
h += aliasMargin; h += aliasMargin;
// Sync File Progress Bar: Show it if syncFile is not empty. // Sync File Progress Bar: Show it if syncFile is not empty.
if( !overallString.isEmpty()) { if( !overallString.isEmpty() || !itemString.isEmpty()) {
int fileNameTextHeight = subFm.boundingRect(tr("File")).height(); int fileNameTextHeight = subFm.boundingRect(tr("File")).height();
int barHeight = qMax(fileNameTextHeight, aliasFm.height()+4); ; int barHeight = qMax(fileNameTextHeight, aliasFm.height()+4); ;
int overallWidth = option.rect.width()-2*aliasMargin; int overallWidth = option.rect.width()-2*aliasMargin;

View file

@ -444,13 +444,16 @@ void ownCloudGui::slotUpdateProgress(const QString &folder, const Progress::Info
{ {
Q_UNUSED(folder); Q_UNUSED(folder);
QString totalSizeStr = Utility::octetsToString( progress._totalSize ); if (!progress._currentDiscoveredFolder.isEmpty()) {
if(progress._totalSize == 0 ) { _actionStatus->setText( tr("Discovering %1")
.arg( progress._currentDiscoveredFolder ));
} else if (progress._totalSize == 0 ) {
quint64 currentFile = progress._completedFileCount + progress._currentItems.count(); quint64 currentFile = progress._completedFileCount + progress._currentItems.count();
_actionStatus->setText( tr("Syncing %1 of %2 (%3 left)") _actionStatus->setText( tr("Syncing %1 of %2 (%3 left)")
.arg( currentFile ).arg( progress._totalFileCount ) .arg( currentFile ).arg( progress._totalFileCount )
.arg( Utility::timeToDescriptiveString(progress.totalEstimate().getEtaEstimate(), 2, " ",true) ) ); .arg( Utility::timeToDescriptiveString(progress.totalEstimate().getEtaEstimate(), 2, " ",true) ) );
} else { } else {
QString totalSizeStr = Utility::octetsToString( progress._totalSize );
_actionStatus->setText( tr("Syncing %1 (%2 left)") _actionStatus->setText( tr("Syncing %1 (%2 left)")
.arg( totalSizeStr ) .arg( totalSizeStr )
.arg( Utility::timeToDescriptiveString(progress.totalEstimate().getEtaEstimate(), 2, " ",true) ) ); .arg( Utility::timeToDescriptiveString(progress.totalEstimate().getEtaEstimate(), 2, " ",true) ) );
@ -491,7 +494,8 @@ void ownCloudGui::slotUpdateProgress(const QString &folder, const Progress::Info
slotRebuildRecentMenus(); slotRebuildRecentMenus();
} }
if (progress._completedFileCount == progress._totalFileCount) { if (progress._completedFileCount == progress._totalFileCount
&& progress._currentDiscoveredFolder.isEmpty()) {
QTimer::singleShot(2000, this, SLOT(slotDisplayIdle())); QTimer::singleShot(2000, this, SLOT(slotDisplayIdle()));
} }
} }

View file

@ -108,9 +108,11 @@ ProgressDispatcher::~ProgressDispatcher()
void ProgressDispatcher::setProgressInfo(const QString& folder, const Progress::Info& progress) void ProgressDispatcher::setProgressInfo(const QString& folder, const Progress::Info& progress)
{ {
if( folder.isEmpty() || if( folder.isEmpty())
(progress._currentItems.size() == 0 // The update phase now also has progress
&& progress._totalFileCount == 0) ) { // (progress._currentItems.size() == 0
// && progress._totalFileCount == 0) )
{
return; return;
} }
emit progressInfo( folder, progress ); emit progressInfo( folder, progress );

View file

@ -39,6 +39,9 @@ namespace Progress
struct Info { struct Info {
Info() : _totalFileCount(0), _totalSize(0), _completedFileCount(0), _completedSize(0) {} Info() : _totalFileCount(0), _totalSize(0), _completedFileCount(0), _completedSize(0) {}
// Used during local and remote update phase
QString _currentDiscoveredFolder;
quint64 _totalFileCount; quint64 _totalFileCount;
quint64 _totalSize; quint64 _totalSize;
quint64 _completedFileCount; quint64 _completedFileCount;

View file

@ -41,6 +41,7 @@
#include <QUrl> #include <QUrl>
#include <QSslCertificate> #include <QSslCertificate>
#include <QProcess> #include <QProcess>
#include <QElapsedTimer>
namespace Mirall { namespace Mirall {
@ -452,6 +453,24 @@ void SyncEngine::handleSyncError(CSYNC *ctx, const char *state) {
finalize(); finalize();
} }
void update_job_update_callback (bool local,
const char *dirUrl,
void *userdata)
{
// Don't wanna overload the UI
static QElapsedTimer throttleTimer;
if (throttleTimer.elapsed() < 200) {
return;
}
throttleTimer.restart();
UpdateJob *updateJob = static_cast<Mirall::UpdateJob*>(userdata);
if (updateJob) {
QString path = QString::fromUtf8(dirUrl).section('/', -1);
emit updateJob->folderDiscovered(local, path);
}
}
void SyncEngine::startSync() void SyncEngine::startSync()
{ {
Q_ASSERT(!_syncRunning); Q_ASSERT(!_syncRunning);
@ -534,11 +553,16 @@ void SyncEngine::startSync()
UpdateJob *job = new UpdateJob(_csync_ctx); UpdateJob *job = new UpdateJob(_csync_ctx);
job->moveToThread(&_thread); job->moveToThread(&_thread);
connect(job, SIGNAL(finished(int)), this, SLOT(slotUpdateFinished(int))); connect(job, SIGNAL(finished(int)), this, SLOT(slotUpdateFinished(int)));
connect(job, SIGNAL(folderDiscovered(bool,QString)),
this, SIGNAL(folderDiscovered(bool,QString)));
QMetaObject::invokeMethod(job, "start", Qt::QueuedConnection); QMetaObject::invokeMethod(job, "start", Qt::QueuedConnection);
} }
void SyncEngine::slotUpdateFinished(int updateResult) void SyncEngine::slotUpdateFinished(int updateResult)
{ {
// To clean the progress info
emit folderDiscovered(false, QString());
if (updateResult < 0 ) { if (updateResult < 0 ) {
handleSyncError(_csync_ctx, "csync_update"); handleSyncError(_csync_ctx, "csync_update");
return; return;

View file

@ -27,6 +27,9 @@
#include <csync.h> #include <csync.h>
// when do we go away with this private/public separation?
#include <csync_private.h>
#include "mirall/syncfileitem.h" #include "mirall/syncfileitem.h"
#include "mirall/progressdispatcher.h" #include "mirall/progressdispatcher.h"
#include "mirall/utility.h" #include "mirall/utility.h"
@ -62,6 +65,9 @@ signals:
void csyncError( const QString& ); void csyncError( const QString& );
void csyncUnavailable(); void csyncUnavailable();
// During update, before reconcile
void folderDiscovered(bool local, QString folderUrl);
// before actual syncing (after update+reconcile) for each item // before actual syncing (after update+reconcile) for each item
void syncItemDiscovered(const SyncFileItem&); void syncItemDiscovered(const SyncFileItem&);
// after the above signals. with the items that actually need propagating // after the above signals. with the items that actually need propagating
@ -141,6 +147,9 @@ private:
QHash<QString, QByteArray> _remotePerms; QHash<QString, QByteArray> _remotePerms;
}; };
void update_job_update_callback (bool local,
const char *dirname,
void *userdata);
class UpdateJob : public QObject { class UpdateJob : public QObject {
Q_OBJECT Q_OBJECT
@ -152,6 +161,8 @@ class UpdateJob : public QObject {
csync_set_log_callback(_log_callback); csync_set_log_callback(_log_callback);
csync_set_log_level(_log_level); csync_set_log_level(_log_level);
csync_set_log_userdata(_log_userdata); csync_set_log_userdata(_log_userdata);
_csync_ctx->callbacks.update_callback = update_job_update_callback;
_csync_ctx->callbacks.update_callback_userdata = this;
emit finished(csync_update(_csync_ctx)); emit finished(csync_update(_csync_ctx));
deleteLater(); deleteLater();
} }
@ -165,6 +176,7 @@ public:
_log_userdata = csync_get_log_userdata(); _log_userdata = csync_get_log_userdata();
} }
signals: signals:
void folderDiscovered(bool local, QString folderUrl);
void finished(int result); void finished(int result);
}; };