From cd1f334f31a2908cc85b7be100475a77e603101d Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Fri, 17 Apr 2015 22:56:35 +0100 Subject: [PATCH 1/3] Update error message about --datadir Noone remembers these days that at some point ZNC supported using the same config directory, but different znc.conf. So now the old message is just confusing. However, nowadays many people confuse "/znc foo" in IRC client with "znc foo" in shell. --- src/main.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 1ab54df2..09b2c9cd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -248,8 +248,9 @@ int main(int argc, char** argv) { } if (optind < argc) { - CUtils::PrintError("Specifying a config file as an argument isn't supported anymore."); - CUtils::PrintError("Use --datadir instead."); + CUtils::PrintError("Unrecognized command line arguments."); + CUtils::PrintError("Did you mean to run `/znc " + CString(argv[optind]) + "' in IRC client instead?"); + CUtils::PrintError("Hint: `/znc " + CString(argv[optind]) + "' is an alias for `/msg *status " + CString(argv[optind]) + "'"); return 1; } From 880713dcfc8694d5f9292559f974bea1c4e9d868 Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Sat, 18 Apr 2015 10:05:37 +0100 Subject: [PATCH 2/3] Main ZNC IRC channel is on freenode. 876 users vs. 220 on efnet. Fix #895 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7c2fc0f1..7c72ecb9 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ Python modules are loaded through the global module [ModPython](http://wiki.znc. ## Further infos -Please visit http://znc.in/ or #znc on EFNet or freenode if you still have questions. +Please visit http://znc.in/ or #znc on freenode if you still have questions. You can get the latest development version with git: git clone git://github.com/znc/znc.git From 00404b8cbb795902beba82819ac64494e3625c0b Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Sat, 18 Apr 2015 17:38:56 +0100 Subject: [PATCH 3/3] Revert "Make tables... not so tabular." This reverts commit 2c3064fb562ab3bba8d18f0780f606db85272998. --- include/znc/Utils.h | 1 - src/Utils.cpp | 149 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 138 insertions(+), 12 deletions(-) diff --git a/include/znc/Utils.h b/include/znc/Utils.h index ceb3bb36..2322a8aa 100644 --- a/include/znc/Utils.h +++ b/include/znc/Utils.h @@ -199,7 +199,6 @@ private: static VCString WrapWords(const CString& s, size_type uWidth); protected: - // TODO: cleanup these fields before 1.7.0 (I don't want to break ABI) VCString m_vsHeaders; std::vector m_vuMaxWidths; // Column don't need to be bigger than this std::vector m_vuMinWidths; // Column can't be thiner than this diff --git a/src/Utils.cpp b/src/Utils.cpp index 9e977afe..963a4e4d 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -555,6 +555,9 @@ bool CTable::AddColumn(const CString& sName, bool bWrappable) { } m_vsHeaders.push_back(sName); + m_vuMaxWidths.push_back(sName.size()); + // TODO: Maybe headers can be wrapped too? + m_vuMinWidths.push_back(sName.size()); m_vbWrappable.push_back(bWrappable); return true; @@ -587,6 +590,27 @@ bool CTable::SetCell(const CString& sColumn, const CString& sValue, size_type uR (*this)[uRowIdx][uColIdx] = sValue; + if (sValue.length() > m_vuMaxWidths[uColIdx]) { + m_vuMaxWidths[uColIdx] = sValue.length(); + } + + if (m_vbWrappable[uColIdx]) { + VCString vsWords; + sValue.Split(" ", vsWords); + size_type uMaxWord = 0; + for (const CString& sWord : vsWords) { + if (sWord.length() > uMaxWord) { + uMaxWord = sWord.length(); + } + } + // We can't shrink column further than the longest word in it + if (uMaxWord > m_vuMinWidths[uColIdx]) { + m_vuMinWidths[uColIdx] = uMaxWord; + } + } else { + m_vuMinWidths[uColIdx] = m_vuMaxWidths[uColIdx]; + } + return true; } @@ -605,22 +629,122 @@ bool CTable::GetLine(unsigned int uIdx, CString& sLine) const { } 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]); - } + size_type uTotalWidth = 1; // '|' + for (size_type uWidth : m_vuMaxWidths) { + uTotalWidth += uWidth + 3; // '|', ' 'x2 + } + + std::vector vuWidth = m_vuMaxWidths; + + std::map miColumnSpace; + for (unsigned int i = 0; i < m_vsHeaders.size(); ++i) { + int iSpace = m_vuMaxWidths[i] - m_vuMinWidths[i]; + if (iSpace > 0) { + miColumnSpace[i] = iSpace; } } - vsOutput.emplace_back("------"); + + // Not very efficient algorithm, and doesn't produce very good results... + while (uTotalWidth > m_uPreferredWidth) { + std::vector viToErase; + for (auto& i : miColumnSpace) { + uTotalWidth--; + i.second--; + vuWidth[i.first]--; + if (i.second == 0) { + viToErase.push_back(i.first); + } + if (uTotalWidth == m_uPreferredWidth) { + break; + } + } + for (int iCol : viToErase) { + miColumnSpace.erase(iCol); + } + if (miColumnSpace.empty()) { + // Every column is at its minimum width now, but total width is still more than preferred width + break; + } + } + + CString sHorizontal; + { + std::ostringstream ssLine; + ssLine << std::setfill('-'); + ssLine << "+"; + for (size_type uWidth : vuWidth) { + ssLine << std::setw(uWidth + 2) << std::left << "-"; + ssLine << "+"; + } + sHorizontal = ssLine.str(); + } + VCString vsOutput; + vsOutput.emplace_back(sHorizontal.Replace_n("-", "=")); + { + std::ostringstream ssLine; + ssLine << "|"; + for (unsigned int iCol = 0; iCol < vuWidth.size(); ++iCol) { + ssLine << " "; + ssLine << std::setw(vuWidth[iCol]) << std::left; + ssLine << m_vsHeaders[iCol] << " |"; + } + vsOutput.emplace_back(ssLine.str()); + } + vsOutput.emplace_back(vsOutput[0]); + for (const VCString& vsRow : *this) { + // Wrap words + std::vector vvsColumns; + vvsColumns.reserve(m_vsHeaders.size()); + unsigned int uRowNum = 1; + for (unsigned int iCol = 0; iCol < vuWidth.size(); ++iCol) { + if (m_vbWrappable[iCol]) { + vvsColumns.emplace_back(WrapWords(vsRow[iCol], vuWidth[iCol])); + } else { + vvsColumns.push_back({vsRow[iCol]}); + } + if (vvsColumns.back().size() > uRowNum) { + uRowNum = vvsColumns.back().size(); + } + } + CString sEmpty; + for (size_type uCurrentLine = 0; uCurrentLine < uRowNum; ++uCurrentLine) { + std::ostringstream ssLine; + ssLine << "|"; + for (unsigned int iCol = 0; iCol < vvsColumns.size(); ++iCol) { + const CString& sData = uCurrentLine < vvsColumns[iCol].size() ? vvsColumns[iCol][uCurrentLine] : sEmpty; + ssLine << " "; + ssLine << std::setw(vuWidth[iCol]) << std::left; + ssLine << sData << " |"; + } + vsOutput.emplace_back(ssLine.str()); + } + vsOutput.emplace_back(sHorizontal); + } + vsOutput.pop_back(); + vsOutput.emplace_back(vsOutput[0]); return vsOutput; } VCString CTable::WrapWords(const CString& s, size_type uWidth) { - return VCString(); + VCString vsWords; + s.Split(" ", vsWords); + VCString vsResult; + vsResult.emplace_back(""); + for (const CString& sWord : vsWords) { + size_type uOldLen = vsResult.back().length(); + if (uOldLen != 0) { + uOldLen++; // ' ' + } + if (uOldLen + sWord.length() > uWidth) { + vsResult.emplace_back(sWord); + } else { + if (uOldLen != 0) { + vsResult.back() += " "; + } + vsResult.back() += sWord; + } + } + return vsResult; } unsigned int CTable::GetColumnIndex(const CString& sName) const { @@ -635,7 +759,10 @@ unsigned int CTable::GetColumnIndex(const CString& sName) const { } CString::size_type CTable::GetColumnWidth(unsigned int uIdx) const { - return 0; + if (uIdx >= m_vsHeaders.size()) { + return 0; + } + return m_vuMaxWidths[uIdx]; } void CTable::Clear() {