diff -r 23436e487f0c -r 09e1a3e272ec src/partdownloader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/partdownloader.cpp Sun Sep 06 16:57:22 2015 +0300 @@ -0,0 +1,336 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013 - 2015 Teemu Piippo + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include "partdownloader.h" +#include "partdownloadrequest.h" +#include "ui_partdownloader.h" +#include "basics.h" +#include "mainwindow.h" +#include "ldDocument.h" +#include "glRenderer.h" + +ConfigOption (QString DownloadFilePath) +ConfigOption (bool GuessDownloadPaths = true) +ConfigOption (bool AutoCloseDownloadDialog = true) + +const char* g_unofficialLibraryURL = "http://ldraw.org/library/unofficial/"; + +PartDownloader::PartDownloader (QWidget* parent) : + QDialog (parent), + HierarchyElement (parent), + ui (*new Ui_PartDownloader), + m_source (SourceType (0)) +{ + ui.setupUi (this); + +#ifdef USE_QT5 + ui.progressTable->horizontalHeader()->setSectionResizeMode (QHeaderView::Stretch); +#else + ui.progressTable->horizontalHeader()->setResizeMode (PartLabelColumn, QHeaderView::Stretch); +#endif + + m_downloadButton = new QPushButton (tr ("Download")); + ui.buttonBox->addButton (m_downloadButton, QDialogButtonBox::ActionRole); + button (Abort)->setEnabled (false); + connect (ui.source, SIGNAL (currentIndexChanged (int)), this, SLOT (sourceChanged (int))); + connect (ui.buttonBox, SIGNAL (clicked (QAbstractButton*)), this, SLOT (buttonClicked (QAbstractButton*))); +} + +PartDownloader::~PartDownloader() +{ + delete &ui; +} + +void PartDownloader::checkValidPath() +{ + QString path = downloadPath(); + + if (path.isEmpty() or not QDir (path).exists()) + { + QMessageBox::information(this, "Notice", "Please input a path for files to download."); + path = QFileDialog::getExistingDirectory (this, "Path for downloaded files:"); + + if (path.isEmpty()) + reject(); + else + m_config->setDownloadFilePath (path); + } +} + +QString PartDownloader::url() +{ + QString destination; + + switch (sourceType()) + { + case PartsTracker: + destination = ui.filename->text(); + modifyDestination (destination); + ui.filename->setText (destination); + return g_unofficialLibraryURL + destination; + + case CustomURL: + return ui.filename->text(); + } + + // Shouldn't happen + return ""; +} + +void PartDownloader::modifyDestination (QString& dest) const +{ + dest = dest.simplified(); + + // If the user doesn't want us to guess, stop right here. + if (not m_config->guessDownloadPaths()) + return; + + // Ensure .dat extension + if (dest.right (4) != ".dat") + { + // Remove the existing extension, if any. It may be we're here over a + // typo in the .dat extension. + const int dotpos = dest.lastIndexOf ("."); + + if ((dotpos != -1) and (dotpos >= dest.length() - 4)) + dest.chop (dest.length() - dotpos); + + dest += ".dat"; + } + + // If the part starts with s\ or s/, then use parts/s/. Same goes with + // 48\ and p/48/. + if (isOneOf (dest.left (2), "s\\", "s/")) + { + dest.remove (0, 2); + dest.prepend ("parts/s/"); + } + else if (isOneOf (dest.left (3), "48\\", "48/")) + { + dest.remove (0, 3); + dest.prepend ("p/48/"); + } + + /* Try determine where to put this part. We have four directories: + parts/, parts/s/, p/, and p/48/. If we haven't already specified + either parts/ or p/, we need to add it automatically. Part files + are numbers wit a possible u prefix for parts with unknown number + which can be followed by any of: + - c** (composites) + - d** (formed stickers) + - p** (patterns) + - a lowercase alphabetic letter for variants + + Subfiles (usually) have an s** prefix, in which case we use parts/s/. + Note that the regex starts with a '^' so it won't catch already fully + given part file names. */ + QString partRegex = "^u?[0-9]+(c[0-9][0-9]+)*(d[0-9][0-9]+)*[a-z]?(p[0-9a-z][0-9a-z]+)*"; + QString subpartRegex = partRegex + "s[0-9][0-9]+"; + + partRegex += "\\.dat$"; + subpartRegex += "\\.dat$"; + + if (QRegExp (subpartRegex).exactMatch (dest)) + dest.prepend ("parts/s/"); + else if (QRegExp (partRegex).exactMatch (dest)) + dest.prepend ("parts/"); + else if (not dest.startsWith ("parts/") and not dest.startsWith ("p/")) + dest.prepend ("p/"); +} + +PartDownloader::SourceType PartDownloader::sourceType() const +{ + return m_source; +} + +void PartDownloader::setSourceType (SourceType src) +{ + m_source = src; + ui.source->setCurrentIndex (int (src)); +} + +void PartDownloader::sourceChanged (int i) +{ + if (i == CustomURL) + ui.fileNameLabel->setText (tr ("URL:")); + else + ui.fileNameLabel->setText (tr ("File name:")); + + m_source = SourceType (i); +} + +void PartDownloader::buttonClicked (QAbstractButton* btn) +{ + if (btn == button (Close)) + { + reject(); + } + else if (btn == button (Abort)) + { + m_isAborted = true; + + for (PartDownloadRequest* req : m_requests) + req->abort(); + } + else if (btn == button (Download)) + { + QString dest = ui.filename->text(); + setPrimaryFile (nullptr); + m_isAborted = false; + + if (sourceType() == CustomURL) + dest = Basename (url()); + + modifyDestination (dest); + + if (QFile::exists (downloadPath() + DIRSLASH + dest)) + { + const QString overwritemsg = format (tr ("%1 already exists in download directory. Overwrite?"), dest); + if (not Confirm (tr ("Overwrite?"), overwritemsg)) + return; + } + + downloadFile (dest, url(), true); + } +} + +void PartDownloader::downloadFile (QString dest, QString url, bool primary) +{ + int row = ui.progressTable->rowCount(); + + // Don't download files repeadetly. + if (m_filesToDownload.indexOf (dest) != -1) + return; + + print ("Downloading %1 from %2\n", dest, url); + modifyDestination (dest); + PartDownloadRequest* req = new PartDownloadRequest (url, dest, primary, this); + m_filesToDownload << dest; + m_requests << req; + ui.progressTable->insertRow (row); + req->setTableRow (row); + req->updateToTable(); + m_downloadButton->setEnabled (false); + ui.progressTable->setEnabled (true); + ui.filename->setEnabled (false); + ui.source->setEnabled (false); + button (Close)->setEnabled (false); + button (Abort)->setEnabled (true); + button (Download)->setEnabled (false); +} + +void PartDownloader::downloadFromPartsTracker (QString file) +{ + modifyDestination (file); + downloadFile (file, g_unofficialLibraryURL + file, false); +} + +void PartDownloader::checkIfFinished() +{ + bool failed = isAborted(); + + // If there is some download still working, we're not finished. + for (PartDownloadRequest* req : m_requests) + { + if (not req->isFinished()) + return; + + if (req->failed()) + failed = true; + } + + for (PartDownloadRequest* req : m_requests) + delete req; + + m_requests.clear(); + + if (primaryFile()) + emit primaryFileDownloaded(); + + for (LDDocument* f : m_files) + f->reloadAllSubfiles(); + + if (m_config->autoCloseDownloadDialog() and not failed) + { + // Close automatically if desired. + accept(); + } + else + { + // Allow the prompt be closed now. + button (Abort)->setEnabled (false); + button (Close)->setEnabled (true); + } +} + +QPushButton* PartDownloader::button (PartDownloader::Button i) +{ + switch (i) + { + case Download: + return m_downloadButton; + + case Abort: + return qobject_cast (ui.buttonBox->button (QDialogButtonBox::Abort)); + + case Close: + return qobject_cast (ui.buttonBox->button (QDialogButtonBox::Close)); + } + + return nullptr; +} + +void PartDownloader::addFile (LDDocument* f) +{ + m_files << f; +} + +bool PartDownloader::isAborted() const +{ + return m_isAborted; +} + +LDDocument* PartDownloader::primaryFile() const +{ + return m_primaryFile; +} + +void PartDownloader::setPrimaryFile (LDDocument* document) +{ + m_primaryFile = document; +} + +QString PartDownloader::downloadPath() +{ + QString path = m_config->downloadFilePath(); + + if (DIRSLASH[0] != '/') + path.replace (DIRSLASH, "/"); + + return path; +} + +QTableWidget* PartDownloader::progressTable() const +{ + return ui.progressTable; +}