diff --git a/include/znc/Utils.h b/include/znc/Utils.h index e64560df..765be427 100644 --- a/include/znc/Utils.h +++ b/include/znc/Utils.h @@ -112,7 +112,8 @@ class CException { EType m_eType; }; -/** Previously this generated a grid-like output from a given input. + +/** Generate a grid-like output from a given input. * * @code * CTable table; @@ -129,15 +130,17 @@ class CException { * } * @endcode * - * But tables look awful in IRC. So now it puts every cell on separate line. + * The above code would generate the following output: + * @verbatim ++-------+-------+ +| a | b | ++-------+-------+ +| hello | world | ++-------+-------+@endverbatim */ class CTable : protected std::vector> { public: - /** Constructor - * - * @param uPreferredWidth If width of table is bigger than this, text in cells will be wrapped to several lines, if possible - */ - CTable() : m_vsHeaders(), m_vsOutput() {} + CTable() {} virtual ~CTable() {} /** Adds a new column to the table. @@ -171,6 +174,14 @@ class CTable : protected std::vector> { */ bool GetLine(unsigned int uIdx, CString& sLine) const; + /** Return the width of the given column. + * Please note that adding and filling new rows might change the + * result of this function! + * @param uIdx The index of the column you are interested in. + * @return The width of the column. + */ + CString::size_type GetColumnWidth(unsigned int uIdx) const; + /// Completely clear the table. void Clear(); @@ -182,12 +193,11 @@ class CTable : protected std::vector> { private: unsigned int GetColumnIndex(const CString& sName) const; - VCString Render() const; protected: - // TODO: cleanup these fields before 1.7.0 (I don't want to break ABI) - VCString m_vsHeaders; - mutable VCString m_vsOutput; // Rendered table + std::vector m_vsHeaders; + // Used to cache the width of a column + std::map m_msuWidths; }; #ifdef HAVE_LIBSSL diff --git a/src/Utils.cpp b/src/Utils.cpp index bb04035d..91a6732a 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -704,6 +704,7 @@ bool CTable::AddColumn(const CString& sName) { } m_vsHeaders.push_back(sName); + m_msuWidths[sName] = sName.size(); return true; } @@ -735,36 +736,63 @@ bool CTable::SetCell(const CString& sColumn, const CString& sValue, (*this)[uRowIdx][uColIdx] = sValue; + if (m_msuWidths[sColumn] < sValue.size()) + m_msuWidths[sColumn] = sValue.size(); + return true; } bool CTable::GetLine(unsigned int uIdx, CString& sLine) const { + std::stringstream ssRet; + if (empty()) { return false; } - if (m_vsOutput.empty()) { - m_vsOutput = Render(); - } - if (uIdx >= m_vsOutput.size()) { - return false; - } - sLine = m_vsOutput[uIdx]; - return true; -} -VCString CTable::Render() const { - VCString vsOutput; - vsOutput.reserve((m_vsHeaders.size() + 1) * size() + 1); - for (const VCString& vsRow : *this) { - vsOutput.emplace_back("------"); - for (unsigned int i = 0; i < m_vsHeaders.size(); ++i) { - if (!vsRow[i].empty()) { - vsOutput.emplace_back(m_vsHeaders[i] + ": " + vsRow[i]); + if (uIdx == 1) { + ssRet.fill(' '); + ssRet << "| "; + + for (unsigned int a = 0; a < m_vsHeaders.size(); a++) { + ssRet.width(GetColumnWidth(a)); + ssRet << std::left << m_vsHeaders[a]; + ssRet << ((a == m_vsHeaders.size() - 1) ? " |" : " | "); + } + + sLine = ssRet.str(); + return true; + } else if ((uIdx == 0) || (uIdx == 2) || (uIdx == (size() + 3))) { + ssRet.fill('-'); + ssRet << "+-"; + + for (unsigned int a = 0; a < m_vsHeaders.size(); a++) { + ssRet.width(GetColumnWidth(a)); + ssRet << std::left << "-"; + ssRet << ((a == m_vsHeaders.size() - 1) ? "-+" : "-+-"); + } + + sLine = ssRet.str(); + return true; + } else { + uIdx -= 3; + + if (uIdx < size()) { + const std::vector& mRow = (*this)[uIdx]; + ssRet.fill(' '); + ssRet << "| "; + + for (unsigned int c = 0; c < m_vsHeaders.size(); c++) { + ssRet.width(GetColumnWidth(c)); + ssRet << std::left << mRow[c]; + ssRet << ((c == m_vsHeaders.size() - 1) ? " |" : " | "); } + + sLine = ssRet.str(); + return true; } } - vsOutput.emplace_back("------"); - return vsOutput; + + return false; } unsigned int CTable::GetColumnIndex(const CString& sName) const { @@ -777,10 +805,26 @@ unsigned int CTable::GetColumnIndex(const CString& sName) const { return (unsigned int)-1; } +CString::size_type CTable::GetColumnWidth(unsigned int uIdx) const { + if (uIdx >= m_vsHeaders.size()) { + return 0; + } + + const CString& sColName = m_vsHeaders[uIdx]; + std::map::const_iterator it = + m_msuWidths.find(sColName); + + if (it == m_msuWidths.end()) { + // AddColumn() and SetCell() should make sure that we get a value :/ + return 0; + } + return it->second; +} + void CTable::Clear() { clear(); m_vsHeaders.clear(); - m_vsOutput.clear(); + m_msuWidths.clear(); } #ifdef HAVE_LIBSSL