rsyncui/mainwindow.cpp

921 lines
30 KiB
C++

#include "mainwindow.h"
#include <QComboBox>
using namespace std;
bool display = false;
//extern QDialog Configuration;
extern Ui::Configuration config;
extern bool testRsyncReturn(QProcess *);
QMap<int, QString> rsyncErrorStrings {
{0, QTranslator::tr("Success. The rsync command completed successfully without any errors.")},
{1, QTranslator::tr("Syntax or usage error. There was a problem with the syntax of the rsync command or with the options specified.")},
{2, QTranslator::tr("Protocol incompatibility. There was a problem with the protocol version or negotiation between the rsync client and server.")},
{3, QTranslator::tr("Errors selecting input/output files, dirs. There was a problem with the source or destination file or directory specified in the rsync command.")},
{4, QTranslator::tr("Requested action not supported: An attempt was made to use an unsupported action or option.")},
{5, QTranslator::tr("Error starting client-server protocol. There was an error starting the client-server protocol.")},
{6, QTranslator::tr("Daemon unable to append to log-file. The rsync daemon was unable to write to its log file.")},
{10, QTranslator::tr("Error in socket I/O. There was an error with the socket input/output.")},
{11, QTranslator::tr("Error in file I/O. There was an error reading or writing to a file.")},
{12, QTranslator::tr("Error in rsync protocol data stream. There was an error in the rsync protocol data stream.")},
{13, QTranslator::tr("Errors with program diagnostics. There was an error generating program diagnostics.")},
{14, QTranslator::tr("Error in IPC code. There was an error in the inter-process communication (IPC) code.")},
{20, QTranslator::tr("Received SIGUSR1 or SIGINT. The rsync process was interrupted by a signal.")},
{21, QTranslator::tr("Some error returned by waitpid(). An error occurred while waiting for a child process to complete.")},
{22, QTranslator::tr("Error allocating core memory buffers. There was an error allocating memory buffers.")},
{23, QTranslator::tr("Partial transfer due to error. The rsync command completed with an error, but some files may have been transferred successfully.")},
{24, QTranslator::tr("Partial transfer due to vanished source files. Some source files disappeared before they could be transferred.")}
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
QAbstractButton * reply;
QMessageBox msgBox;
ui->setupUi(this);
QCoreApplication::setOrganizationName("RsyncUI");
QCoreApplication::setApplicationName("RsyncUI");
// context menu for treewidget (list of files)
ui->treeWidget->addAction(ui->actionDownload);
// init configuration window
config.setupUi(&Configuration);
// init of About
this->about.description = tr("Client for rsync server\n\nYou click on file to enqueue it, and RyncUI Download one file a time");
// connectors
connect(this, &MainWindow::progressSignal, ui->progressBar, &QProgressBar::setValue);
//connect(this, &MainWindow::errorSignal, this, &MainWindow::downloadingErrorSlot);
connect(this, &MainWindow::stopDownloading, this, &MainWindow::cancelled);
connect(config.buttonBox, SIGNAL(accepted()), this, SLOT(on_buttonBox_accepted()));
connect(config.comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &MainWindow::on_comboBox_currentIndexChanged);
// init of widgets
ui->ktreewidgetsearchline->setTreeWidget(ui->treeWidget); // attach search widget to treewidget
ui->ktreewidgetsearchline->setCaseSensitivity(Qt::CaseInsensitive); // and set it case insensitive
ui->treeWidget->setHeaderLabels({tr("Path"), tr("Type"), tr("Size")} ); // set header of columns of tree widget
// if last server exists in settings
if (this->settings.contains("connexion/lastServer"))
{
// set window to precedent server/port configuration
ui->portEdit->setText(this->settings.value("connexion/port").toString());
ui->khistorycombobox->setCurrentText(this->settings.value("connexion/lastServer").toString());
}else
{
ui->portEdit->text() = QString::number(this->connexion.port);
ui->khistorycombobox->clear();
}
// setting arrowcursor for treeWidget, listWidget and listDownload to arrow
ui->treeWidget->setCursor(Qt::ArrowCursor);
ui->listWidget->setCursor(Qt::ArrowCursor);
ui->listDownload->setCursor(Qt::ArrowCursor);
// Hiding progress bar
ui->progressBar->hide();
loadSettings();
//setting configuration window
config.comboBox->setCurrentIndex(ui->toolBar->toolButtonStyle()); // setting combobox to saved settings
//setting unit of bandwidth limit
config.UnitCombobox->addItems({tr("KB"), tr("MB"), tr("GB"), tr("TB"), tr("PB")});
//if exists list of donwloads in saved settings
if (this->settings.value("Downloads/rows").toInt() != 0)
{
// asking if we load the list and continue downloading
msgBox.setWindowTitle("RsyncUI");
msgBox.setInformativeText(tr("A list of interrupted downloads exists, do you want to continue downloading ? if not the list will be cleared" ));
QPushButton *yes = msgBox.addButton(QMessageBox::Yes);
msgBox.addButton(QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::Yes);
msgBox.exec();
reply = msgBox.clickedButton();
// if response is yes then loading list
if(reply == yes)
{
loadDownloadList();
}
}
// load list of services
populateList();
}
MainWindow::~MainWindow()
{
delete ui;
}
// Close window has been clicked
void MainWindow::closeEvent (QCloseEvent *event)
{
QMessageBox::StandardButton reply;
QMessageBox::StandardButtons param;
QString displayText;
// saving settings
saveSettings();
if (ui->listDownload->count() != 0) // some downloads waiting
{
// Asking for stopping or continuing
param = QMessageBox::Yes|QMessageBox::No;
if (config.autosaveCheckbox->checkState() != Qt::Checked)
{
param |= QMessageBox::Save;
displayText = tr("Clicking Save button, You can save the list of downloads\n");
}
reply = QMessageBox::question(
this,
"RsyncUI",
tr("Exiting will stop downloading, and will clear the download queue.\nDo you want to exit ?") + displayText,
param,
QMessageBox::No);
if (reply == QMessageBox::No)
{
// continuing
event->ignore();
return;
}else if(reply == QMessageBox::Yes)
{
// emission of signal to downloading thread and stopping
emit (stopDownloading(this->downloading.process));
}else
{
saveDownloadList();
}
}
event->accept();
}
// Populate treeview with list of files
void MainWindow::populateTree(QTreeWidgetItem * parent)
{
QString path;
// Clear treewidget
ui->treeWidget->clear();
if (!this->connexion.server.isEmpty() and this->connexion.port > 0 and this->connexion.port < 65536)
{
// setting cursor to "Wait"
QGuiApplication::setOverrideCursor(Qt::WaitCursor);
// validating server's address
if (validateServer(this->connexion.server))
{
// server is validated
path = ui->listWidget->currentItem()->text().section('\n', 0, 0) + "/";
scanDir(this->connexion.server, this->connexion.port, parent, path);
}
// Restoring cursor
QGuiApplication::restoreOverrideCursor();
}
}
// Populate Listview with list of services
void MainWindow::populateList()
{
QString server;
int port;
server = ui->khistorycombobox->currentText();
port = ui->portEdit->text().toUInt();
if ((server != this->connexion.server) or (port != this->connexion.port))
{
// clearing listwidget
ui->listWidget->clear();
this->connexion.server = server;
this->connexion.port = port;
// setting cursor to "Wait"
QGuiApplication::setOverrideCursor(Qt::WaitCursor);
// verify if server is in history
this->settings.beginGroup("connexion/server");
if (this->settings.contains(server) and this->connexion.comboboxChanged)
{
// server is in history => setting port value
port = this->settings.value(server).toUInt();
ui->portEdit->setText(QString::number(port));
this->connexion.port = port;
//display list of services
listServices();
}else
{
if (!server.isEmpty() and (port > 0 and port < 65536))
{
if (validateServer(server))
{
cout << server.toStdString() << endl;
// storing serverURL and port in settings
this->settings.setValue(server, port);
this->settings.sync();
this->downloading.server = server;
// storing in history of combobox
ui->khistorycombobox->addToHistory(server);
// load and display rsync services of the rsync server
listServices();
}
}
}
this->settings.endGroup();
QGuiApplication::restoreOverrideCursor(); //setting cursor to default
}
}
//list services of the rsync server
void MainWindow::listServices()
{
QString cmd;
QStringList param;
QString line;
QString errorRsync;
QStringList v;
QString service;
QProcess *myProcess;
bool flag = false;
cmd = "/usr/bin/rsync";
param << "--contimeout=10" << "--port=" + QString::number(this->connexion.port) << this->connexion.server + "::";
myProcess = new QProcess(this);
myProcess->start(cmd, param);
// waiting for response of the server with a timeout of 10 seconds
while(myProcess->waitForReadyRead(10000))
{
while(!flag)
{
line = QString::fromUtf8(myProcess->readLine());
// line empty then buffer is empty so returning to wait new datas
if (line.isEmpty())
{
flag = true;
break;
}
// extracting name and comment of the service
v = line.split("\t");
v[0].replace(" ", "");
v[1].replace("\n", "");
service = v[0] + "\n\t" + v[1];
// adding to list of services
ui->listWidget->addItem(service);
}
// buffer empty go to waiting new datas
flag =false;
}
// verifying error code
testRsyncReturn(myProcess);
}
// connect to rsync server to get list of files
void MainWindow::scanDir(QString server, int portN, QTreeWidgetItem *parent, QString path)
{
QString cmd;
QStringList param;
QString line;
QString size;
QString filename;
QTreeWidgetItem * item;
QProcess * myProcess;
bool isDir = false;
bool flag = false;
cmd = "rsync";
param << "--contimeout=10" << "--port=" + QString::number(portN) << server + "::" + path;
myProcess = new QProcess(this);
myProcess->start(cmd, param);
// waiting for response of the server with a timeout of 10 seconds
while(myProcess->waitForReadyRead(100000))
{
while (!flag)
{
line = QString::fromUtf8(myProcess->readLine());
// line empty then buffer is empty so returning to wait new datas
if (line.isEmpty())
{
flag = true;
break;
}
// extracting name, size and is dir/file
line = line.simplified();
size = line.section(" ", 1, 1);
filename = line.section(" ", 4);
if (filename != '.')
{
if (line[0] == "d")
{
isDir = true;
}else
{
isDir = false;
}
if (parent != NULL)
{
//adding item to tree
item = addTreeChild(parent, filename, size, isDir);
}else
{
//adding item to tree (as directory)
item = addTreeRoot(filename, size, isDir);
}
}
}
flag = false;
}
// buffer empty go to waiting new datas
testRsyncReturn(myProcess);
}
// Verify if server address is IP address
bool MainWindow::isIpAddress(QString server)
{
QStringList r;
int elementN;
bool ok;
r = server.split('.');
if (r.size() == 4)
{
for (auto element : r)
{
elementN = element.toInt(&ok);
if (elementN < 0 or elementN > 255 or ok == false)
{
return false;
}
}
return true;
}else
{
return false;
}
}
// validate address server
bool MainWindow::validateServer(QString server)
{
QString cmd;
QStringList param;
QString line;
QProcess * myProcess;
bool flag = false;
bool bflag = false;
cmd = "dig";
param << server;
myProcess = new QProcess(this);
myProcess->start(cmd, param);
// maiking a dig on the server's address
while(myProcess->waitForReadyRead())
{
while (!bflag)
{
line = QString::fromUtf8(myProcess->readAllStandardOutput());
// line empty then buffer is empty so returning to wait new datas
if (line.isEmpty())
{
bflag = true;
break;
}else if (line.indexOf(";; ANSWER SECTION:") != -1)
{
flag = true;
}
}
bflag = false;
}
//testRsyncReturn(myProcess);
if ( flag == false)
{
//server's address is not valid testing if ip address is valid
flag = isIpAddress(server);
}
if ( flag == false)
{
// server-s address not valid
QMessageBox::warning(
this,
"RsyncUI",
tr("server does not exists" )
);
}
return flag;
}
// slot activated when combobox is changed
void MainWindow::on_khistorycombobox_currentIndexChanged(int i)
{
this->connexion.comboboxChanged = true;
populateList();
}
// slot activated when button connection is clicked
void MainWindow::on_connectButton_clicked()
{
populateList();
}
// add a dir in treeview
QTreeWidgetItem * MainWindow::addTreeRoot(QString name, QString fileSize, bool isDir)
{
QTreeWidgetItem *treeItem = new QTreeWidgetItem(ui->treeWidget);
if (isDir == true)
{
// item is a dir
treeItem->setText(1, tr("Dir"));
}else
{
// item is a file
treeItem->setText(1,tr("File"));
}
treeItem->setText(0, name);
treeItem->setText(2, fileSize);
return treeItem;
}
// add a file in treeview
QTreeWidgetItem * MainWindow::addTreeChild(QTreeWidgetItem *parent, QString name, QString fileSize, bool isDir)
{
QTreeWidgetItem *treeItem = new QTreeWidgetItem();
if (isDir == true)
{
// item is a dir
treeItem->setText(1, tr("Dir"));
}else
{
// item is a file
treeItem->setText(1,("File"));
}
treeItem->setText(0, name);
treeItem->setText(2, fileSize);
// QTreeWidgetItem::addChild(QTreeWidgetItem * child)
parent->addChild(treeItem);
return treeItem;
}
// Slot acivated when a service in the list is clicked
void MainWindow::on_listWidget_clicked()
{
QString str;
this->connexion.service = ui->listWidget->currentItem()->text().section("\n", 0 ,0);
str = "Folder/" + this->connexion.server + "/" + this->connexion.service;
// if service exists in settings for this server
if (this->settings.contains(str))
{
// setting savePath from settings
this->downloading.savePath = this->settings.value(str).toString();
}
populateTree(NULL);
}
//Slot activated when a file is clicked in the treeview
void MainWindow::on_treeWidget_itemClicked(QTreeWidgetItem *item, bool downloadDir)
{
QFuture<void> future;
QFileDialog dialog;
QTreeWidgetItem * itemR;
QString path;
QString str;
itemR = item;
// assembling path from treewidget
path = item->text(0);
while(itemR->parent() != NULL)
{
itemR = itemR->parent();
// concatening parent to path
path.prepend(itemR->text(0) + "/");
};
if (item->text(1) == tr("File") or downloadDir == true)
{
// Item is a file
if(ui->listDownload->findItems(path, Qt::MatchStartsWith).empty())
{
// exists saving path in settings ?
str = "Folder/" + this->connexion.server + "/" + this->downloading.service;
if(!this->settings.contains(str))
{
// saving path do not exists, asking for it
if(!on_DefaultSaveFolder_triggered())
{
cout << "no directory selectioned, ignoring download request";
return;
}
}
// is there a downloading process ?
if (this->downloading.process == nullptr)
{
// no downloading process launching it
this->downloading.path = path;
this->downloading.server = this->connexion.server;
this->downloading.service = this->connexion.service;
startDownloading();
// wit 1 second to process start
//sleep(1);
}
// Adding download in download list
str = path + " => " + this->connexion.server + "/" + this->connexion.service;
ui->listDownload->addItem(str);
}
}else
{
//Item is a Directory
scanDir(this->connexion.server, this->connexion.port, item, this->connexion.service + "/" + path +"/");
item->setExpanded(true);
}
if (config.autosaveCheckbox->checkState() == Qt::Checked)
{
saveDownloadList();
}
}
// Launch the thread which download the file
void MainWindow::startDownloading()
{
ui->progressBar->setValue(0);
ui->progressBar->show();
//QtConcurrent::run(&this->downloadO, &downloadFile::download, this);
download();
}
// Slot stopping download
void MainWindow::stoppingDownload()
{
emit (stopDownloading(this->downloading.process));
}
// when download is finished, launch download of next file in queue
void MainWindow::downloadFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
QString path;
QString str;
int pos;
// test if process crashed
if (exitStatus == QProcess::CrashExit)
{
QMessageBox::warning(
NULL,
"RsyncUI",
tr("Rsync process crashed"));
}
//test result code of command (if 20 then command stopped by user)
if (exitCode != 0 and exitCode != 20)
{
// displaying warning with exit code
QMessageBox::warning(
NULL,
"RsyncUI",
rsyncErrorStrings[exitCode]);
}
// disconnecting signals to slots
disconnect(this->downloading.process, 0, 0, 0);
// reset variables and window
this->downloading.process= nullptr;
ui->progressBar->hide();
delete ui->listDownload->takeItem(0);
this->downloading.clear();
// Some downloads staying in queue
if (ui->listDownload->count() != 0)
{
// autosave is activated
if (config.autosaveCheckbox->checkState() == Qt::Checked)
{
// saving download list
saveDownloadList();
}
// initializing download
path = ui->listDownload->item(0)->text();
pos = path.lastIndexOf("/");
this->downloading.service = path.midRef(pos+1).toString();
path.resize(pos);
pos = path.lastIndexOf(" => ");
this->downloading.server = path.midRef(pos+4).toString();
path.resize(pos);
this->downloading.path = path;
// save path exists in settings ?
str = "Folder/" + this->downloading.server + "/" + this->downloading.service;
if (this->settings.contains(str))
{
// setting savepath from saved settings
this->downloading.savePath = this->settings.value(str).toString();
startDownloading();
}else
{
// no save path
if(!on_DefaultSaveFolder_triggered())
{
cout << "Error no save path so deleting download";
//downloadFinished();
return;
}
}
}
}
// Slot activated when a line is clicked in queue list
void MainWindow::on_listDownload_itemClicked(QListWidgetItem *item)
{
QMessageBox::StandardButton reply;
if (item->listWidget()->row(item) == 0)
{
// first line clicked on download list
reply = QMessageBox::question(
this,
"RsyncUI",
tr("Do you want to stop downloading and delete this file from download queue ?"),
QMessageBox::Yes|QMessageBox::No,
QMessageBox::No);
if (reply == QMessageBox::Yes)
{
// stopping download
emit (stopDownloading(this->downloading.process));
}
}else
{
// not first line on download list
reply = QMessageBox::question(
this,
"RsyncUI",
tr("Do you want to delete this file from download queue ?"),
QMessageBox::Yes|QMessageBox::No,
QMessageBox::No);
if (reply == QMessageBox::Yes)
{
// removing line from download list
ui->listDownload->removeItemWidget(item);
delete item;
if (config.autosaveCheckbox->checkState() == Qt::Checked)
{
// autosave acivated,so saving download list
saveDownloadList();
}
}
}
}
// load settings
void MainWindow::loadSettings()
{
// restoring geometry and state of window and widgets
this->restoreGeometry(this->settings.value("window/geometry").toByteArray());
this->restoreState(this->settings.value("window/state").toByteArray());
ui->treeWidget->header()->restoreState(this->settings.value("treeWidget/state").toByteArray());
ui->splitter->restoreState(this->settings.value("splitter/state").toByteArray());
ui->splitter_2->restoreState(this->settings.value("splitter2/state").toByteArray());
ui->toolBar->setToolButtonStyle((Qt::ToolButtonStyle)this->settings.value("toolbar/state").toInt());
if (this->settings.value("Autosave").toInt() == Qt::Checked)
{
this->config.autosaveCheckbox->setChecked(true);
}
// loading connexion settings
// loading servers history
this->settings.beginGroup("connexion/server");
QStringList servers = this->settings.allKeys();
this->settings.endGroup();
for( const QString &server : servers )
{
ui->khistorycombobox->addToHistory(server);
}
// loading save path
this->downloading.savePath = this->settings.value("Folder").toString();
// loading bandwidth limit
this->connexion.bandwidthLimit = this->settings.value("bandwidthlimit").toUInt();
this->connexion.bandwidthLimitUnit = this->settings.value("bandwidthlimitunit").toInt();
}
// save settings
void MainWindow::saveSettings()
{
this->settings.setValue("window/geometry", saveGeometry());
this->settings.setValue("window/state", saveState());
this->settings.setValue("treeWidget/state", ui->treeWidget->header()->saveState());
this->settings.setValue("splitter/state", ui->splitter->saveState());
this->settings.setValue("splitter2/state", ui->splitter_2->saveState());
this->settings.setValue("connexion/lastServer", this->connexion.server);
this->settings.setValue("connexion/lastPort", QString::number(this->connexion.port));
this->settings.setValue("toolbar/state", ui->toolBar->toolButtonStyle());
this->settings.setValue("Autosave", this->config.autosaveCheckbox->checkState());
this->settings.sync();
}
// About
void MainWindow::on_actionAbout_triggered()
{
QString text = this->about.description + "\n\n" +
tr("Version") + ": " + this->about.version + "\n" +
tr("Licence") + ": " + this->about.licence + "\n" +
tr("Author") + ": " + this->about.author + "\n" +
tr("EMail") + ": " + this->about.email + "\n" +
tr("Source code") + ": " + this->about.git;
QMessageBox::about(this, this->about.title, text);
}
// About QT
void MainWindow::on_actionAbout_Qt_triggered()
{
QMessageBox::aboutQt(this);
}
// Activated when menu "change folder" is clicked
bool MainWindow::on_DefaultSaveFolder_triggered()
{
QFileDialog dialog;
QString folder;
QString path;
// if service not selected display a message
if (this->connexion.service.isEmpty())
{
QMessageBox::warning(
NULL,
"RsyncUI",
tr("Since the save path is linked to service, you need to select a service before you can select a folder"));
return false;
}
// Asking for directory to save files
path = dialog.getExistingDirectory(this, tr("Choose folder where to save file"), QDir::homePath(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
if (!path.isEmpty())
{
this->downloading.savePath = path;
if (!this->connexion.service.isEmpty() and !this->connexion.server.isEmpty())
{
// saving save path in settings
folder = "Folder/" + this->connexion.server + "/" + this->connexion.service;
this->settings.setValue(folder, this->downloading.savePath);
this->settings.sync();
}
}else
{
return false;
}
return true;
}
// Activated when menu "settings" is clicked
void MainWindow::on_action_Settings_triggered()
{
config.UnitCombobox->setCurrentIndex(this->connexion.bandwidthLimitUnit);
config.spinBox->setValue(this->connexion.bandwidthLimit);
Configuration.show();
}
// Acivated when "Ok" is clicked in Configuration window
void MainWindow::on_buttonBox_accepted()
{
QString unit;
QString bw;
bw = config.spinBox->text();
if (bw.toInt() == 0)
{
// bandwidth = 0
this->connexion.bandwidthLimit = 0;
this->connexion.bandwidthLimitUnit = 0;
}else
{
this->connexion.bandwidthLimit = config.spinBox->value();
this->connexion.bandwidthLimitUnit = config.UnitCombobox->currentIndex();
}
this->settings.setValue("bandwidthlimit", this->connexion.bandwidthLimit);
this->settings.setValue("bandwidthlimitunit", this->connexion.bandwidthLimitUnit);
this->settings.setValue("Autosave", this->config.autosaveCheckbox->checkState());
this->settings.sync();
Configuration.hide();
}
// Saving download list
void MainWindow::saveDownloadList()
{
int nRows;
// remove list of downloads
this->settings.remove("Downloads/");
// Saving list of current downloads
nRows = ui->listDownload->count();
this->settings.beginGroup("Downloads");
this->settings.setValue("rows", nRows);
for (int i = 0; i < nRows; i++)
{
this->settings.setValue(QString::number(i), ui->listDownload->item(i)->text());
}
this->settings.endGroup();
this->settings.sync();
}
// Loading download list
void MainWindow::loadDownloadList()
{
QString path;
QString str;
int pos;
this->settings.sync();
this->settings.beginGroup("Downloads");
int size = this->settings.value("rows").toInt();
for (int i = 0; i < size; ++i)
{
ui->listDownload->addItem(this->settings.value(QString::number(i)).toString());
}
this->settings.endGroup();
path = ui->listDownload->item(0)->text();
pos = path.lastIndexOf("/");
this->downloading.service = path.midRef(pos+1).toString();
path.resize(pos);
pos = path.lastIndexOf(" => ");
this->downloading.server = path.midRef(pos+4).toString();
path.resize(pos);
this->downloading.path = path;
str = "Folder/" + this->downloading.server + "/" + this->downloading.service;
if (this->settings.contains(str))
{
this->downloading.savePath = this->settings.value(str).toString();
}
startDownloading();
}
// clear object downloading
void Downloading::clear()
{
this->path.clear();
this->server.clear();
this->savePath.clear();
this->service.clear();
}
// Context menu of file list clicked
void MainWindow::on_actionDownload_triggered()
{
// action made in qt-designer and added in init function.
QTreeWidgetItem *item;
item = ui->treeWidget->currentItem();
on_treeWidget_itemClicked(item, true);
}
/*void MainWindow::downloadingErrorSlot(QString errorString)
{
QMessageBox::warning(
this,
"RsyncUI",
errorString,
QMessageBox::Ok,
QMessageBox::Ok);
}
*/
// Chnage toolbar style
void MainWindow::on_comboBox_currentIndexChanged(int index)
{
ui->toolBar->setToolButtonStyle((Qt::ToolButtonStyle)index);
}