/* * Copyright (C) by Klaas Freitag * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include #include "mirall/itemprogressdialog.h" #include "mirall/syncresult.h" #include "mirall/logger.h" #include "mirall/utility.h" #include "mirall/theme.h" #include "mirall/folderman.h" #include "mirall/syncfileitem.h" #include "ui_itemprogressdialog.h" namespace Mirall { ItemProgressDialog::ItemProgressDialog(Application*, QWidget *parent) : QDialog(parent), ErrorIndicatorRole( Qt::UserRole +1 ), _ui(new Ui::ItemProgressDialog) { _ui->setupUi(this); connect(_ui->_dialogButtonBox->button(QDialogButtonBox::Close), SIGNAL(clicked()), this, SLOT(accept())); connect(ProgressDispatcher::instance(), SIGNAL(progressInfo(QString,Progress::Info)), this, SLOT(slotProgressInfo(QString,Progress::Info))); connect(ProgressDispatcher::instance(), SIGNAL(progressSyncProblem(const QString&,const Progress::SyncProblem&)), this, SLOT(slotProgressErrors(const QString&, const Progress::SyncProblem&))); // Adjust copyToClipboard() when making changes here! QStringList header; header << tr("Time"); header << tr("File"); header << tr("Folder"); header << tr("Action"); header << tr("Size"); _ui->_treeWidget->setHeaderLabels( header ); _ui->_treeWidget->setColumnWidth(1, 180); _ui->_treeWidget->setColumnCount(5); _ui->_treeWidget->setRootIsDecorated(false); connect(this, SIGNAL(guiLog(QString,QString)), Logger::instance(), SIGNAL(guiLog(QString,QString))); QPushButton *copyBtn = _ui->_dialogButtonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole); connect(copyBtn, SIGNAL(clicked()), SLOT(copyToClipboard())); setWindowTitle(tr("Sync Protocol")); } void ItemProgressDialog::setSyncResultStatus(const SyncResult& result ) { if( result.errorStrings().count() ) { _ui->_errorLabel->setVisible(true); _ui->_errorLabel->setTextFormat(Qt::RichText); QString errStr; QStringList errors = result.errorStrings(); int cnt = errors.size(); bool appendDots = false; if( cnt > 3 ) { cnt = 3; appendDots = true; } for( int i = 0; i < cnt; i++) { errStr.append(QString("%1
").arg(errors.at(i))); } if( appendDots ) { errStr.append(QString("...")); } _ui->_errorLabel->setText(errStr); } else { _ui->_errorLabel->setText(QString::null); _ui->_errorLabel->setVisible(false); } } void ItemProgressDialog::setSyncResult( const SyncResult& result ) { setSyncResultStatus(result); const QString& folder = result.folder(); qDebug() << "Setting sync result for folder " << folder; SyncFileItemVector::const_iterator i; const SyncFileItemVector& items = result.syncFileItemVector(); QDateTime dt = QDateTime::currentDateTime(); for (i = items.begin(); i != items.end(); ++i) { const SyncFileItem& item = *i; QString errMsg; QString tooltip; // handle ignored files here. if( item._instruction == CSYNC_INSTRUCTION_IGNORE || item._instruction == CSYNC_INSTRUCTION_CONFLICT ) { QStringList columns; QString timeStr = timeString(dt); QString longTimeStr = timeString(dt, QLocale::LongFormat); columns << timeStr; columns << item._file; columns << folder; if( item._instruction == CSYNC_INSTRUCTION_IGNORE) { if( item._type == SyncFileItem::SoftLink ) { errMsg = tr("Soft Link ignored"); tooltip = tr("Softlinks break the semantics of synchronization.\nPlease do not " "use them in synced directories"); } else { QString obj = tr("file"); if( item._type == SyncFileItem::Directory ) { obj = tr("directory"); } tooltip = tr("The %1 was ignored because it is listed in the clients ignore list\n" "or the %1 name contains characters that are not syncable\nin a cross platform " "environment").arg(obj); errMsg = tr("Item ignored"); if( item._errorString == QLatin1String("File listed on ignore list.") ) { errMsg = tr("%1 on ignore list").arg(obj); tooltip = tr("The %1 was skipped because it is listed on the clients\n" "list of names to ignore").arg(obj); } else if( item._errorString == QLatin1String("File contains invalid characters.") ) { errMsg = tr("Invalid characters"); tooltip = tr("The %1 name contains one or more invalid characters which break\n" "syncing in a cross platform environment").arg(obj); } } } else if( item._instruction == CSYNC_INSTRUCTION_CONFLICT ) { errMsg = tr("Conflict file."); tooltip = tr("The file was changed on server and local repository and as a result it\n" "created a so called conflict. The local change is copied to the conflict\n" "file while the file from the server side is available under the original\n" "name"); } else { Q_ASSERT(!"unhandled instruction."); } columns << errMsg; QTreeWidgetItem *twitem = new QTreeWidgetItem(columns); twitem->setData(0, ErrorIndicatorRole, QVariant(true) ); twitem->setToolTip(0, longTimeStr); twitem->setToolTip(3, tooltip); twitem->setIcon(0, Theme::instance()->syncStateIcon(SyncResult::Problem, true)); _ui->_treeWidget->insertTopLevelItem(0, twitem); } } } void ItemProgressDialog::setupList() { // get the folders to set up the top level list. Folder::Map map = FolderMan::instance()->map(); SyncResult lastResult; QDateTime dt; bool haveSyncResult = false; foreach( Folder *f, map.values() ) { if( f->syncResult().syncTime() > dt ) { dt = f->syncResult().syncTime(); lastResult = f->syncResult(); haveSyncResult = true; } } if( haveSyncResult ) { setSyncResult(lastResult); } QList progressList = ProgressDispatcher::instance()->recentChangedItems(0); // All. QHash folderHash; foreach( Progress::Info info, progressList ) { slotProgressInfo( info.folder, info ); folderHash[info.folder] = 1; } QList problemList = ProgressDispatcher::instance()->recentProblems(0); foreach( Progress::SyncProblem prob, problemList ) { slotProgressErrors(prob.folder, prob); folderHash[prob.folder] = 1; } } ItemProgressDialog::~ItemProgressDialog() { delete _ui; } void ItemProgressDialog::copyToClipboard() { QString text; QTextStream ts(&text); int topLevelItems = _ui->_treeWidget->topLevelItemCount(); for (int i = 0; i < topLevelItems; i++) { QTreeWidgetItem *child = _ui->_treeWidget->topLevelItem(i); ts << left // time stamp << qSetFieldWidth(10) << child->data(0,Qt::DisplayRole).toString() // file name << qSetFieldWidth(64) << child->data(1,Qt::DisplayRole).toString() // folder << qSetFieldWidth(15) << child->data(2, Qt::DisplayRole).toString() // action << qSetFieldWidth(15) << child->data(3, Qt::DisplayRole).toString() // size << qSetFieldWidth(10) << child->data(4, Qt::DisplayRole).toString() << qSetFieldWidth(0) << endl; } QApplication::clipboard()->setText(text); emit guiLog(tr("Copied to clipboard"), tr("The sync protocol has been copied to the clipboard.")); } void ItemProgressDialog::accept() { QDialog::accept(); } void ItemProgressDialog::cleanErrors( const QString& /* folder */ ) // FIXME: Use the folder to detect which errors can be deleted. { _problemCounter = 0; QList wipeList; int itemCnt = _ui->_treeWidget->topLevelItemCount(); for( int cnt = 0; cnt < itemCnt; cnt++ ) { QTreeWidgetItem *item = _ui->_treeWidget->topLevelItem(cnt); bool isErrorItem = item->data(0, ErrorIndicatorRole).toBool(); if( isErrorItem ) { wipeList.append(item); } } qDeleteAll(wipeList.begin(), wipeList.end()); } QString ItemProgressDialog::timeString(QDateTime dt, QLocale::FormatType format) const { QLocale loc = QLocale::system(); QString timeStr; QDate today = QDate::currentDate(); if( format == QLocale::NarrowFormat ) { if( dt.date().day() == today.day() ) { timeStr = loc.toString(dt.time(), QLocale::NarrowFormat); } else { timeStr = loc.toString(dt, QLocale::NarrowFormat); } } else { timeStr = loc.toString(dt, format); } return timeStr; } void ItemProgressDialog::slotProgressErrors( const QString& folder, const Progress::SyncProblem& problem ) { QStringList columns; QString timeStr = timeString(problem.timestamp); QString longTimeStr = timeString(problem.timestamp, QLocale::LongFormat); columns << timeStr; columns << problem.current_file; columns << folder; QString errMsg = tr("Problem: %1").arg(problem.error_message); #if 0 if( problem.error_code == 507 ) { errMsg = tr("No more storage space available on server."); } #endif columns << errMsg; QTreeWidgetItem *item = new QTreeWidgetItem(columns); item->setData(0, ErrorIndicatorRole, QVariant(true) ); // Maybe we should not set the error icon for all problems but distinguish // by error_code. A quota problem is considered an error, others might not?? item->setIcon(0, Theme::instance()->syncStateIcon(SyncResult::Error, true)); item->setToolTip(0, longTimeStr); _ui->_treeWidget->insertTopLevelItem(0, item); Q_UNUSED(item); } void ItemProgressDialog::slotProgressInfo( const QString& folder, const Progress::Info& progress ) { if( progress.kind == Progress::StartSync ) { cleanErrors( folder ); } if( progress.kind == Progress::EndSync ) { // decorateFolderItem( folder ); } // Ingore other events than finishing an individual up- or download. if( !(progress.kind == Progress::EndDownload || progress.kind == Progress::EndUpload || progress.kind == Progress::EndDelete)) { return; } QStringList columns; QString timeStr = timeString(progress.timestamp); QString longTimeStr = timeString(progress.timestamp, QLocale::LongFormat); columns << timeStr; columns << progress.current_file; columns << progress.folder; columns << Progress::asResultString(progress.kind); columns << Utility::octetsToString( progress.file_size ); QTreeWidgetItem *item = new QTreeWidgetItem(columns); item->setToolTip(0, longTimeStr); _ui->_treeWidget->insertTopLevelItem(0, item); Q_UNUSED(item); } }