2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013,2014, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
37 * Implements functions in helpformat.h.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_onlinehelp
42 #include "helpformat.h"
48 #include "gromacs/onlinehelp/helpwritercontext.h"
49 #include "gromacs/utility/gmxassert.h"
50 #include "gromacs/utility/stringutil.h"
55 /********************************************************************
56 * TextTableFormatter::Impl
60 * Private implementation class for TextTableFormatter.
62 * \ingroup module_onlinehelp
64 class TextTableFormatter::Impl
68 * Manages a single column for TextTableFormatter.
70 * \ingroup module_onlinehelp
74 //! Initializes a text table column with given values.
75 ColumnData(const char *title, int width, bool bWrap)
76 : title_(title != NULL ? title : ""),
77 width_(width), bWrap_(bWrap), firstLine_(0),
78 nextLineIndex_(0), nextLineOffset_(0)
80 GMX_ASSERT(width >= 0, "Negative width not possible");
81 GMX_ASSERT(title_.length() <= static_cast<size_t>(width),
82 "Title too long for column width");
85 //! Returns the title of the column.
86 const std::string &title() const { return title_; }
87 //! Returns the width of the column.
88 int width() const { return width_; }
90 * Returns the first line offset for the current row.
92 * Note that the return value may be outside the printed lines if
95 int firstLine() const { return firstLine_; }
98 * Resets the formatting state.
100 * After this call, textForNextLine() and hasLinesRemaining() can
101 * be used to format the lines for the column.
103 void startFormatting()
105 nextLineIndex_ = (!lines_.empty() ? -firstLine_ : 0);
108 //! Whether there are lines remaining for textForNextLine().
109 bool hasLinesRemaining() const
111 return nextLineIndex_ < static_cast<int>(lines_.size());
114 * Returns the text for the next line.
116 * \param[in] columnWidth Width to wrap the text to.
117 * \returns Text for the next line, or empty string if there is
118 * no text for this column.
120 std::string textForNextLine(int columnWidth)
122 if (nextLineIndex_ < 0 || !hasLinesRemaining())
125 return std::string();
129 TextLineWrapperSettings settings;
130 settings.setLineLength(columnWidth);
131 TextLineWrapper wrapper(settings);
132 const std::string ¤tLine = lines_[nextLineIndex_];
133 const size_t prevOffset = nextLineOffset_;
134 const size_t nextOffset
135 = wrapper.findNextLine(currentLine, prevOffset);
136 if (nextOffset >= currentLine.size())
143 nextLineOffset_ = nextOffset;
145 return wrapper.formatLine(currentLine, prevOffset, nextOffset);
149 return lines_[nextLineIndex_++];
153 //! Statit data: title of the column.
155 //! Static data: width of the column.
157 //! Static data: whether to automatically wrap input text.
159 //! First line offset for the current row.
161 //! Text lines for the current row.
162 std::vector<std::string> lines_;
163 //! Formatting state: index in `lines_` for the next line.
165 //! Formatting state: offset within line `nextLineIndex_` for the next line.
166 size_t nextLineOffset_;
169 //! Container type for column data.
170 typedef std::vector<ColumnData> ColumnList;
172 //! Initializes data for an empty formatter.
176 * Convenience method for checked access to data for a column.
178 * \param[in] index Zero-based column index.
179 * \returns \c columns_[index]
181 ColumnData &columnData(int index)
183 GMX_ASSERT(index >= 0 && index < static_cast<int>(columns_.size()),
184 "Invalid column index");
185 return columns_[index];
187 //! \copydoc columnData()
188 const ColumnData &columnData(int index) const
190 return const_cast<Impl *>(this)->columnData(index);
193 //! Container for column data.
195 //! Indentation before the first column.
196 int firstColumnIndent_;
197 //! Indentation before the last column if folded.
198 int foldLastColumnToNextLineIndent_;
199 //! If true, no output has yet been produced.
201 //! If true, a header will be printed before the first row.
205 TextTableFormatter::Impl::Impl()
206 : firstColumnIndent_(0), foldLastColumnToNextLineIndent_(-1),
207 bFirstRow_(true), bPrintHeader_(false)
211 /********************************************************************
215 TextTableFormatter::TextTableFormatter()
220 TextTableFormatter::~TextTableFormatter()
224 void TextTableFormatter::addColumn(const char *title, int width, bool bWrap)
226 if (title != NULL && title[0] != '\0')
228 impl_->bPrintHeader_ = true;
230 impl_->columns_.push_back(Impl::ColumnData(title, width, bWrap));
233 void TextTableFormatter::setFirstColumnIndent(int indent)
235 GMX_RELEASE_ASSERT(indent >= 0, "Negative indentation not allowed");
236 impl_->firstColumnIndent_ = indent;
239 void TextTableFormatter::setFoldLastColumnToNextLine(int indent)
241 impl_->foldLastColumnToNextLineIndent_ = indent;
244 bool TextTableFormatter::didOutput() const
246 return !impl_->bFirstRow_;
249 void TextTableFormatter::clear()
251 Impl::ColumnList::iterator i;
252 for (i = impl_->columns_.begin(); i != impl_->columns_.end(); ++i)
259 void TextTableFormatter::addColumnLine(int index, const std::string &text)
261 Impl::ColumnData &column = impl_->columnData(index);
262 TextLineWrapper wrapper;
263 std::vector<std::string> lines(wrapper.wrapToVector(text));
264 column.lines_.insert(column.lines_.end(), lines.begin(), lines.end());
267 void TextTableFormatter::addColumnHelpTextBlock(
268 int index, const HelpWriterContext &context, const std::string &text)
270 Impl::ColumnData &column = impl_->columnData(index);
271 TextLineWrapperSettings settings;
272 // TODO: If in the future, there is actually a coupling between the markup
273 // and the wrapping, this must be postponed into formatRow(), where we do
274 // the actual line wrapping.
275 std::vector<std::string> lines(
276 context.substituteMarkupAndWrapToVector(settings, text));
277 column.lines_.insert(column.lines_.end(), lines.begin(), lines.end());
280 void TextTableFormatter::setColumnFirstLineOffset(int index, int firstLine)
282 GMX_ASSERT(firstLine >= 0, "Invalid first line");
283 Impl::ColumnData &column = impl_->columnData(index);
284 column.firstLine_ = firstLine;
287 std::string TextTableFormatter::formatRow()
290 Impl::ColumnList::iterator column;
291 // Print a header if this is the first line.
292 if (impl_->bPrintHeader_ && impl_->bFirstRow_)
294 size_t totalWidth = 0;
295 result.append(impl_->firstColumnIndent_, ' ');
296 for (column = impl_->columns_.begin();
297 column != impl_->columns_.end();
300 std::string title(column->title());
301 if (column != impl_->columns_.end() - 1)
303 title.resize(column->width() + 1, ' ');
304 totalWidth += title.length();
308 totalWidth += std::min(column->width(),
309 static_cast<int>(title.length() + 13));
311 result.append(title);
314 result.append(impl_->firstColumnIndent_, ' ');
315 result.append(totalWidth, '-');
319 // Format all the lines, one column at a time.
320 std::vector<std::string> lines;
321 std::vector<std::string> columnLines;
322 int currentWidth = 0;
323 bool bFoldLastColumn = false;
324 for (column = impl_->columns_.begin();
325 column != impl_->columns_.end();
328 // Format the column into columnLines.
329 column->startFormatting();
331 columnLines.reserve(lines.size());
332 for (size_t line = 0; column->hasLinesRemaining(); ++line)
334 int columnWidth = column->width();
335 if (line < lines.size())
337 const int overflow = static_cast<int>(lines[line].length()) - currentWidth;
340 if (overflow > columnWidth && column->bWrap_)
342 columnLines.push_back(std::string());
345 columnWidth -= overflow;
348 columnLines.push_back(column->textForNextLine(columnWidth));
350 if (column == impl_->columns_.end() - 1
351 && impl_->foldLastColumnToNextLineIndent_ >= 0
352 && columnLines.size() >= lines.size() + column->lines_.size())
354 bFoldLastColumn = true;
355 currentWidth += column->width();
358 // Add columnLines into lines.
359 if (lines.size() < columnLines.size())
361 lines.resize(columnLines.size());
363 for (size_t line = 0; line < columnLines.size(); ++line)
365 if (column != impl_->columns_.begin() && !columnLines[line].empty())
367 lines[line].append(" ");
368 if (static_cast<int>(lines[line].length()) < currentWidth)
370 lines[line].resize(currentWidth, ' ');
373 lines[line].append(columnLines[line]);
375 currentWidth += column->width() + 1;
378 // Construct the result by concatenating all the lines.
379 std::vector<std::string>::const_iterator line;
380 for (line = lines.begin(); line != lines.end(); ++line)
382 result.append(impl_->firstColumnIndent_, ' ');
383 result.append(*line);
389 Impl::ColumnList::reference lastColumn = impl_->columns_.back();
390 const int totalIndent
391 = impl_->firstColumnIndent_ + impl_->foldLastColumnToNextLineIndent_;
392 lastColumn.startFormatting();
393 currentWidth -= impl_->foldLastColumnToNextLineIndent_;
394 while (lastColumn.hasLinesRemaining())
396 result.append(totalIndent, ' ');
397 result.append(lastColumn.textForNextLine(currentWidth));
402 impl_->bFirstRow_ = false;