/*************************************************************************** * Copyright (c) 2015 Yorik van Havre * * Copyright (c) 2016 WandererFan * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library 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 Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include "PreCompiled.h" #ifndef _PreComp_ #include #include #include #endif #include #include #include #include #include #include "DrawUtil.h" #include "Preferences.h" #include "DrawViewSpreadsheet.h" using namespace TechDraw; //=========================================================================== // DrawViewSpreadsheet //=========================================================================== PROPERTY_SOURCE(TechDraw::DrawViewSpreadsheet, TechDraw::DrawViewSymbol) DrawViewSpreadsheet::DrawViewSpreadsheet() { static const char *vgroup = "Spreadsheet"; ADD_PROPERTY_TYPE(Source ,(nullptr), vgroup, App::Prop_None, "Spreadsheet to view"); Source.setScope(App::LinkScope::Global); ADD_PROPERTY_TYPE(CellStart ,("A1"), vgroup, App::Prop_None, "The top left cell of the range to display"); ADD_PROPERTY_TYPE(CellEnd ,("B2"), vgroup, App::Prop_None, "The bottom right cell of the range to display"); ADD_PROPERTY_TYPE(Font ,(Preferences::labelFont().c_str()), vgroup, App::Prop_None, "The name of the font to use"); ADD_PROPERTY_TYPE(TextColor, (0.0f, 0.0f, 0.0f), vgroup, App::Prop_None, "The default color of the text and lines"); ADD_PROPERTY_TYPE(TextSize, (12.0), vgroup, App::Prop_None, "The size of the text"); ADD_PROPERTY_TYPE(LineWidth, (0.35), vgroup, App::Prop_None, "The thickness of the cell lines"); ADD_PROPERTY_TYPE(Owner, (nullptr), vgroup, (App::PropertyType)(App::Prop_None), "Feature to which this sheet is attached"); EditableTexts.setStatus(App::Property::Hidden, true); } DrawViewSpreadsheet::~DrawViewSpreadsheet() { } short DrawViewSpreadsheet::mustExecute() const { if (!isRestoring()) { if ( Source.isTouched() || CellStart.isTouched() || CellEnd.isTouched() || Font.isTouched() || TextSize.isTouched() || TextColor.isTouched() || LineWidth.isTouched() ) { return 1; } } return TechDraw::DrawView::mustExecute(); } void DrawViewSpreadsheet::onChanged(const App::Property* prop) { TechDraw::DrawView::onChanged(prop); } App::DocumentObjectExecReturn *DrawViewSpreadsheet::execute() { App::DocumentObject* link = Source.getValue(); std::string scellstart = CellStart.getValue(); std::string scellend = CellEnd.getValue(); if (!link) return new App::DocumentObjectExecReturn("No spreadsheet linked"); if (!link->isDerivedFrom()) return new App::DocumentObjectExecReturn("The linked object is not a spreadsheet"); if (scellstart.empty() || scellend.empty()) return new App::DocumentObjectExecReturn("Empty cell value"); Symbol.setValue(getSheetImage()); overrideKeepUpdated(false); return TechDraw::DrawView::execute(); } std::vector DrawViewSpreadsheet::getAvailColumns() { // builds a list of available columns: A, B, ... Y, Z, AA, AB, ... ZY, ZZ. const std::string alphabet [] {"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"}; std::vector availcolumns { std::begin (alphabet), std::end (alphabet) }; for (const std::string &left : alphabet) for (const std::string &right : alphabet) availcolumns.push_back(left + right); return availcolumns; } std::string DrawViewSpreadsheet::getSVGHead() { return std::string("\n"); } std::string DrawViewSpreadsheet::getSVGTail() { return "\n"; } std::string DrawViewSpreadsheet::getSheetImage() { App::DocumentObject* link = Source.getValue(); link->recomputeFeature(); //make sure s/s is up to date std::string scellstart = CellStart.getValue(); std::string scellend = CellEnd.getValue(); //s/s columns are A, B,C, ... ZX, ZY, ZZ //lower case characters are not valid transform(scellstart.begin(), scellstart.end(), scellstart.begin(), ::toupper); transform(scellend.begin(), scellend.end(), scellend.begin(), ::toupper); std::string colPart; std::string rowPart; boost::regex re{"([A-Z]*)([0-9]*)"}; boost::smatch what; int iRowStart = 0, iRowEnd = 0; std::string sColStart, sColEnd; if (boost::regex_search(scellstart, what, re)) { if (what.size() < 3) { Base::Console().Error("%s - start cell (%s) is invalid\n", getNameInDocument(), CellStart.getValue()); return std::string(); } colPart = what[1]; sColStart = colPart; rowPart = what[2]; try { iRowStart = std::stoi(rowPart); } catch (...) { Base::Console().Error("%s - start cell (%s) invalid row\n", getNameInDocument(), rowPart.c_str()); return std::string(); } } if (boost::regex_search(scellend, what, re)) { if (what.size() < 3) { Base::Console().Error("%s - end cell (%s) is invalid\n", getNameInDocument(), CellEnd.getValue()); } else { colPart = what[1]; sColEnd = colPart; rowPart = what[2]; try { iRowEnd = std::stoi(rowPart); } catch (...) { Base::Console().Error("%s - end cell (%s) invalid row\n", getNameInDocument(), rowPart.c_str()); return std::string(); } } } const std::vector availcolumns = getAvailColumns(); //validate range start column in sheet's available columns int iAvailColStart = colInList(availcolumns, sColStart); if (iAvailColStart < 0) { //not found range start column in availcolumns list Base::Console().Error("DVS - %s - start Column (%s) is invalid\n", getNameInDocument(), sColStart.c_str()); return std::string(); } //validate range end column in sheet's available columns int iAvailColEnd = colInList(availcolumns, sColEnd); if (iAvailColEnd < 0) { Base::Console().Error("DVS - %s - end Column (%s) is invalid\n", getNameInDocument(), sColEnd.c_str()); return std::string(); } //check for logical range if ( (iAvailColStart > iAvailColEnd) || (iRowStart > iRowEnd) ) { Base::Console().Error("%s - cell range is illogical\n", getNameInDocument()); return std::string(); } // build row and column ranges std::vector validColNames; std::vector validRowNumbers; int iCol = iAvailColStart; for (; iCol <= iAvailColEnd; iCol++) { validColNames.push_back(availcolumns.at(iCol)); } int iRow = iRowStart; for ( ; iRow <= iRowEnd ; iRow++) { validRowNumbers.push_back(iRow); } // create the SVG code std::stringstream result; result << getSVGHead(); std::string ViewName = Label.getValue(); Base::Color c = TextColor.getValue(); result << "" << std::endl; // fill the cells float rowoffset = 0.0; float coloffset = 0.0; float cellheight = 100; float cellwidth = 100; std::string celltext; Spreadsheet::Sheet* sheet = static_cast(link); std::vector skiplist; for (std::vector::const_iterator col = validColNames.begin(); col != validColNames.end(); ++col) { // create a group for each column result << " " << std::endl; for (std::vector::const_iterator row = validRowNumbers.begin(); row != validRowNumbers.end(); ++row) { // get cell size std::stringstream srow; srow << (*row); App::CellAddress address((*col) + srow.str()); cellwidth = sheet->getColumnWidth(address.col()); cellheight = sheet->getRowHeight(address.row()); celltext = ""; Spreadsheet::Cell* cell = sheet->getCell(address); // get the text App::Property* prop = sheet->getPropertyByName(address.toString().c_str()); std::stringstream field; if (prop && cell) { if (prop->isDerivedFrom()) { auto contentAsQuantity = static_cast(prop)->getQuantityValue(); field << contentAsQuantity.getUserString(); } else if (prop->isDerivedFrom() || prop->isDerivedFrom()) { std::string temp = cell->getFormattedQuantity(); DrawUtil::encodeXmlSpecialChars(temp); field << temp; } else if (prop->isDerivedFrom()) { std::string temp = static_cast(prop)->getValue(); DrawUtil::encodeXmlSpecialChars(temp); field << temp; } else { Base::Console().Error("DVSS: Unknown property type\n"); } celltext = field.str(); } // get colors, style, alignment and span int alignment = 0; std::string bcolor = "none"; std::string fcolor = c.asHexString(); std::string textstyle; if (cell) { Base::Color f, b; std::set st; int colspan, rowspan; if (cell->getBackground(b)) { bcolor = b.asHexString(); } if (cell->getForeground(f)) { fcolor = f.asHexString(); } if (cell->getStyle(st)) { for (std::set::const_iterator i = st.begin(); i != st.end(); ++i) { if ((*i) == "bold") textstyle += "font-weight: bold; "; else if ((*i) == "italic") textstyle += "font-style: italic; "; else if ((*i) == "underline") textstyle += "text-decoration: underline; "; } } if (cell->getSpans(rowspan, colspan)) { for (int i=0; i 0) cellwidth += sheet->getColumnWidth(nextcell.col()); if (j > 0) cellheight += sheet->getRowHeight(nextcell.row()); if ( (i > 0) || (j > 0) ) skiplist.push_back(nextcell.toString()); } } } cell->getAlignment(alignment); } // skip cell if found in skiplist if (std::ranges::find(skiplist, address.toString()) == skiplist.end()) { result << " " << std::endl; if (alignment & Spreadsheet::Cell::ALIGNMENT_LEFT) result << " " << celltext << "" << std::endl; } if (!(alignment & Spreadsheet::Cell::ALIGNMENT_LEFT) && !(alignment & Spreadsheet::Cell::ALIGNMENT_RIGHT) && !(alignment & Spreadsheet::Cell::ALIGNMENT_HCENTER) ) { // no horizontal alignment specified, so we will default to // Spreadsheet::Cell::ALIGNMENT_LEFT result << " " << celltext << "" << std::endl; } } rowoffset += sheet->getRowHeight(address.row()); } result << " " << std::endl; rowoffset = 0.0; coloffset += cellwidth; } // close the containing group result << "" << std::endl; result << getSVGTail(); return result.str(); } //find index of column name "toFind" in "list" of column names int DrawViewSpreadsheet::colInList(const std::vector& list, const std::string& toFind) { const auto match = std::ranges::find(list, toFind); if (match == std::end(list)) { return -1; // Error value } return match - std::begin(list); } // Python Drawing feature --------------------------------------------------------- namespace App { /// @cond DOXERR PROPERTY_SOURCE_TEMPLATE(TechDraw::DrawViewSpreadsheetPython, TechDraw::DrawViewSpreadsheet) template<> const char* TechDraw::DrawViewSpreadsheetPython::getViewProviderName() const { return "TechDrawGui::ViewProviderSpreadsheet"; } /// @endcond // explicit template instantiation template class TechDrawExport FeaturePythonT; }