/* * 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 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #endif #include "mirall/protocolwidget.h" #include "mirall/mirallconfigfile.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 "mirall/folder.h" #include "openfilemanager.h" #include "ui_protocolwidget.h" #include namespace Mirall { ProtocolWidget::ProtocolWidget(QWidget *parent) : QWidget(parent), IgnoredIndicatorRole( Qt::UserRole +1 ), _ui(new Ui::ProtocolWidget) { _ui->setupUi(this); connect(ProgressDispatcher::instance(), SIGNAL(progressInfo(QString,Progress::Info)), this, SLOT(slotProgressInfo(QString,Progress::Info))); connect(_ui->_treeWidget, SIGNAL(itemActivated(QTreeWidgetItem*,int)), SLOT(slotOpenFile(QTreeWidgetItem*,int))); // 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); _ui->_treeWidget->setTextElideMode(Qt::ElideMiddle); _ui->_treeWidget->header()->setObjectName("ActivityListHeader"); #if defined(Q_OS_MAC) _ui->_treeWidget->setMinimumWidth(400); #endif connect(this, SIGNAL(guiLog(QString,QString)), Logger::instance(), SIGNAL(guiLog(QString,QString))); _retrySyncBtn = _ui->_dialogButtonBox->addButton(tr("Retry Sync"), QDialogButtonBox::ActionRole); _retrySyncBtn->setEnabled(false); connect(_retrySyncBtn, SIGNAL(clicked()), SLOT(slotRetrySync())); _copyBtn = _ui->_dialogButtonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole); _copyBtn->setToolTip( tr("Copy the activity list to the clipboard.")); _copyBtn->setEnabled(false); connect(_copyBtn, SIGNAL(clicked()), SLOT(copyToClipboard())); MirallConfigFile cfg; cfg.restoreGeometryHeader(_ui->_treeWidget->header()); } ProtocolWidget::~ProtocolWidget() { MirallConfigFile cfg; cfg.saveGeometryHeader(_ui->_treeWidget->header() ); delete _ui; } void ProtocolWidget::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 status has been copied to the clipboard.")); } void ProtocolWidget::slotRetrySync() { FolderMan *folderMan = FolderMan::instance(); Folder::Map folders = folderMan->map(); foreach( Folder *f, folders ) { int num = f->slotWipeBlacklist(); qDebug() << num << "entries were removed from" << f->alias() << "blacklist"; num = f->slotDiscardDownloadProgress(); qDebug() << num << "temporary files with partial downloads" << "were removed from" << f->alias(); } folderMan->slotScheduleAllFolders(); } void ProtocolWidget::cleanIgnoreItems(const QString& folder) { int itemCnt = _ui->_treeWidget->topLevelItemCount(); // Limit the number of items while(itemCnt > 2000) { delete _ui->_treeWidget->takeTopLevelItem(itemCnt - 1); itemCnt--; } for( int cnt = itemCnt-1; cnt >=0 ; cnt-- ) { QTreeWidgetItem *item = _ui->_treeWidget->topLevelItem(cnt); bool isErrorItem = item->data(0, IgnoredIndicatorRole).toBool(); QString itemFolder = item->data(2, Qt::DisplayRole).toString(); if( isErrorItem && itemFolder == folder ) { delete item; } } } QString ProtocolWidget::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 ProtocolWidget::slotOpenFile( QTreeWidgetItem *item, int ) { QString folderName = item->text(2); QString fileName = item->text(1); Folder *folder = FolderMan::instance()->folder(folderName); if (folder) { // folder->path() always comes back with trailing path QString fullPath = folder->path() + fileName; if (QFile(fullPath).exists()) { showInFileManager(fullPath); } } } QString ProtocolWidget::fixupFilename( const QString& name ) { if( Utility::isMac() ) { QString n(name); return n.replace(QChar(':'), QChar('/')); } return name; } QTreeWidgetItem* ProtocolWidget::createCompletedTreewidgetItem(const QString& folder, const SyncFileItem& item) { QStringList columns; QDateTime timestamp = QDateTime::currentDateTime(); const QString timeStr = timeString(timestamp); const QString longTimeStr = timeString(timestamp, QLocale::LongFormat); QIcon icon; QString message; columns << timeStr; columns << fixupFilename(item._file); columns << folder; if (Progress::isWarningKind(item._status)) { message= item._errorString; columns << message; if (item._status == SyncFileItem::NormalError || item._status == SyncFileItem::FatalError) { icon = Theme::instance()->syncStateIcon(SyncResult::Error); } else { icon = Theme::instance()->syncStateIcon(SyncResult::Problem); } } else { // if the error string is set, it's prefered because it is a usefull user message. // at least should be... if(item._errorString.isEmpty()) { message = Progress::asResultString(item); } else { message = item._errorString; } columns << message; if (Progress::isSizeDependent(item._instruction)) { columns << Utility::octetsToString( item._size ); } } QTreeWidgetItem *twitem = new QTreeWidgetItem(columns); if (item._status == SyncFileItem::FileIgnored) { // Tell that we want to remove it on the next sync. twitem->setData(0, IgnoredIndicatorRole, true); } twitem->setIcon(0, icon); twitem->setToolTip(0, longTimeStr); twitem->setToolTip(1, item._file); twitem->setToolTip(3, message ); return twitem; } void ProtocolWidget::computeResyncButtonEnabled() { FolderMan *folderMan = FolderMan::instance(); Folder::Map folders = folderMan->map(); int blacklist_cnt = 0; int downloads_cnt = 0; foreach( Folder *f, folders ) { blacklist_cnt += f->blackListEntryCount(); downloads_cnt += f->downloadInfoCount(); } QString t = tr("Currently no files are ignored because of previous errors and no downloads are in progress."); bool enabled = blacklist_cnt > 0 || downloads_cnt > 0; if (enabled) { t = tr("%n files are ignored because of previous errors.\n", 0, blacklist_cnt) + tr("%n files are partially downloaded.\n", 0, downloads_cnt) + tr("Try to sync these again."); } _retrySyncBtn->setEnabled(enabled); _retrySyncBtn->setToolTip(t); } void ProtocolWidget::slotProgressInfo( const QString& folder, const Progress::Info& progress ) { if( progress._completedFileCount == ULLONG_MAX ) { // The sync is restarting, clean the old items cleanIgnoreItems(folder); computeResyncButtonEnabled(); } else if (progress._completedFileCount >= progress._totalFileCount) { //Sync completed computeResyncButtonEnabled(); } SyncFileItem last = progress._lastCompletedItem; if (last.isEmpty()) return; QTreeWidgetItem *item = createCompletedTreewidgetItem(folder, last); if(item) { _ui->_treeWidget->insertTopLevelItem(0, item); if (!_copyBtn->isEnabled()) { _copyBtn->setEnabled(true); } } } }