//Copyright 2017 Ryan Wick
//This file is part of Bandage
//Bandage 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.
//Bandage 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 Bandage. If not, see .
#include "pathspecifydialog.h"
#include "ui_pathspecifydialog.h"
#include "../program/globals.h"
#include "../program/settings.h"
#include "../graph/assemblygraph.h"
#include
#include
#include
#include
#include "mygraphicsview.h"
#include "../program/memory.h"
PathSpecifyDialog::PathSpecifyDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::PathSpecifyDialog)
{
ui->setupUi(this);
setWindowFlags(windowFlags() | Qt::Tool);
ui->pathTextEdit->setPlainText(g_memory->userSpecifiedPathString);
ui->circularPathCheckBox->setChecked(g_memory->userSpecifiedPathCircular);
g_memory->pathDialogIsVisible = true;
checkPathValidity();
ui->circularPathInfoText->setInfoText("Tick this box to indicate that the path is circular, i.e. there is an edge connecting the "
"last node in the list to the first.
"
"Circular paths must contain the entirety of their nodes and therefore cannot contain "
"start/end positions.");
connect(ui->pathTextEdit, SIGNAL(textChanged()), this, SLOT(checkPathValidity()));
connect(ui->pathTextEdit, SIGNAL(textChanged()), g_graphicsView->viewport(), SLOT(update()));
connect(ui->circularPathCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkPathValidity()));
connect(ui->circularPathCheckBox, SIGNAL(toggled(bool)), g_graphicsView->viewport(), SLOT(update()));
connect(ui->copyButton, SIGNAL(clicked(bool)), this, SLOT(copyPathToClipboard()));
connect(ui->saveButton, SIGNAL(clicked(bool)), this, SLOT(savePathToFile()));
connect(this, SIGNAL(rejected()), this, SLOT(deleteLater()));
}
PathSpecifyDialog::~PathSpecifyDialog()
{
g_memory->pathDialogIsVisible = false;
delete ui;
}
void PathSpecifyDialog::checkPathValidity()
{
g_memory->userSpecifiedPathString = ui->pathTextEdit->toPlainText();
g_memory->userSpecifiedPathCircular = ui->circularPathCheckBox->isChecked();
g_memory->userSpecifiedPath = Path();
//Clear out the Path object. If the string makes a valid path,
//it will be rebuilt.
g_memory->userSpecifiedPath = Path();
//If there is no graph loaded, then no path can be valid.
if (g_assemblyGraph->m_deBruijnGraphNodes.size() == 0)
{
ui->validPathLabel->setText("Invalid path: no graph is currently loaded");
setPathValidityUiElements(false);
return;
}
//Create a path from the user-supplied string.
QString pathStringFailure;
QString pathText = ui->pathTextEdit->toPlainText().simplified();
g_memory->userSpecifiedPath = Path::makeFromString(pathText,
ui->circularPathCheckBox->isChecked(),
&pathStringFailure);
//If the Path turned out to be empty, that means that makeFromString failed.
if (g_memory->userSpecifiedPath.isEmpty())
{
if (pathText == "")
ui->validPathLabel->setText("No path specified");
else
ui->validPathLabel->setText("Invalid path: " + pathStringFailure);
setPathValidityUiElements(false);
}
//If the Path isn't empty, then we have succeeded!
else
{
int pathLength = g_memory->userSpecifiedPath.getLength();
ui->validPathLabel->setText("Valid path: " + formatIntForDisplay(pathLength) + " bp");
setPathValidityUiElements(true);
}
}
void PathSpecifyDialog::setPathValidityUiElements(bool pathValid)
{
QPixmap tickCross;
if (pathValid)
tickCross = QPixmap(":/icons/tick-128.png");
else
tickCross = QPixmap(":/icons/cross-128.png");
tickCross.setDevicePixelRatio(devicePixelRatio()); //This is a workaround for a Qt bug. Can possibly remove in the future. https://bugreports.qt.io/browse/QTBUG-46846
ui->tickCrossLabel->setPixmap(tickCross);
ui->copyButton->setEnabled(pathValid);
ui->saveButton->setEnabled(pathValid);
}
void PathSpecifyDialog::copyPathToClipboard()
{
QClipboard * clipboard = QApplication::clipboard();
clipboard->setText(g_memory->userSpecifiedPath.getPathSequence());
}
void PathSpecifyDialog::savePathToFile()
{
QString defaultFileNameAndPath = g_memory->rememberedPath + "/path_sequence.fasta";
QString fullFileName = QFileDialog::getSaveFileName(this, "Save path sequence", defaultFileNameAndPath, "FASTA (*.fasta)");
if (fullFileName != "") //User did not hit cancel
{
QFile file(fullFileName);
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
out << g_memory->userSpecifiedPath.getFasta();
g_memory->rememberedPath = QFileInfo(fullFileName).absolutePath();
}
}
void PathSpecifyDialog::addNodeName(DeBruijnNode * node)
{
QString pathText = ui->pathTextEdit->toPlainText();
//If the node fits on the end of the path add it there.
Path extendedPath = g_memory->userSpecifiedPath;
if (g_memory->userSpecifiedPath.canNodeFitOnEnd(node, &extendedPath))
pathText = extendedPath.getString(true);
//If not, try the front of the path.
else if (g_memory->userSpecifiedPath.canNodeFitAtStart(node, &extendedPath))
pathText = extendedPath.getString(true);
//If neither of these work, try the reverse complement, first
//at the end and then at the front.
//But only do this if we are in single mode.
else if (!g_settings->doubleMode &&
g_memory->userSpecifiedPath.canNodeFitOnEnd(node->getReverseComplement(), &extendedPath))
pathText = extendedPath.getString(true);
else if (!g_settings->doubleMode &&
g_memory->userSpecifiedPath.canNodeFitAtStart(node->getReverseComplement(), &extendedPath))
pathText = extendedPath.getString(true);
//If all of the above failed, we do nothing. I.e. if the node cannot be
//added to the path, it isn't added to the text.
ui->pathTextEdit->setPlainText(pathText);
}