mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-28 05:00:20 +00:00
b22f1da59e
Subsurface creates a lot of singleton instances on demand, but nothing ever deleted them. Since they are singletons, these memory allocations are technically not leaks. However, they clutter the output in valgrind and other memory analysers, hiding the real issues. The solution is to delete these items at exit. For the models and for gettextFromC, the solution is to use a QScopedPointer, which will delete its payload when it gets destroyed. For the dialogs and other widgets, we can't do that: they need to be deleted before QApplication exits, so we just set the parent in all of them to the main window. Signed-off-by: Thiago Macieira <thiago@macieira.org> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
333 lines
8.5 KiB
C++
333 lines
8.5 KiB
C++
#include "downloadfromdivecomputer.h"
|
|
|
|
#include "../libdivecomputer.h"
|
|
#include "../helpers.h"
|
|
#include "../display.h"
|
|
#include "../divelist.h"
|
|
#include "mainwindow.h"
|
|
#include <cstdlib>
|
|
#include <QThread>
|
|
#include <QDebug>
|
|
#include <QStringListModel>
|
|
#include <QTimer>
|
|
#include <QMessageBox>
|
|
|
|
struct product {
|
|
const char *product;
|
|
dc_descriptor_t *descriptor;
|
|
struct product *next;
|
|
};
|
|
|
|
struct vendor {
|
|
const char *vendor;
|
|
struct product *productlist;
|
|
struct vendor *next;
|
|
};
|
|
|
|
struct mydescriptor {
|
|
const char *vendor;
|
|
const char *product;
|
|
dc_family_t type;
|
|
unsigned int model;
|
|
};
|
|
|
|
namespace DownloadFromDcGlobal{
|
|
const char *err_string;
|
|
};
|
|
|
|
DownloadFromDCWidget *DownloadFromDCWidget::instance()
|
|
{
|
|
static DownloadFromDCWidget *dialog = new DownloadFromDCWidget(mainWindow());
|
|
dialog->setAttribute(Qt::WA_QuitOnClose, false);
|
|
return dialog;
|
|
}
|
|
|
|
DownloadFromDCWidget::DownloadFromDCWidget(QWidget* parent, Qt::WindowFlags f) :
|
|
QDialog(parent, f), thread(0), timer(new QTimer(this)),
|
|
currentState(INITIAL)
|
|
{
|
|
ui.setupUi(this);
|
|
ui.progressBar->hide();
|
|
ui.progressBar->setMinimum(0);
|
|
ui.progressBar->setMaximum(100);
|
|
|
|
fill_device_list();
|
|
fill_computer_list();
|
|
|
|
vendorModel = new QStringListModel(vendorList);
|
|
ui.vendor->setModel(vendorModel);
|
|
if (default_dive_computer_vendor) {
|
|
ui.vendor->setCurrentIndex(ui.vendor->findText(default_dive_computer_vendor));
|
|
productModel = new QStringListModel(productList[default_dive_computer_vendor]);
|
|
ui.product->setModel(productModel);
|
|
if (default_dive_computer_product)
|
|
ui.product->setCurrentIndex(ui.product->findText(default_dive_computer_product));
|
|
}
|
|
if (default_dive_computer_device)
|
|
ui.device->setEditText(default_dive_computer_device);
|
|
|
|
timer->setInterval(200);
|
|
connect(timer, SIGNAL(timeout()), this, SLOT(updateProgressBar()));
|
|
updateState(INITIAL);
|
|
}
|
|
|
|
void DownloadFromDCWidget::runDialog()
|
|
{
|
|
updateState(INITIAL);
|
|
exec();
|
|
}
|
|
|
|
void DownloadFromDCWidget::updateProgressBar()
|
|
{
|
|
ui.progressBar->setValue(progress_bar_fraction *100);
|
|
}
|
|
|
|
void DownloadFromDCWidget::updateState(states state)
|
|
{
|
|
if (state == currentState)
|
|
return;
|
|
|
|
if (state == INITIAL) {
|
|
fill_device_list();
|
|
ui.progressBar->hide();
|
|
markChildrenAsEnabled();
|
|
timer->stop();
|
|
}
|
|
|
|
// tries to cancel an on going download
|
|
else if (currentState == DOWNLOADING && state == CANCELLING) {
|
|
import_thread_cancelled = true;
|
|
ui.cancel->setEnabled(false);
|
|
}
|
|
|
|
// user pressed cancel but the application isn't doing anything.
|
|
// means close the window
|
|
else if ((currentState == INITIAL || currentState == CANCELLED || currentState == DONE || currentState == ERROR)
|
|
&& state == CANCELLING) {
|
|
timer->stop();
|
|
reject();
|
|
ui.ok->setText(tr("OK"));
|
|
}
|
|
|
|
// the cancelation process is finished
|
|
else if (currentState == CANCELLING && (state == DONE || state == CANCELLED)) {
|
|
timer->stop();
|
|
state = CANCELLED;
|
|
ui.progressBar->setValue(0);
|
|
ui.progressBar->hide();
|
|
markChildrenAsEnabled();
|
|
}
|
|
|
|
// DOWNLOAD is finally done, close the dialog and go back to the main window
|
|
else if (currentState == DOWNLOADING && state == DONE) {
|
|
timer->stop();
|
|
ui.progressBar->setValue(100);
|
|
markChildrenAsEnabled();
|
|
ui.ok->setText(tr("OK"));
|
|
accept();
|
|
}
|
|
|
|
// DOWNLOAD is started.
|
|
else if (state == DOWNLOADING) {
|
|
timer->start();
|
|
ui.progressBar->setValue(0);
|
|
ui.progressBar->show();
|
|
markChildrenAsDisabled();
|
|
}
|
|
|
|
// got an error
|
|
else if (state == ERROR) {
|
|
QMessageBox::critical(this, TITLE_OR_TEXT(tr("Error"), this->thread->error), QMessageBox::Ok);
|
|
|
|
markChildrenAsEnabled();
|
|
ui.progressBar->hide();
|
|
ui.ok->setText(tr("Retry"));
|
|
}
|
|
|
|
// properly updating the widget state
|
|
currentState = state;
|
|
}
|
|
|
|
void DownloadFromDCWidget::on_vendor_currentIndexChanged(const QString& vendor)
|
|
{
|
|
QAbstractItemModel *currentModel = ui.product->model();
|
|
if (!currentModel)
|
|
return;
|
|
|
|
productModel = new QStringListModel(productList[vendor]);
|
|
ui.product->setModel(productModel);
|
|
|
|
// Memleak - but deleting gives me a crash.
|
|
//currentModel->deleteLater();
|
|
}
|
|
|
|
void DownloadFromDCWidget::fill_computer_list()
|
|
{
|
|
dc_iterator_t *iterator = NULL;
|
|
dc_descriptor_t *descriptor = NULL;
|
|
struct mydescriptor *mydescriptor;
|
|
|
|
QStringList computer;
|
|
dc_descriptor_iterator(&iterator);
|
|
while (dc_iterator_next (iterator, &descriptor) == DC_STATUS_SUCCESS) {
|
|
const char *vendor = dc_descriptor_get_vendor(descriptor);
|
|
const char *product = dc_descriptor_get_product(descriptor);
|
|
|
|
if (!vendorList.contains(vendor))
|
|
vendorList.append(vendor);
|
|
|
|
if (!productList[vendor].contains(product))
|
|
productList[vendor].push_back(product);
|
|
|
|
descriptorLookup[QString(vendor) + QString(product)] = descriptor;
|
|
}
|
|
dc_iterator_free(iterator);
|
|
|
|
/* and add the Uemis Zurich which we are handling internally
|
|
THIS IS A HACK as we magically have a data structure here that
|
|
happens to match a data structure that is internal to libdivecomputer;
|
|
this WILL BREAK if libdivecomputer changes the dc_descriptor struct...
|
|
eventually the UEMIS code needs to move into libdivecomputer, I guess */
|
|
|
|
mydescriptor = (struct mydescriptor*) malloc(sizeof(struct mydescriptor));
|
|
mydescriptor->vendor = "Uemis";
|
|
mydescriptor->product = "Zurich";
|
|
mydescriptor->type = DC_FAMILY_NULL;
|
|
mydescriptor->model = 0;
|
|
|
|
if (!vendorList.contains("Uemis"))
|
|
vendorList.append("Uemis");
|
|
|
|
if (!productList["Uemis"].contains("Zurich"))
|
|
productList["Uemis"].push_back("Zurich");
|
|
|
|
descriptorLookup[QString("UemisZurich")] = (dc_descriptor_t *)mydescriptor;
|
|
}
|
|
|
|
void DownloadFromDCWidget::on_cancel_clicked()
|
|
{
|
|
updateState(CANCELLING);
|
|
}
|
|
|
|
void DownloadFromDCWidget::on_ok_clicked()
|
|
{
|
|
updateState(DOWNLOADING);
|
|
|
|
// I don't really think that create/destroy the thread
|
|
// is really necessary.
|
|
if (thread) {
|
|
thread->deleteLater();
|
|
}
|
|
|
|
data.devname = strdup(ui.device->currentText().toUtf8().data());
|
|
data.vendor = strdup(ui.vendor->currentText().toUtf8().data());
|
|
data.product = strdup(ui.product->currentText().toUtf8().data());
|
|
|
|
data.descriptor = descriptorLookup[ui.vendor->currentText() + ui.product->currentText()];
|
|
data.force_download = ui.forceDownload->isChecked();
|
|
data.deviceid = data.diveid = 0;
|
|
set_default_dive_computer(data.vendor, data.product);
|
|
set_default_dive_computer_device(data.devname);
|
|
|
|
thread = new DownloadThread(this, &data);
|
|
|
|
connect(thread, SIGNAL(finished()),
|
|
this, SLOT(onDownloadThreadFinished()), Qt::QueuedConnection);
|
|
|
|
MainWindow *w = mainWindow();
|
|
connect(thread, SIGNAL(finished()), w, SLOT(refreshDisplay()));
|
|
|
|
thread->start();
|
|
}
|
|
|
|
bool DownloadFromDCWidget::preferDownloaded()
|
|
{
|
|
return ui.preferDownloaded->isChecked();
|
|
}
|
|
|
|
void DownloadFromDCWidget::reject()
|
|
{
|
|
// we don't want the download window being able to close
|
|
// while we're still downloading.
|
|
if (currentState != DOWNLOADING && currentState != CANCELLING)
|
|
QDialog::reject();
|
|
}
|
|
|
|
void DownloadFromDCWidget::onDownloadThreadFinished()
|
|
{
|
|
if (currentState == DOWNLOADING) {
|
|
if (thread->error.isEmpty())
|
|
updateState(DONE);
|
|
else
|
|
updateState(ERROR);
|
|
|
|
// I'm not sure if we should really call process_dives even
|
|
// if there's an error or a cancelation
|
|
process_dives(TRUE, preferDownloaded());
|
|
} else
|
|
updateState(CANCELLED);
|
|
}
|
|
|
|
void DownloadFromDCWidget::markChildrenAsDisabled()
|
|
{
|
|
ui.device->setDisabled(true);
|
|
ui.vendor->setDisabled(true);
|
|
ui.product->setDisabled(true);
|
|
ui.forceDownload->setDisabled(true);
|
|
ui.preferDownloaded->setDisabled(true);
|
|
ui.ok->setDisabled(true);
|
|
ui.search->setDisabled(true);
|
|
}
|
|
|
|
void DownloadFromDCWidget::markChildrenAsEnabled()
|
|
{
|
|
ui.device->setDisabled(false);
|
|
ui.vendor->setDisabled(false);
|
|
ui.product->setDisabled(false);
|
|
ui.forceDownload->setDisabled(false);
|
|
ui.preferDownloaded->setDisabled(false);
|
|
ui.ok->setDisabled(false);
|
|
ui.cancel->setDisabled(false);
|
|
ui.search->setDisabled(false);
|
|
}
|
|
|
|
static void fillDeviceList(const char *name, void *data)
|
|
{
|
|
QComboBox *comboBox = (QComboBox *)data;
|
|
comboBox->addItem(name);
|
|
}
|
|
|
|
void DownloadFromDCWidget::fill_device_list()
|
|
{
|
|
int deviceIndex;
|
|
ui.device->clear();
|
|
deviceIndex = enumerate_devices(fillDeviceList, ui.device);
|
|
if (deviceIndex >= 0)
|
|
ui.device->setCurrentIndex(deviceIndex);
|
|
}
|
|
|
|
DownloadThread::DownloadThread(QObject* parent, device_data_t* data): QThread(parent),
|
|
data(data)
|
|
{
|
|
}
|
|
|
|
static QString str_error(const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
const QString str = QString().vsprintf( fmt, args );
|
|
va_end(args);
|
|
|
|
return str;
|
|
}
|
|
|
|
void DownloadThread::run()
|
|
{
|
|
const char *error;
|
|
if (!strcmp(data->vendor, "Uemis"))
|
|
error = do_uemis_import(data->devname, data->force_download);
|
|
else
|
|
error = do_libdivecomputer_import(data);
|
|
if (error)
|
|
this->error = str_error(error, data->devname, data->vendor, data->product);
|
|
}
|