Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
/*
* LibrePCB - Professional EDA for everyone!
* Copyright (C) 2013 LibrePCB Developers, see AUTHORS.md for contributors.
* https://librepcb.org/
*
* 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 <http://www.gnu.org/licenses/>.
*/
/*******************************************************************************
* Includes
******************************************************************************/
#include "newprojectwizardpage_eagleimport.h"
#include "../../dialogs/filedialog.h"
#include "../../editorcommandset.h"
#include "../../widgets/waitingspinnerwidget.h"
#include "../../workspace/desktopservices.h"
#include "ui_newprojectwizardpage_eagleimport.h"
#include <librepcb/core/exceptions.h>
#include <librepcb/core/fileio/filepath.h>
#include <librepcb/core/utils/messagelogger.h>
#include <librepcb/core/workspace/workspace.h>
#include <librepcb/eagleimport/eagleprojectimport.h>
#include <QtConcurrent>
#include <QtCore>
#include <QtWidgets>
/*******************************************************************************
* Namespace
******************************************************************************/
namespace librepcb {
namespace editor {
/*******************************************************************************
* Constructors / Destructor
******************************************************************************/
NewProjectWizardPage_EagleImport::NewProjectWizardPage_EagleImport(
const UiTheme& theme, const Workspace& ws, QWidget* parent) noexcept
: QWizardPage(parent),
mTheme(theme),
mWorkspace(ws),
mUi(new Ui::NewProjectWizardPage_EagleImport) {
mUi->setupUi(this);
setPixmap(QWizard::LogoPixmap, QPixmap(":/img/actions/plus_2.png"));
setPixmap(QWizard::WatermarkPixmap, QPixmap(":/img/wizards/watermark.jpg"));
mWaitingSpinner.reset(new WaitingSpinnerWidget(mUi->sclMessages));
mWaitingSpinner->hide();
// Setup schematic input field.
const EditorCommandSet& cmd = EditorCommandSet::instance();
QAction* aBrowseSch = cmd.inputBrowse.createAction(
mUi->edtSchematicFilePath, this,
[this]() {
const QString fp = FileDialog::getOpenFileName(
this, tr("Select EAGLE Schematic"),
mUi->edtSchematicFilePath->text(), "*.sch");
if (!fp.isEmpty()) {
mUi->edtSchematicFilePath->setText(fp);
const QString brdFp = fp.chopped(4) % ".brd";
if (FilePath(brdFp).isExistingFile()) {
mUi->edtBoardFilePath->setText(brdFp); // Import board too.
} else {
mUi->edtBoardFilePath->clear(); // Don't import any board.
}
}
},
EditorCommand::ActionFlag::WidgetShortcut);
mUi->edtSchematicFilePath->addAction(aBrowseSch, QLineEdit::TrailingPosition);
connect(mUi->edtSchematicFilePath, &QLineEdit::textChanged, this,
&NewProjectWizardPage_EagleImport::updateStatus);
// Setup board input field.
QAction* aBrowseBrd = cmd.inputBrowse.createAction(
mUi->edtBoardFilePath, this,
[this]() {
const QString fp =
FileDialog::getOpenFileName(this, tr("Select EAGLE Board"),
mUi->edtBoardFilePath->text(), "*.brd");
if (!fp.isEmpty()) {
mUi->edtBoardFilePath->setText(fp);
}
},
EditorCommand::ActionFlag::WidgetShortcut);
mUi->edtBoardFilePath->addAction(aBrowseBrd, QLineEdit::TrailingPosition);
connect(mUi->edtBoardFilePath, &QLineEdit::textChanged, this,
&NewProjectWizardPage_EagleImport::updateStatus);
mUi->edtBoardFilePath->setEnabled(false);
// Load settings.
QSettings clientSettings;
mUi->edtSchematicFilePath->setText(
clientSettings.value("new_project_wizard/eagle_import/schematic_file")
.toString());
mUi->edtBoardFilePath->setText(
clientSettings.value("new_project_wizard/eagle_import/board_file")
.toString());
// Periodically update status.
QTimer* timer = new QTimer(this);
connect(timer, &QTimer::timeout, this,
&NewProjectWizardPage_EagleImport::updateStatus);
timer->start(100);
}
NewProjectWizardPage_EagleImport::~NewProjectWizardPage_EagleImport() noexcept {
// Save settings.
QSettings clientSettings;
clientSettings.setValue("new_project_wizard/eagle_import/schematic_file",
mUi->edtSchematicFilePath->text());
clientSettings.setValue("new_project_wizard/eagle_import/board_file",
mUi->edtBoardFilePath->text());
}
/*******************************************************************************
* General Methods
******************************************************************************/
void NewProjectWizardPage_EagleImport::import(Project& project) {
if (!mImport) {
throw LogicError(__FILE__, __LINE__, "No importer available.");
}
// Displaying the messages in a dialog which remains open even after the
// import finished. Unfortunately this is a bit hacky, some day it should be
// improved.
QDialog* dialog = new QDialog();
dialog->setWindowTitle(tr("EAGLE Project Import"));
dialog->setWindowIcon(QIcon(":/img/logo/64x64.png"));
QVBoxLayout* layout = new QVBoxLayout(dialog);
QTextBrowser* browser = new QTextBrowser(dialog);
browser->setWordWrapMode(QTextOption::NoWrap);
browser->setOpenLinks(false);
const Workspace* ws = &mWorkspace;
connect(browser, &QTextBrowser::anchorClicked, ws, [ws](const QUrl& url) {
DesktopServices ds(ws->getSettings());
ds.openUrl(url);
});
connect(mImport->getLogger().get(), &MessageLogger::msgEmitted, browser,
[browser, &theme = mTheme](const MessageLogger::Message& msg) {
browser->append(msg.toRichText(&theme, true));
browser->verticalScrollBar()->setValue(
browser->verticalScrollBar()->maximum());
qApp->processEvents();
});
layout->addWidget(browser);
QPushButton* btnClose = new QPushButton(tr("Close"), dialog);
btnClose->setEnabled(false);
connect(this, &NewProjectWizardPage_EagleImport::destroyed, btnClose,
&QPushButton::setEnabled);
connect(btnClose, &QPushButton::clicked, dialog, &QDialog::close);
layout->addWidget(btnClose);
dialog->resize(800, 600);
dialog->show();
qApp->processEvents();
// Run the import (long-running blocking operation).
mImport->import(project); // can throw
// After the project editor has been opened, bring messages dialog to front.
// A bit hacky, probably some day we will need a better solution for this...
QTimer::singleShot(2500, dialog, [dialog]() {
dialog->raise();
dialog->activateWindow();
});
}
/*******************************************************************************
* Private Methods
******************************************************************************/
void NewProjectWizardPage_EagleImport::updateStatus() noexcept {
const QString sch = mUi->edtSchematicFilePath->text().trimmed();
const QString brd = mUi->edtBoardFilePath->text().trimmed();
const FilePath schFp(sch);
const FilePath brdFp(brd);
mUi->edtBoardFilePath->setEnabled(schFp.isValid());
if (((sch != mCurrentSchematic) || (brd != mCurrentBoard)) &&
(!mFuture.isRunning())) {
mImport.reset();
mCurrentSchematic = sch;
mCurrentBoard = brd;
if (sch.isEmpty()) {
mUi->lblMessages->hide();
} else if (schFp.isValid() && (brdFp.isValid() || brd.isEmpty())) {
mWaitingSpinner->show();
mUi->lblMessages->setText("<font color=\"#63d0df\">" %
tr("Parsing project...") % "</font>");
mUi->lblMessages->show();
mFuture = QtConcurrent::run(
&parseAsync, std::make_shared<eagleimport::EagleProjectImport>(),
schFp, brdFp);
} else {
mUi->lblMessages->setText("<font color=\"red\">⚠ " %
tr("Invalid file path(s).") % "</font>");
mUi->lblMessages->show();
}
emit completeChanged();
}
if (mFuture.isResultReadyAt(0)) {
const auto result = mFuture.result();
mFuture = {};
mImport = result.import;
mUi->lblMessages->setText(result.messages.join("<br>"));
mUi->lblMessages->setVisible(!result.messages.isEmpty());
mWaitingSpinner->hide();
emit completeChanged();
}
}
NewProjectWizardPage_EagleImport::ParserResult
NewProjectWizardPage_EagleImport::parseAsync(
std::shared_ptr<eagleimport::EagleProjectImport> import,
const FilePath& schFp, const FilePath& brdFp) noexcept {
ParserResult result;
try {
result.import = import;
const QStringList warnings = result.import->open(schFp, brdFp);
foreach (const QString& warning, warnings) {
result.messages.append("<font color=\"yellow\">➤ " % warning % "</font>");
}
const QString msg = result.import->hasBoard()
? tr("Ready to import %n sheet(s) and a board.", nullptr,
result.import->getSheetCount())
: tr("Ready to import %n sheet(s) without board.", nullptr,
result.import->getSheetCount());
result.messages.append("<font color=\"#62de70\">✔ " % msg % "</font>");
} catch (const Exception& e) {
result.messages.append("<font color=\"red\">⚠ " % tr("ERROR:") % " " %
e.getMsg() % "</font>");
}
return result;
}
bool NewProjectWizardPage_EagleImport::isComplete() const noexcept {
// Check base class.
if (!QWizardPage::isComplete()) return false;
// Check EAGLE project.
if ((!mImport) || (!mImport->isReady())) return false;
// Preselect project name for next wizard page.
emit projectSelected(mImport->getProjectName());
return true;
}
/*******************************************************************************
* End of File
******************************************************************************/
} // namespace editor
} // namespace librepcb
f187d7f67This file is pinned to the selected release. Download the original to open it in your design tool.