diff options
Diffstat (limited to 'src/dotfrontend.cpp')
-rw-r--r-- | src/dotfrontend.cpp | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/src/dotfrontend.cpp b/src/dotfrontend.cpp new file mode 100644 index 0000000..e0cfbed --- /dev/null +++ b/src/dotfrontend.cpp @@ -0,0 +1,297 @@ +/*************************************************************************** + * + * Copyright (C) 2006 Elad Lahav (elad_lahav@users.sf.net) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ***************************************************************************/ + +#include <qfileinfo.h> +#include <qpaintdevicemetrics.h> +#include <kmessagebox.h> +#include <klocale.h> +#include "dotfrontend.h" +#include "graphwidget.h" +#include "kscopeconfig.h" + +/** + * Class constructor. + * @param pGraph The graph widget on which to draw the output + */ +DotFrontend::DotFrontend(GraphWidget* pGraph) : Frontend(1), + m_pGraph(pGraph) +{ +} + +/** + * Class destructor. + */ +DotFrontend::~DotFrontend() +{ +} + +/** + * Executes dot on the goven input file. + * @param sFile The path to a temporary file holding the graph's + * description + * @return true if successful, false otherwise + */ +bool DotFrontend::run(const QString& sFile) +{ + QString sPath; + QStringList slArgs; + QPaintDeviceMetrics pdm(m_pGraph); + + // Set the horizontal and vertical DPI values + m_dDpiX = (double)pdm.logicalDpiX(); + m_dDpiY = (double)pdm.logicalDpiY(); + + // Make sure the executable exists + sPath = Config().getDotPath(); + + // Set the command line arguments + slArgs.append(sPath); + slArgs.append("-Tplain"); + slArgs.append(sFile); + + // Run a new process + if (!Frontend::run("dot", slArgs)) + return false; + + // Initialize stdout parsing + m_state = Graph; + m_delim = All; + + return true; +} + +/** + * Tests that the given file path leads to an executable. + * @param sPath The path to check + * @return true if the file in the given path exists and has executable + * permissions, false otherwise + */ +bool DotFrontend::verify(const QString& sPath) +{ + QFileInfo fi(sPath); + + if (!fi.exists() || !fi.isFile() || !fi.isExecutable() || + fi.fileName() != "dot") { + KMessageBox::error(0, i18n("Dot cannot be found in the given " + "path")); + return false; + } + + return true; +} + +#define PAD 5 + +/** + * Parses the output of a Dot process. + * @param sToken The current token read (the token delimiter is determined + * by the current state) + * @param delim The delimiter that ends this token + * @return A value indicating the way this token should be treated: dropped, + * added to the token queue, or finishes a new record + */ +Frontend::ParseResult DotFrontend::parseStdout(QString& sToken, + ParserDelim delim) +{ + static int nWidth, nHeight, nXpos, nYpos, nCurveSize, nCurveCount; + static QPointArray arrCurve; + static QString sNode, sEdgeHead, sEdgeTail; + ParseResult result = DiscardToken; + double dVal; + bool bOK; + + // Handle the token according to the current state + switch (m_state) { + case Graph: + if (sToken == "graph") + m_state = GraphScale; + break; + + case GraphScale: + sToken.toDouble(&bOK); + if (bOK) + m_state = GraphWidth; + break; + + case GraphWidth: + // Read and transform the graph's width + dVal = sToken.toDouble(&bOK); + if (bOK) { + nWidth = (int)(dVal * m_dDpiX) + (PAD * 2); + m_state = GraphHeight; + } + break; + + case GraphHeight: + // Read and transform the graph's height + dVal = sToken.toDouble(&bOK); + if (bOK) { + nHeight = (int)(dVal * m_dDpiY) + (PAD * 2); + + // Set the graph's size + m_pGraph->resize(nWidth, nHeight); + + m_state = NodeEdgeStop; + } + break; + + case NodeEdgeStop: + // "node" starts a new node + // "edge" starts a new edge + // "stop" ends this graph + if (sToken == "node") { + m_state = NodeName; + } + else if (sToken == "edge") { + m_state = EdgeHead; + } + else if (sToken == "stop") { + m_state = Graph; + } + break; + + case NodeName: + // Get a node's name + sNode = sToken; + m_state = NodeCentreX; + break; + + case NodeCentreX: + // Read and transform the node's centre location (X coordinate) + dVal = sToken.toDouble(&bOK); + if (bOK) { + nXpos = (int)(dVal * m_dDpiX) + PAD; + m_state = NodeCentreY; + } + break; + + case NodeCentreY: + // Read and transform the node's centre location (Y coordinate) + dVal = sToken.toDouble(&bOK); + if (bOK) { + nYpos = (int)(dVal * m_dDpiY) + PAD; + m_state = NodeWidth; + } + break; + + case NodeWidth: + // Read and transform the node's width + dVal = sToken.toDouble(&bOK); + if (bOK) { + nWidth = (int)(dVal * m_dDpiX); + m_state = NodeHeight; + } + break; + + case NodeHeight: + // Read and transform the node's height + dVal = sToken.toDouble(&bOK); + if (bOK) { + nHeight = (int)(dVal * m_dDpiY); + + // Create the bounding rectangle of the node + QRect rect; + rect.setX(nXpos - (nWidth / 2)); + rect.setY(nYpos - (nHeight / 2)); + rect.setWidth(nWidth); + rect.setHeight(nHeight); + + // Draw the node + m_pGraph->drawNode(sNode, rect); + + m_state = EndNodeEdge; + } + break; + + case EdgeHead: + // Get the edge's head node + sEdgeHead = sToken; + m_state = EdgeTail; + break; + + case EdgeTail: + // Get the edge's tail node + sEdgeTail = sToken; + m_state = EdgeCurveSize; + break; + + case EdgeCurveSize: + // Get the number of control points in the edge's spline + nCurveSize = sToken.toInt(&bOK); + if (bOK) { + arrCurve.resize(nCurveSize); + nCurveCount = 0; + m_state = EdgeCurveX; + } + break; + + case EdgeCurveX: + // Read and a control point (X coordinate) + dVal = sToken.toDouble(&bOK); + if (bOK) { + nXpos = (int)(dVal * m_dDpiX) + PAD; + m_state = EdgeCurveY; + } + break; + + case EdgeCurveY: + // Read and a control point (Y coordinate) + dVal = sToken.toDouble(&bOK); + if (bOK) { + nYpos = (int)(dVal * m_dDpiY) + PAD; + + // Add the control point to the spline array + arrCurve.setPoint(nCurveCount++, nXpos, nYpos); + + // Check if this is the last control point + if (nCurveCount == nCurveSize) { + // Draw the edge + m_pGraph->drawEdge(sEdgeHead, sEdgeTail, arrCurve); + + // Must detach from contents since a QPointArray shares data + arrCurve.detach(); + + m_state = EndNodeEdge; + } + else { + // Another control point available + m_state = EdgeCurveX; + } + } + break; + + case EndNodeEdge: + // Discard everything else on a node or edge line + if (delim == Newline) + m_state = NodeEdgeStop; + break; + } + + return result; +} + +#include "dotfrontend.moc" |