//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 "commoncommandlinefunctions.h"
#include "../graph/assemblygraph.h"
#include
#include "../blast/blastsearch.h"
#include
#include
#include "../program/memory.h"
#include
QStringList getArgumentList(int argc, char *argv[])
{
QStringList arguments;
for (int i = 1; i < argc; ++i)
{
QString argument = argv[i];
arguments.push_back(argument);
}
return arguments;
}
void getSettingsUsage(QStringList * text)
{
QString dashes = "";
for (int i = 0; i < g_memory->terminalWidth - 10; ++i)
dashes += '-';
*text << "Settings: The following options configure the Bandage settings that are available in the Bandage GUI.";
*text << "";
*text << "Colours can be specified using hex values, with or without an alpha channel, (e.g. #FFB6C1 or #7FD2B48C) or using standard colour names (e.g. red, yellowgreen or skyblue). Note that hex colours will either need to be enclosed in quotes (e.g. \"#FFB6C1\") or have the hash symbol escaped (e.g. \\#FFB6C1).";
*text << "";
*text << "Graph scope";
*text << dashes;
*text << "These settings control the graph scope. If the aroundnodes scope is used, then the --nodes option must also be used. If the aroundblast scope is used, a BLAST query must be given with the --query option.";
getGraphScopeOptions(text);
*text << "--double Draw graph in double mode (default: off)";
*text << "";
*text << "Graph size";
*text << dashes;
*text << "--nodelen Node length per megabase " + getRangeAndDefault(g_settings->manualNodeLengthPerMegabase, "auto");
*text << "--minnodlen Minimum node length " + getRangeAndDefault(g_settings->minimumNodeLength);
*text << "--edgelen Edge length " + getRangeAndDefault(g_settings->edgeLength);
*text << "--edgewidth Edge width " + getRangeAndDefault(g_settings->edgeWidth);
*text << "--doubsep Double mode separation " + getRangeAndDefault(g_settings->doubleModeNodeSeparation);
*text << "";
*text << "Graph layout";
*text << dashes;
*text << "--nodseglen Node segment length " + getRangeAndDefault(g_settings->nodeSegmentLength);
*text << "--iter Graph layout iterations " + getRangeAndDefault(g_settings->graphLayoutQuality);
*text << "--linear Linear graph layout (default: off)" ;
*text << "";
*text << "Graph appearance";
*text << dashes;
*text << "--edgecol Colour for edges " + getDefaultColour(g_settings->edgeColour);
*text << "--outcol Colour for node outlines " + getDefaultColour(g_settings->outlineColour);
*text << "--outline Node outline thickness " + getRangeAndDefault(g_settings->outlineThickness);
*text << "--selcol Colour for selections " + getDefaultColour(g_settings->selectionColour);
*text << "--noaa Disable antialiasing (default: antialiasing on)";
*text << "--singlearr Show node arrowheads in single mode (default: nodes are only displayed with arrowheads in double mode)";
*text << "";
*text << "Text appearance";
*text << dashes;
*text << "--textcol Colour for label text " + getDefaultColour(g_settings->textColour);
*text << "--toutcol Colour for text outline " + getDefaultColour(g_settings->textOutlineColour);
*text << "--toutline Surround text with an outline with this thickness " + getRangeAndDefault(g_settings->textOutlineThickness);
*text << "--centre Node labels appear at the centre of the node (default: off, node labels appear over visible parts of nodes)";
*text << "";
*text << "Node width";
*text << dashes;
*text << "Node widths are determined using the following formula:";
*text << "a*b*((c/d)^e-1)+1";
*text << " a = average node width";
*text << " b = depth effect on width";
*text << " c = node depth";
*text << " d = mean depth";
*text << " e = power of depth effect on width";
*text << "--nodewidth Average node width " + getRangeAndDefault(g_settings->averageNodeWidth);
*text << "--depwidth Depth effect on width " + getRangeAndDefault(g_settings->depthEffectOnWidth);
*text << "--deppower Power of depth effect on width " + getRangeAndDefault(g_settings->depthPower);
*text << "";
*text << "Node labels";
*text << dashes;
*text << "--names Label nodes with name (default: off)";
*text << "--lengths Label nodes with length (default: off)";
*text << "--depth Label nodes with depth (default: off)";
*text << "--blasthits Label BLAST hits (default: off)";
*text << "--fontsize Font size for node labels " + getRangeAndDefault(1, 100, g_settings->labelFont.pointSize());
*text << "";
*text << "Node colours";
*text << dashes;
*text << "--colour Node colouring scheme, from one of the following options: random, uniform, depth, blastsolid, blastrainbow, custom (default: random if --query option not used, blastsolid if --query option used)";
*text << "";
*text << "Random colour scheme";
*text << dashes;
*text << "These settings only apply when the random colour scheme is used.";
*text << "--ransatpos Positive node saturation " + getRangeAndDefault(g_settings->randomColourPositiveSaturation);
*text << "--ransatneg Negative node saturation " + getRangeAndDefault(g_settings->randomColourNegativeSaturation);
*text << "--ranligpos Positive node lightness " + getRangeAndDefault(g_settings->randomColourPositiveLightness);
*text << "--ranligneg Negative node lightness " + getRangeAndDefault(g_settings->randomColourNegativeLightness);
*text << "--ranopapos Positive node opacity " + getRangeAndDefault(g_settings->randomColourPositiveOpacity);
*text << "--ranopaneg Negative node opacity " + getRangeAndDefault(g_settings->randomColourNegativeOpacity);
*text << "";
*text << "Uniform colour scheme";
*text << dashes;
*text << "These settings only apply when the uniform colour scheme is used.";
*text << "--unicolpos Positive node colour " + getDefaultColour(g_settings->uniformPositiveNodeColour);
*text << "--unicolneg Negative node colour " + getDefaultColour(g_settings->uniformNegativeNodeColour);
*text << "--unicolspe Special node colour " + getDefaultColour(g_settings->uniformNodeSpecialColour);
*text << "";
*text << "Depth colour scheme";
*text << dashes;
*text << "These settings only apply when the depth colour scheme is used.";
*text << "--depcollow Colour for nodes with depth below the low depth value " + getDefaultColour(g_settings->lowDepthColour);
*text << "--depcolhi Colour for nodes with depth above the high depth value " + getDefaultColour(g_settings->highDepthColour);
*text << "--depvallow Low depth value " + getRangeAndDefault(g_settings->lowDepthValue, "auto");
*text << "--depvalhi High depth value " + getRangeAndDefault(g_settings->highDepthValue, "auto");
*text << "";
*text << "BLAST search";
*text << dashes;
*text << "--query A FASTA file of either nucleotide or protein sequences to be used as BLAST queries (default: none)";
*text << "--blastp Parameters to be used by blastn and tblastn when conducting a BLAST search in Bandage (default: none). Format BLAST parameters exactly as they would be used for blastn/tblastn on the command line, and enclose them in quotes.";
*text << "--alfilter Alignment length filter for BLAST hits. Hits with shorter alignments will be excluded " + getRangeAndDefault(g_settings->blastAlignmentLengthFilter);
*text << "--qcfilter Query coverage filter for BLAST hits. Hits with less coverage will be excluded " + getRangeAndDefault(g_settings->blastQueryCoverageFilter);
*text << "--ifilter Identity filter for BLAST hits. Hits with less identity will be excluded " + getRangeAndDefault(g_settings->blastIdentityFilter);
*text << "--evfilter E-value filter for BLAST hits. Hits with larger e-values will be excluded " + getRangeAndDefault(g_settings->blastEValueFilter);
*text << "--bsfilter Bit score filter for BLAST hits. Hits with lower bit scores will be excluded " + getRangeAndDefault(g_settings->blastBitScoreFilter);
*text << "";
*text << "BLAST query paths";
*text << dashes;
*text << "These settings control how Bandage searches for query paths after conducting a BLAST search.";
*text << "--pathnodes The number of allowed nodes in a BLAST query path " + getRangeAndDefault(g_settings->maxQueryPathNodes);
*text << "--minpatcov Minimum fraction of a BLAST query which must be covered by a query path " + getRangeAndDefault(g_settings->minQueryCoveredByPath);
*text << "--minhitcov Minimum fraction of a BLAST query which must be covered by BLAST hits in a query path " + getRangeAndDefault(g_settings->minQueryCoveredByHits);
*text << "--minmeanid Minimum mean identity of BLAST hits in a query path " + getRangeAndDefault(g_settings->minMeanHitIdentity);
*text << "--maxevprod Maximum e-value product for all BLAST hits in a query path " + getRangeAndDefault(g_settings->maxEValueProduct);
*text << "--minpatlen Minimum allowed relative path length as compared to the query " + getRangeAndDefault(g_settings->minLengthPercentage);
*text << "--maxpatlen Maximum allowed relative path length as compared to the query " + getRangeAndDefault(g_settings->maxLengthPercentage);
*text << "--minlendis Minimum allowed length discrepancy (in bases) between a BLAST query and its path in the graph " + getRangeAndDefault(g_settings->minLengthBaseDiscrepancy);
*text << "--maxlendis Maximum allowed length discrepancy (in bases) between a BLAST query and its path in the graph " + getRangeAndDefault(g_settings->maxLengthBaseDiscrepancy);
*text << "";
}
void outputText(QString text, QTextStream * out)
{
QStringList list;
list << text;
outputText(list, out);
}
void outputText(QStringList text, QTextStream * out)
{
QStringList wrapped;
bool seenHeaderOrList = false;
for (int i = 0; i < text.size(); ++i)
{
QString line = text[i];
if (isSectionHeader(line) || isListItem(line))
seenHeaderOrList = true;
if (isError(line))
wrapped << wrapText(line, g_memory->terminalWidth, 0, 0);
else if (!seenHeaderOrList)
wrapped << wrapText(line, g_memory->terminalWidth, 0, 0);
else if (isSectionHeader(line) && line.contains("--"))
wrapped << wrapText(line, g_memory->terminalWidth, 0, 30);
else if (isSectionHeader(line))
wrapped << wrapText(line, g_memory->terminalWidth, 0, 10);
else if (isListItem(line))
wrapped << wrapText(line, g_memory->terminalWidth, 2, 4);
else if (isCommand(line))
wrapped << wrapText(line, g_memory->terminalWidth, 10, 23);
else if (isOption(line))
wrapped << wrapText(line, g_memory->terminalWidth, 10, 30);
else
wrapped << wrapText(line, g_memory->terminalWidth, 10, 10);
}
*out << Qt::endl;
for (int i = 0; i < wrapped.size(); ++i)
{
*out << wrapped[i];
*out << Qt::endl;
}
}
//This is in a separate function because the command line tool Bandage reduce
//also displays these.
void getGraphScopeOptions(QStringList * text)
{
*text << "--scope Graph scope, from one of the following options: entire, aroundnodes, aroundblast, depthrange (default: entire)";
*text << "--nodes A comma-separated list of starting nodes for the aroundnodes scope (default: none)";
*text << "--partial Use partial node name matching (default: exact node name matching)";
*text << "--distance The number of node steps away to draw for the aroundnodes and aroundblast scopes " + getRangeAndDefault(g_settings->nodeDistance);
*text << "--mindepth The minimum allowed depth for the depthrange scope " + getRangeAndDefault(g_settings->minDepthRange);
*text << "--maxdepth The maximum allowed depth for the depthrange scope " + getRangeAndDefault(g_settings->maxDepthRange);
}
//This function checks the values of the Bandage settings.
//If everything is fine, it removes the good arguments/values and returns
//a null string. If there's a problem, it returns an error message.
QString checkForInvalidOrExcessSettings(QStringList * arguments)
{
QStringList argumentsCopy = *arguments;
QStringList validScopeOptions;
validScopeOptions << "entire" << "aroundnodes" << "aroundblast" << "depthrange";
QString error;
error = checkOptionForString("--scope", arguments, validScopeOptions); if (error.length() > 0) return error;
error = checkOptionForString("--nodes", arguments, QStringList(), "a list of node names"); if (error.length() > 0) return error;
checkOptionWithoutValue("--partial", arguments);
error = checkOptionForInt("--distance", arguments, g_settings->nodeDistance, false); if (error.length() > 0) return error;
error = checkOptionForFloat("--mindepth", arguments, g_settings->minDepthRange, false); if (error.length() > 0) return error;
error = checkOptionForFloat("--maxdepth", arguments, g_settings->maxDepthRange, false); if (error.length() > 0) return error;
if (isOptionPresent("--query", arguments) && g_memory->commandLineCommand == NO_COMMAND) return "A graph must be given (e.g. via Bandage load) to use the --query option";
error = checkOptionForFile("--query", arguments); if (error.length() > 0) return error;
error = checkOptionForString("--blastp", arguments, QStringList(), "blastn/tblastn parameters"); if (error.length() > 0) return error;
checkOptionWithoutValue("--double", arguments);
error = checkOptionForFloat("--nodelen", arguments, g_settings->manualNodeLengthPerMegabase, false); if (error.length() > 0) return error;
error = checkOptionForFloat("--minnodlen", arguments, g_settings->minimumNodeLength, false); if (error.length() > 0) return error;
error = checkOptionForFloat("--edgelen", arguments, g_settings->edgeLength, false); if (error.length() > 0) return error;
error = checkOptionForFloat("--doubsep", arguments, g_settings->doubleModeNodeSeparation, false); if (error.length() > 0) return error;
error = checkOptionForInt("--iter", arguments, g_settings->graphLayoutQuality, false); if (error.length() > 0) return error;
checkOptionWithoutValue("--linear", arguments);
error = checkOptionForFloat("--nodseglen", arguments, g_settings->nodeSegmentLength, false); if (error.length() > 0) return error;
error = checkOptionForFloat("--nodewidth", arguments, g_settings->averageNodeWidth, false); if (error.length() > 0) return error;
error = checkOptionForFloat("--depwidth", arguments, g_settings->depthEffectOnWidth, false); if (error.length() > 0) return error;
error = checkOptionForFloat("--deppower", arguments, g_settings->depthPower, false); if (error.length() > 0) return error;
error = checkOptionForFloat("--edgewidth", arguments, g_settings->edgeWidth, false); if (error.length() > 0) return error;
error = checkOptionForFloat("--outline", arguments, g_settings->outlineThickness, false); if (error.length() > 0) return error;
checkOptionWithoutValue("--names", arguments);
checkOptionWithoutValue("--lengths", arguments);
checkOptionWithoutValue("--depth", arguments);
checkOptionWithoutValue("--blasthits", arguments);
error = checkOptionForInt("--fontsize", arguments, IntSetting(0, 1, 100), false); if (error.length() > 0) return error;
error = checkOptionForFloat("--toutline", arguments, g_settings->textOutlineThickness, false); if (error.length() > 0) return error;
checkOptionWithoutValue("--centre", arguments);
error = checkOptionForColour("--edgecol", arguments); if (error.length() > 0) return error;
error = checkOptionForColour("--outcol", arguments); if (error.length() > 0) return error;
error = checkOptionForColour("--selcol", arguments); if (error.length() > 0) return error;
error = checkOptionForColour("--textcol", arguments); if (error.length() > 0) return error;
error = checkOptionForColour("--toutcol", arguments); if (error.length() > 0) return error;
checkOptionWithoutValue("--noaa", arguments);
checkOptionWithoutValue("--singlearr", arguments);
QStringList validColourOptions;
validColourOptions << "random" << "uniform" << "depth" << "blastsolid" << "blastrainbow" << "custom";
error = checkOptionForString("--colour", arguments, validColourOptions); if (error.length() > 0) return error;
error = checkOptionForInt("--ransatpos", arguments, g_settings->randomColourPositiveSaturation, false); if (error.length() > 0) return error;
error = checkOptionForInt("--ransatneg", arguments, g_settings->randomColourNegativeSaturation, false); if (error.length() > 0) return error;
error = checkOptionForInt("--ranligpos", arguments, g_settings->randomColourPositiveLightness, false); if (error.length() > 0) return error;
error = checkOptionForInt("--ranligneg", arguments, g_settings->randomColourNegativeLightness, false); if (error.length() > 0) return error;
error = checkOptionForInt("--ranopapos", arguments, g_settings->randomColourPositiveOpacity, false); if (error.length() > 0) return error;
error = checkOptionForInt("--ranopaneg", arguments, g_settings->randomColourNegativeOpacity, false); if (error.length() > 0) return error;
error = checkOptionForColour("--unicolpos", arguments); if (error.length() > 0) return error;
error = checkOptionForColour("--unicolneg", arguments); if (error.length() > 0) return error;
error = checkOptionForColour("--unicolspe", arguments); if (error.length() > 0) return error;
error = checkOptionForColour("--depcollow", arguments); if (error.length() > 0) return error;
error = checkOptionForColour("--depcolhi", arguments); if (error.length() > 0) return error;
error = checkTwoOptionsForFloats("--depvallow", "--depvalhi", arguments, g_settings->lowDepthValue, g_settings->highDepthValue, true); if (error.length() > 0) return error;
error = checkOptionForInt("--pathnodes", arguments, g_settings->maxQueryPathNodes, false); if (error.length() > 0) return error;
error = checkOptionForFloat("--minpatcov", arguments, g_settings->minQueryCoveredByPath, false); if (error.length() > 0) return error;
error = checkOptionForFloat("--minhitcov", arguments, g_settings->minQueryCoveredByHits, true); if (error.length() > 0) return error;
error = checkOptionForFloat("--minmeanid", arguments, g_settings->minMeanHitIdentity, true); if (error.length() > 0) return error;
error = checkOptionForSciNot("--maxevprod", arguments, g_settings->maxEValueProduct, true); if (error.length() > 0) return error;
error = checkOptionForFloat("--minpatlen", arguments, g_settings->minLengthPercentage, true); if (error.length() > 0) return error;
error = checkOptionForFloat("--maxpatlen", arguments, g_settings->maxLengthPercentage, true); if (error.length() > 0) return error;
error = checkOptionForInt("--minlendis", arguments, g_settings->minLengthBaseDiscrepancy, true); if (error.length() > 0) return error;
error = checkOptionForInt("--maxlendis", arguments, g_settings->maxLengthBaseDiscrepancy, true); if (error.length() > 0) return error;
error = checkOptionForInt("--alfilter", arguments, g_settings->blastAlignmentLengthFilter, false); if (error.length() > 0) return error;
error = checkOptionForFloat("--qcfilter", arguments, g_settings->blastQueryCoverageFilter, false); if (error.length() > 0) return error;
error = checkOptionForFloat("--ifilter", arguments, g_settings->blastIdentityFilter, false); if (error.length() > 0) return error;
error = checkOptionForSciNot("--evfilter", arguments, g_settings->blastEValueFilter, false); if (error.length() > 0) return error;
error = checkOptionForFloat("--bsfilter", arguments, g_settings->blastBitScoreFilter, false); if (error.length() > 0) return error;
//Make sure that the min depth is less than or equal to the max read
//depth.
double minDepth = g_settings->minDepthRange;
double maxDepth = g_settings->maxDepthRange;
if (isOptionPresent("--mindepth", &argumentsCopy))
minDepth = getFloatOption("--mindepth", &argumentsCopy);
if (isOptionPresent("--maxdepth", &argumentsCopy))
maxDepth = getFloatOption("--maxdepth", &argumentsCopy);
if (minDepth > maxDepth)
return "the maximum depth must be greater than or equal to the minimum depth.";
//Make sure that the min path length is less than or equal to the max path
//length.
bool minLengthPercentageOn = g_settings->minLengthPercentage.on;
bool maxLengthPercentageOn = g_settings->maxLengthPercentage.on;
double minLengthPercentage = g_settings->minLengthPercentage;
double maxLengthPercentage = g_settings->maxLengthPercentage;
if (isOptionPresent("--minpatlen", &argumentsCopy))
{
QString optionString = getStringOption("--minpatlen", &argumentsCopy);
if (optionString.toLower() == "off")
minLengthPercentageOn = false;
else
{
minLengthPercentageOn = true;
minLengthPercentage = getFloatOption("--minpatlen", &argumentsCopy);
}
}
if (isOptionPresent("--maxpatlen", &argumentsCopy))
{
QString optionString = getStringOption("--maxpatlen", &argumentsCopy);
if (optionString.toLower() == "off")
maxLengthPercentageOn = false;
else
{
maxLengthPercentageOn = true;
maxLengthPercentage = getFloatOption("--maxpatlen", &argumentsCopy);
}
}
if (minLengthPercentageOn && maxLengthPercentageOn &&
minLengthPercentage > maxLengthPercentage)
return "the maximum BLAST query path length discrepancy must be greater than or equal to the minimum length discrepancy.";
//Make sure that the min length discrepancy is less than or equal to the max
//length discrepancy.
bool minLengthBaseDiscrepancyOn = g_settings->minLengthBaseDiscrepancy.on;
bool maxLengthBaseDiscrepancyOn = g_settings->maxLengthBaseDiscrepancy.on;
int minLengthBaseDiscrepancy = g_settings->minLengthBaseDiscrepancy;
int maxLengthBaseDiscrepancy = g_settings->maxLengthBaseDiscrepancy;
if (isOptionPresent("--minlendis", &argumentsCopy))
{
QString optionString = getStringOption("--minlendis", &argumentsCopy);
if (optionString.toLower() == "off")
minLengthBaseDiscrepancyOn = false;
else
{
minLengthBaseDiscrepancyOn = true;
minLengthBaseDiscrepancy = getIntOption("--minlendis", &argumentsCopy);
}
}
if (isOptionPresent("--maxlendis", &argumentsCopy))
{
QString optionString = getStringOption("--maxlendis", &argumentsCopy);
if (optionString.toLower() == "off")
g_settings->maxLengthBaseDiscrepancy.on = false;
else
{
maxLengthBaseDiscrepancyOn = true;
maxLengthBaseDiscrepancy = getIntOption("--maxlendis", &argumentsCopy);
}
}
if (minLengthBaseDiscrepancyOn && maxLengthBaseDiscrepancyOn &&
minLengthBaseDiscrepancy > maxLengthBaseDiscrepancy)
return "the maximum BLAST query path length discrepancy must be greater than or equal to the minimum length discrepancy.";
bool blastScope = isOptionAndValuePresent("--scope", "aroundblast", &argumentsCopy);
bool queryFile = isOptionPresent("--query", &argumentsCopy);
if (blastScope && !queryFile)
return "A BLAST query must be given with the --query option when the\naroundblast scope is used.";
bool nodesScope = isOptionAndValuePresent("--scope", "aroundnodes", &argumentsCopy);
bool nodesList = isOptionPresent("--nodes", &argumentsCopy);
if (nodesScope && !nodesList)
return "A list of starting nodes must be given with the --nodes option\nwhen the aroundnodes scope is used.";
bool depthScope = isOptionAndValuePresent("--scope", "depthrange", &argumentsCopy);
bool minDepthPresent = isOptionPresent("--mindepth", &argumentsCopy);
bool maxDepthPresent = isOptionPresent("--maxdepth", &argumentsCopy);
if (depthScope && !(minDepthPresent && maxDepthPresent))
return "A depth range must be given with the --mindepth and\n--maxdepth options when the aroundnodes scope is used.";
return checkForExcessArguments(*arguments);
}
void parseSettings(QStringList arguments)
{
if (isOptionPresent("--scope", &arguments))
g_settings->graphScope = getGraphScopeOption("--scope", &arguments);
if (isOptionPresent("--distance", &arguments))
g_settings->nodeDistance = getIntOption("--distance", &arguments);
if (isOptionPresent("--mindepth", &arguments))
g_settings->minDepthRange = getFloatOption("--mindepth", &arguments);
if (isOptionPresent("--maxdepth", &arguments))
g_settings->maxDepthRange = getFloatOption("--maxdepth", &arguments);
if (isOptionPresent("--nodes", &arguments))
g_settings->startingNodes = getStringOption("--nodes", &arguments);
g_settings->startingNodesExactMatch = !isOptionPresent("--partial", &arguments);
if (isOptionPresent("--query", &arguments))
g_settings->blastQueryFilename = getStringOption("--query", &arguments);
if (isOptionPresent("--blastp", &arguments))
g_settings->blastSearchParameters = getStringOption("--blastp", &arguments);
g_settings->doubleMode = isOptionPresent("--double", &arguments);
if (isOptionPresent("--nodelen", &arguments))
{
g_settings->manualNodeLengthPerMegabase = getIntOption("--nodelen", &arguments);
g_settings->nodeLengthMode = MANUAL_NODE_LENGTH;
}
if (isOptionPresent("--edgelen", &arguments))
g_settings->edgeLength = getFloatOption("--edgelen", &arguments);
if (isOptionPresent("--iter", &arguments))
{
int quality = getIntOption("--iter", &arguments);
if (quality < 0)
quality = 0;
if (quality > 4)
quality = 4;
g_settings->graphLayoutQuality = quality;
}
g_settings->linearLayout = isOptionPresent("--linear", &arguments);
if (isOptionPresent("--nodseglen", &arguments))
g_settings->nodeSegmentLength = getFloatOption("--nodseglen", &arguments);
if (isOptionPresent("--nodewidth", &arguments))
g_settings->averageNodeWidth = getFloatOption("--nodewidth", &arguments);
if (isOptionPresent("--depwidth", &arguments))
g_settings->depthEffectOnWidth = getFloatOption("--depwidth", &arguments);
if (isOptionPresent("--deppower", &arguments))
g_settings->depthPower = getFloatOption("--deppower", &arguments);
if (isOptionPresent("--edgewidth", &arguments))
g_settings->edgeWidth = getFloatOption("--edgewidth", &arguments);
if (isOptionPresent("--outline", &arguments))
g_settings->outlineThickness = getFloatOption("--outline", &arguments);
g_settings->antialiasing = !isOptionPresent("--noaa", &arguments);
g_settings->arrowheadsInSingleMode = isOptionPresent("--singlearr", &arguments);
if (isOptionPresent("--edgecol", &arguments))
g_settings->edgeColour = getColourOption("--edgecol", &arguments);
if (isOptionPresent("--outcol", &arguments))
g_settings->outlineColour = getColourOption("--outcol", &arguments);
if (isOptionPresent("--selcol", &arguments))
g_settings->selectionColour = getColourOption("--selcol", &arguments);
if (isOptionPresent("--textcol", &arguments))
g_settings->textColour = getColourOption("--textcol", &arguments);
if (isOptionPresent("--toutcol", &arguments))
g_settings->textOutlineColour = getColourOption("--toutcol", &arguments);
g_settings->positionTextNodeCentre = isOptionPresent("--centre", &arguments);
g_settings->displayNodeNames = isOptionPresent("--names", &arguments);
g_settings->displayNodeLengths = isOptionPresent("--lengths", &arguments);
g_settings->displayNodeDepth = isOptionPresent("--depth", &arguments);
g_settings->displayBlastHits = isOptionPresent("--blasthits", &arguments);
if (isOptionPresent("--fontsize", &arguments))
{
int fontsize = getIntOption("--fontsize", &arguments);
QFont font = g_settings->labelFont;
font.setPointSize(fontsize);
g_settings->labelFont = font;
}
if (isOptionPresent("--toutline", &arguments))
{
double textOutlineThickness = getFloatOption("--toutline", &arguments);
if (textOutlineThickness == 0.0)
g_settings->textOutline = false;
else
{
g_settings->textOutline = true;
g_settings->textOutlineThickness = textOutlineThickness;
}
}
g_settings->nodeColourScheme = getColourSchemeOption("--colour", &arguments);
if (isOptionPresent("--ransatpos", &arguments))
g_settings->randomColourPositiveSaturation = getIntOption("--ransatpos", &arguments);
if (isOptionPresent("--ransatneg", &arguments))
g_settings->randomColourNegativeSaturation = getIntOption("--ransatneg", &arguments);
if (isOptionPresent("--ranligpos", &arguments))
g_settings->randomColourPositiveLightness = getIntOption("--ranligpos", &arguments);
if (isOptionPresent("--ranligneg", &arguments))
g_settings->randomColourNegativeLightness = getIntOption("--ranligneg", &arguments);
if (isOptionPresent("--ranopapos", &arguments))
g_settings->randomColourPositiveOpacity = getIntOption("--ranopapos", &arguments);
if (isOptionPresent("--ranopaneg", &arguments))
g_settings->randomColourNegativeOpacity = getIntOption("--ranopaneg", &arguments);
if (isOptionPresent("--unicolpos", &arguments))
g_settings->uniformPositiveNodeColour = getColourOption("--unicolpos", &arguments);
if (isOptionPresent("--unicolneg", &arguments))
g_settings->uniformNegativeNodeColour = getColourOption("--unicolneg", &arguments);
if (isOptionPresent("--unicolspe", &arguments))
g_settings->uniformNodeSpecialColour = getColourOption("--unicolspe", &arguments);
if (isOptionPresent("--depcollow", &arguments))
g_settings->lowDepthColour = getColourOption("--depcollow", &arguments);
if (isOptionPresent("--depcolhi", &arguments))
g_settings->highDepthColour = getColourOption("--depcolhi", &arguments);
if (isOptionPresent("--depvallow", &arguments))
{
g_settings->lowDepthValue = getFloatOption("--depvallow", &arguments);
g_settings->autoDepthValue = false;
}
if (isOptionPresent("--depvalhi", &arguments))
{
g_settings->highDepthValue = getFloatOption("--depvalhi", &arguments);
g_settings->autoDepthValue = false;
}
if (isOptionPresent("--pathnodes", &arguments))
g_settings->maxQueryPathNodes = getIntOption("--pathnodes", &arguments);
if (isOptionPresent("--minpatcov", &arguments))
g_settings->minQueryCoveredByPath = getFloatOption("--minpatcov", &arguments);
if (isOptionPresent("--minhitcov", &arguments))
{
QString optionString = getStringOption("--minhitcov", &arguments);
if (optionString.toLower() == "off")
g_settings->minQueryCoveredByHits.on = false;
else
{
g_settings->minQueryCoveredByHits.on = true;
g_settings->minQueryCoveredByHits = getFloatOption("--minhitcov", &arguments);
}
}
if (isOptionPresent("--minmeanid", &arguments))
{
QString optionString = getStringOption("--minmeanid", &arguments);
if (optionString.toLower() == "off")
g_settings->minMeanHitIdentity.on = false;
else
{
g_settings->minMeanHitIdentity.on = true;
g_settings->minMeanHitIdentity = getFloatOption("--minmeanid", &arguments);
}
}
if (isOptionPresent("--maxevprod", &arguments))
{
QString optionString = getStringOption("--maxevprod", &arguments);
if (optionString.toLower() == "off")
g_settings->maxEValueProduct.on = false;
else
{
g_settings->maxEValueProduct.on = true;
g_settings->maxEValueProduct = getSciNotOption("--maxevprod", &arguments);
}
}
if (isOptionPresent("--minpatlen", &arguments))
{
QString optionString = getStringOption("--minpatlen", &arguments);
if (optionString.toLower() == "off")
g_settings->minLengthPercentage.on = false;
else
{
g_settings->minLengthPercentage.on = true;
g_settings->minLengthPercentage = getFloatOption("--minpatlen", &arguments);
}
}
if (isOptionPresent("--maxpatlen", &arguments))
{
QString optionString = getStringOption("--maxpatlen", &arguments);
if (optionString.toLower() == "off")
g_settings->maxLengthPercentage.on = false;
else
{
g_settings->maxLengthPercentage.on = true;
g_settings->maxLengthPercentage = getFloatOption("--maxpatlen", &arguments);
}
}
if (isOptionPresent("--minlendis", &arguments))
{
QString optionString = getStringOption("--minlendis", &arguments);
if (optionString.toLower() == "off")
g_settings->minLengthBaseDiscrepancy.on = false;
else
{
g_settings->minLengthBaseDiscrepancy.on = true;
g_settings->minLengthBaseDiscrepancy = getIntOption("--minlendis", &arguments);
}
}
if (isOptionPresent("--maxlendis", &arguments))
{
QString optionString = getStringOption("--maxlendis", &arguments);
if (optionString.toLower() == "off")
g_settings->maxLengthBaseDiscrepancy.on = false;
else
{
g_settings->maxLengthBaseDiscrepancy.on = true;
g_settings->maxLengthBaseDiscrepancy = getIntOption("--maxlendis", &arguments);
}
}
if (isOptionPresent("--alfilter", &arguments))
{
g_settings->blastAlignmentLengthFilter.on = true;
g_settings->blastAlignmentLengthFilter = getIntOption("--alfilter", &arguments);
}
if (isOptionPresent("--qcfilter", &arguments))
{
g_settings->blastQueryCoverageFilter.on = true;
g_settings->blastQueryCoverageFilter = getFloatOption("--qcfilter", &arguments);
}
if (isOptionPresent("--ifilter", &arguments))
{
g_settings->blastIdentityFilter.on = true;
g_settings->blastIdentityFilter = getFloatOption("--ifilter", &arguments);
}
if (isOptionPresent("--evfilter", &arguments))
{
g_settings->blastEValueFilter.on = true;
g_settings->blastEValueFilter = getSciNotOption("--evfilter", &arguments);
}
if (isOptionPresent("--bsfilter", &arguments))
{
g_settings->blastBitScoreFilter.on = true;
g_settings->blastBitScoreFilter = getFloatOption("--bsfilter", &arguments);
}
}
bool checkForHelp(QStringList arguments)
{
int h1 = arguments.indexOf("-h");
int h2 = arguments.indexOf("-help");
int h3 = arguments.indexOf("--help");
return (h1 != -1 || h2 != -1 || h3 != -1);
}
bool checkForHelpAll(QStringList arguments)
{
return (arguments.indexOf("--helpall") != -1);
}
bool checkForVersion(QStringList arguments)
{
int v1 = arguments.indexOf("-v");
int v2 = arguments.indexOf("-version");
int v3 = arguments.indexOf("--version");
return (v1 != -1 || v2 != -1 || v3 != -1);
}
//This function checks the value for an integer-accepting command line option.
//If offOkay is true, then it will also accept "off" as a valid argument.
//Returns empty string if everything is okay and an error message if there's a
//problem. If everything is okay, it also removes the option and its value from
//arguments.
QString checkOptionForInt(QString option, QStringList * arguments, IntSetting setting, bool offOkay)
{
int optionIndex = arguments->indexOf(option);
//If the option isn't found, that's fine.
if (optionIndex == -1)
return "";
int integerIndex = optionIndex + 1;
//If nothing follows the option, that's a problem.
if (integerIndex >= arguments->size())
return option + " must be followed by an integer";
//If the thing following the option isn't an integer, that's a problem.
bool optionIsInt;
int optionInt = arguments->at(integerIndex).toInt(&optionIsInt);
bool optionIsOff = arguments->at(integerIndex).toLower() == "off";
if (offOkay && !(optionIsInt || optionIsOff))
return option + " must be followed by an integer or \"off\"";
if (!offOkay && !optionIsInt)
return option + " must be followed by an integer";
//Check the range of the option.
if (optionIsInt)
{
if (optionInt < setting.min || optionInt > setting.max)
return "Value of " + option + " must be between "
+ QString::number(setting.min) + " and " + QString::number(setting.max) +
" (inclusive)";
}
//If the code got here, the option and its integer are okay.
//Remove them from the arguments.
arguments->removeAt(integerIndex);
arguments->removeAt(optionIndex);
return "";
}
//This function checks the value for a float-accepting command line option.
//If offOkay is true, then it will also accept "off" as a valid argument.
//Returns empty string if everything is okay and an error message if there's a
//problem. If everything is okay, it also removes the option and its value from
//arguments.
QString checkOptionForFloat(QString option, QStringList * arguments, FloatSetting setting, bool offOkay)
{
int optionIndex = arguments->indexOf(option);
//If the option isn't found, that's fine.
if (optionIndex == -1)
return "";
int floatIndex = optionIndex + 1;
//If nothing follows the option, that's a problem.
if (floatIndex >= arguments->size())
return option + " must be followed by a number";
//If the thing following the option isn't a number, that's a problem.
bool optionIsFloat;
double optionFloat = arguments->at(floatIndex).toDouble(&optionIsFloat);
bool optionIsOff = arguments->at(floatIndex).toLower() == "off";
if (offOkay && !(optionIsFloat || optionIsOff))
return option + " must be followed by a number or \"off\"";
if (!offOkay && !optionIsFloat)
return option + " must be followed by a number";
//Check the range of the option.
if (optionIsFloat)
{
if (optionFloat < setting.min || optionFloat > setting.max)
return "Value of " + option + " must be between "
+ QString::number(setting.min) + " and " + QString::number(setting.max) +
" (inclusive)";
}
//If the code got here, the option and its number are okay.
//Remove them from the arguments.
arguments->removeAt(floatIndex);
arguments->removeAt(optionIndex);
return "";
}
//This function checks the value for a scientific notation-accepting command
//line option. If offOkay is true, then it will also accept "off" as a valid
//argument.
//Returns empty string if everything is okay and an error message if there's a
//problem. If everything is okay, it also removes the option and its value from
//arguments.
QString checkOptionForSciNot(QString option, QStringList * arguments,
SciNotSetting setting, bool offOkay)
{
int optionIndex = arguments->indexOf(option);
//If the option isn't found, that's fine.
if (optionIndex == -1)
return "";
int sciNotIndex = optionIndex + 1;
//If nothing follows the option, that's a problem.
if (sciNotIndex >= arguments->size())
return option + " must be followed by a number in scientific notation";
//If the thing following the option isn't a number in scientific notation or
//"off", that's a problem.
bool optionIsSciNot = SciNot::isValidSciNotString(arguments->at(sciNotIndex));
bool optionIsOff = arguments->at(sciNotIndex).toLower() == "off";
if (offOkay && !(optionIsSciNot || optionIsOff))
return option + " must be followed by a number in scientific notation or \"off\"";
if (!offOkay && !optionIsSciNot)
return option + " must be followed by a number in scientific notation";
SciNot optionSciNot = SciNot(arguments->at(sciNotIndex));
//Check the range of the option.
if (optionIsSciNot)
{
if (optionSciNot < setting.min || optionSciNot > setting.max)
return "Value of " + option + " must be between "
+ setting.min.asString(true) + " and " + setting.max.asString(true) +
" (inclusive)";
}
//If the code got here, the option and its number are okay.
//Remove them from the arguments.
arguments->removeAt(sciNotIndex);
arguments->removeAt(optionIndex);
return "";
}
//Returns empty string if everything is okay and an error
//message if there's a problem. If everything is okay, it
//also removes the option and its value from arguments.
QString checkOptionForString(QString option, QStringList * arguments, QStringList validOptionsList, QString validDescription)
{
int optionIndex = arguments->indexOf(option);
//If the option isn't found, that's fine.
if (optionIndex == -1)
return "";
int stringIndex = optionIndex + 1;
QString validOptions;
for (int i = 0; i < validOptionsList.size(); ++i)
{
validOptions += validOptionsList.at(i);
if (i == validOptionsList.size() - 2)
validOptions += " or ";
else if (i < validOptionsList.size() - 2)
validOptions += ", ";
}
if (validOptions == "")
validOptions = validDescription;
//If nothing follows the option, that's a problem.
if (stringIndex >= arguments->size())
return option + " must be followed by " + validOptions;
//If the thing following the option isn't a valid choice, that's a problem.
if (validOptionsList.size() > 0)
{
QString value = arguments->at(stringIndex);
if (!validOptionsList.contains(value, Qt::CaseInsensitive))
return option + " must be followed by " + validOptions;
}
//If the code got here, the option and its string are okay.
//Remove them from the arguments.
arguments->removeAt(stringIndex);
arguments->removeAt(optionIndex);
return "";
}
QString checkOptionForColour(QString option, QStringList * arguments)
{
int optionIndex = arguments->indexOf(option);
//If the option isn't found, that's fine.
if (optionIndex == -1)
return "";
int colIndex = optionIndex + 1;
//If nothing follows the option, that's a problem.
if (colIndex >= arguments->size())
return option + " must be followed by a 6-digit hex colour (e.g. #FFB6C1), an 8-digit hex colour (e.g. #7FD2B48C) or a standard colour name (e.g. skyblue)";
//If the thing following the option isn't a colour, that's a problem.
QColor colour(arguments->at(colIndex));
if (!colour.isValid())
return option + " must be followed by a 6-digit hex colour (e.g. #FFB6C1), an 8-digit hex colour (e.g. #7FD2B48C) or a standard colour name (e.g. skyblue)";
//If the code got here, the option and its colour are okay.
//Remove them from the arguments.
arguments->removeAt(colIndex);
arguments->removeAt(optionIndex);
return "";
}
QString checkOptionForFile(QString option, QStringList * arguments)
{
int optionIndex = arguments->indexOf(option);
//If the option isn't found, that's fine.
if (optionIndex == -1)
return "";
int fileIndex = optionIndex + 1;
//If nothing follows the option, that's a problem.
if (fileIndex >= arguments->size())
return option + " must be followed by a filename";
//If the thing that follows the option isn't a file that's a problem
if (!checkIfFileExists(arguments->at(fileIndex)))
return option + " must be followed by a valid filename";
//If the code got here, the option and its file are okay.
//Remove them from the arguments.
arguments->removeAt(fileIndex);
arguments->removeAt(optionIndex);
return "";
}
bool checkIfFileExists(QString filename)
{
QFileInfo checkFile(filename);
return (checkFile.exists() && checkFile.isFile());
}
//This function simply removes an option from arguments if it is found.
void checkOptionWithoutValue(QString option, QStringList * arguments)
{
int optionIndex = arguments->indexOf(option);
//If the option isn't found, that's fine.
if (optionIndex == -1)
return ;
//If the code got here, the option is okay.
//Remove it from the arguments.
arguments->removeAt(optionIndex);
}
//This function checks to make sure either both or neither of the options
//are used. It can also optionally check to make sure the second is larger
//than the first.
QString checkTwoOptionsForFloats(QString option1, QString option2, QStringList * arguments,
FloatSetting setting1, FloatSetting setting2,
bool secondMustBeEqualOrLarger)
{
//First check each option independently
QStringList argumentsCopy = *arguments;
QString option1Error = checkOptionForFloat(option1, &argumentsCopy, setting1, false);
if (option1Error != "")
return option1Error;
QString option2Error = checkOptionForFloat(option2, &argumentsCopy, setting2, false);
if (option2Error != "")
return option2Error;
//Now make sure either both or neither are present.
if (isOptionPresent(option1, arguments) != isOptionPresent(option2, arguments))
return option1 + " and " + option2 + " must be used together";
if (secondMustBeEqualOrLarger)
{
if (getFloatOption(option2, arguments) < getFloatOption(option1, arguments))
return option2 + " must be greater than or equal to " + option1;
}
//Now remove the options from the arguments before finishing.
checkOptionForFloat(option1, arguments, setting1, false);
checkOptionForFloat(option2, arguments, setting2, false);
return "";
}
bool isOptionPresent(QString option, QStringList * arguments)
{
return (arguments->indexOf(option) > -1);
}
bool isOptionAndValuePresent(QString option, QString value, QStringList * arguments)
{
int optionIndex = arguments->indexOf(option);
if (optionIndex == -1)
return false;
int valueIndex = optionIndex + 1;
if (valueIndex >= arguments->size())
return false;
QString optionValue = arguments->at(valueIndex);
return (optionValue == value);
}
int getIntOption(QString option, QStringList * arguments)
{
int optionIndex = arguments->indexOf(option);
if (optionIndex == -1)
return 0;
int integerIndex = optionIndex + 1;
if (integerIndex >= arguments->size())
return 0;
return arguments->at(integerIndex).toInt();
}
double getFloatOption(QString option, QStringList * arguments)
{
int optionIndex = arguments->indexOf(option);
if (optionIndex == -1)
return 0;
int floatIndex = optionIndex + 1;
if (floatIndex >= arguments->size())
return 0;
return arguments->at(floatIndex).toDouble();
}
SciNot getSciNotOption(QString option, QStringList * arguments)
{
int optionIndex = arguments->indexOf(option);
if (optionIndex == -1)
return 0;
int sciNotIndex = optionIndex + 1;
if (sciNotIndex >= arguments->size())
return SciNot();
return SciNot(arguments->at(sciNotIndex));
}
NodeColourScheme getColourSchemeOption(QString option, QStringList * arguments)
{
NodeColourScheme defaultScheme = RANDOM_COLOURS;
if (isOptionPresent("--query", arguments))
defaultScheme = BLAST_HITS_SOLID_COLOUR;
int optionIndex = arguments->indexOf(option);
if (optionIndex == -1)
return defaultScheme;
int colourIndex = optionIndex + 1;
if (colourIndex >= arguments->size())
return defaultScheme;
QString colourString = arguments->at(colourIndex).toLower();
if (colourString == "random")
return RANDOM_COLOURS;
else if (colourString == "uniform")
return UNIFORM_COLOURS;
else if (colourString == "depth")
return DEPTH_COLOUR;
else if (colourString == "blastsolid")
return BLAST_HITS_SOLID_COLOUR;
else if (colourString == "blastrainbow")
return BLAST_HITS_RAINBOW_COLOUR;
else if (colourString == "custom")
return CUSTOM_COLOURS;
//Random colours is the default
return defaultScheme;
}
GraphScope getGraphScopeOption(QString option, QStringList * arguments)
{
int optionIndex = arguments->indexOf(option);
if (optionIndex == -1)
return WHOLE_GRAPH;
int scopeIndex = optionIndex + 1;
if (scopeIndex >= arguments->size())
return WHOLE_GRAPH;
QString scopeString = arguments->at(scopeIndex).toLower();
if (scopeString == "entire")
return WHOLE_GRAPH;
else if (scopeString == "aroundnodes")
return AROUND_NODE;
else if (scopeString == "aroundblast")
return AROUND_BLAST_HITS;
else if (scopeString == "depthrange")
return DEPTH_RANGE;
//Entire graph scope is the default.
return WHOLE_GRAPH;
}
QColor getColourOption(QString option, QStringList * arguments)
{
int optionIndex = arguments->indexOf(option);
if (optionIndex == -1)
return QColor();
int colIndex = optionIndex + 1;
if (colIndex >= arguments->size())
return QColor();
return QColor(arguments->at(colIndex));
}
QString getStringOption(QString option, QStringList * arguments)
{
int optionIndex = arguments->indexOf(option);
if (optionIndex == -1)
return "";
int stringIndex = optionIndex + 1;
if (stringIndex >= arguments->size())
return "";
return arguments->at(stringIndex);
}
//This function generates an error if excess arguments are left after
//parsing.
QString checkForExcessArguments(QStringList arguments)
{
if (arguments.size() == 0)
return "";
QString invalidOptionText = "Invalid option";
if (arguments.size() > 1)
invalidOptionText += "s";
invalidOptionText += ": ";
for (int i = 0; i < arguments.size(); ++i)
{
invalidOptionText += arguments.at(i);
if (i < arguments.size() - 1)
invalidOptionText += ", ";
}
return invalidOptionText;
}
void getCommonHelp(QStringList * text)
{
*text << "--help View this help message";
*text << "--helpall View all command line settings";
*text << "--version View Bandage version number";
*text << "";
}
bool createBlastTempDirectory()
{
//Running from the command line, it makes more sense to put the temp
//directory in the current directory.
g_blastSearch->m_tempDirectory = "bandage_temp-" + QString::number(QApplication::applicationPid()) + "/";
if (!QDir().mkdir(g_blastSearch->m_tempDirectory))
return false;
g_blastSearch->m_blastQueries.createTempQueryFiles();
return true;
}
void deleteBlastTempDirectory()
{
if (g_blastSearch->m_tempDirectory != "" &&
QDir(g_blastSearch->m_tempDirectory).exists() &&
QDir(g_blastSearch->m_tempDirectory).dirName().contains("bandage_temp"))
QDir(g_blastSearch->m_tempDirectory).removeRecursively();
}
QString getElapsedTime(QDateTime start, QDateTime end)
{
int msecElapsed = start.msecsTo(end);
int secElapsed = msecElapsed / 1000;
msecElapsed = msecElapsed % 1000;
int minElapsed = secElapsed / 60;
secElapsed = secElapsed % 60;
int hoursElapsed = minElapsed / 60;
minElapsed = minElapsed % 60;
QString msecString = QString("%1").arg(msecElapsed, 2, 10, QChar('0'));
QString secString = QString("%1").arg(secElapsed, 2, 10, QChar('0'));
QString minString = QString("%1").arg(minElapsed, 2, 10, QChar('0'));
QString hourString = QString("%1").arg(hoursElapsed, 2, 10, QChar('0'));
return hourString + ":" + minString + ":" + secString + "." + msecString;
}
QStringList wrapText(QString text, int width, int firstLineIndent, int laterLineIndent)
{
QStringList returnList;
QString firstLineSpaces = "";
for (int i = 0; i < firstLineIndent; ++i)
firstLineSpaces += ' ';
QString laterLineSpaces = "";
for (int i = 0; i < laterLineIndent; ++i)
laterLineSpaces += ' ';
text = firstLineSpaces + text;
//If the terminal width is at the minimum, don't bother wrapping.
if (g_memory->terminalWidth <= 50)
{
returnList << text;
return returnList;
}
while (text.length() > width)
{
QString leftString = text.left(width);
int spaceIndex = leftString.lastIndexOf(' ');
if (spaceIndex < width / 2)
spaceIndex = width;
leftString = text.left(spaceIndex);
returnList << rstrip(leftString);
text = laterLineSpaces + text.mid(spaceIndex).trimmed();
}
returnList << text;
return returnList;
}
//http://stackoverflow.com/questions/8215303/how-do-i-remove-trailing-whitespace-from-a-qstring
QString rstrip(const QString& str)
{
int n = str.size() - 1;
for (; n >= 0; --n)
{
if (!str.at(n).isSpace())
return str.left(n + 1);
}
return "";
}
QString getRangeAndDefault(IntSetting setting) {return (setting.on) ? getRangeAndDefault(setting.min, setting.max, setting.val) : getRangeAndDefault(setting.min, setting.max, "off");}
QString getRangeAndDefault(IntSetting setting, QString defaultVal) {return getRangeAndDefault(setting.min, setting.max, defaultVal);}
QString getRangeAndDefault(FloatSetting setting) {return (setting.on) ? getRangeAndDefault(setting.min, setting.max, setting.val) : getRangeAndDefault(setting.min, setting.max, "off");}
QString getRangeAndDefault(FloatSetting setting, QString defaultVal) {return getRangeAndDefault(setting.min, setting.max, defaultVal);}
QString getRangeAndDefault(SciNotSetting setting) {return (setting.on) ? getRangeAndDefault(setting.min.asString(true), setting.max.asString(true), setting.val.asString(true)) : getRangeAndDefault(setting.min.asString(true), setting.max.asString(true), "off");}
QString getRangeAndDefault(int min, int max, int defaultVal) { return getRangeAndDefault(double(min), double(max), QString::number(defaultVal));}
QString getRangeAndDefault(int min, int max, QString defaultVal) {return getRangeAndDefault(double(min), double(max), defaultVal);}
QString getRangeAndDefault(double min, double max, double defaultVal) {return getRangeAndDefault(min, max, QString::number(defaultVal));}
QString getRangeAndDefault(double min, double max, QString defaultVal) {return getRangeAndDefault(QString::number(min), QString::number(max), defaultVal);}
QString getRangeAndDefault(QString min, QString max, QString defaultVal)
{
return "(" + min + " to " + max + ", default: " + defaultVal + ")";
}
QString getDefaultColour(QColor colour)
{
return "(default: " + getColourName(colour.name()) + ")";
}
QString getBandageTitleAsciiArt()
{
return " ____ _ \n | _ \\ | | \n | |_) | __ _ _ __ __| | __ _ __ _ ___ \n | _ < / _` | '_ \\ / _` |/ _` |/ _` |/ _ \\\n | |_) | (_| | | | | (_| | (_| | (_| | __/\n |____/ \\__,_|_| |_|\\__,_|\\__,_|\\__, |\\___|\n __/ | \n |___/ ";
}
bool isOption(QString text)
{
bool option = (text.length() > 2 && text[0] == '-' && text[1] == '-' && text[2] != '-');
QRegularExpression rx("^[\\w ]+:");
return option || text.contains(rx);
}
bool isSectionHeader(QString text)
{
//Make an exception:
if (text.startsWith("Node widths are determined"))
return false;
QRegularExpression rx("^[\\w ]+:");
return text.contains(rx);
}
bool isListItem(QString text)
{
return (text.length() > 1 && text[0] == '*' && text[1] == ' ');
}
bool isCommand(QString text)
{
return text.startsWith("load ") ||
text.startsWith("info ") ||
text.startsWith("image ") ||
text.startsWith("querypaths ") ||
text.startsWith("reduce ");
}
bool isError(QString text)
{
return text.startsWith("Bandage error");
}
void getOnlineHelpMessage(QStringList * text)
{
*text << "Online Bandage help: https://github.com/rrwick/Bandage/wiki";
*text << "";
}