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
44 #include "helpformat.h"
50 #include "gromacs/onlinehelp/helpwritercontext.h"
51 #include "gromacs/utility/gmxassert.h"
52 #include "gromacs/utility/stringutil.h"
57 /********************************************************************
58 * TextTableFormatter::Impl
62 * Private implementation class for TextTableFormatter.
64 * \ingroup module_onlinehelp
66 class TextTableFormatter::Impl
70 * Manages a single column for TextTableFormatter.
72 * \ingroup module_onlinehelp
76 //! Initializes a text table column with given values.
77 ColumnData(const char *title, int width, bool bWrap)
78 : title_(title != NULL ? title : ""),
79 width_(width), bWrap_(bWrap), firstLine_(0),
80 nextLineIndex_(0), nextLineOffset_(0)
82 GMX_ASSERT(width >= 0, "Negative width not possible");
83 GMX_ASSERT(title_.length() <= static_cast<size_t>(width),
84 "Title too long for column width");
87 //! Returns the title of the column.
88 const std::string &title() const { return title_; }
89 //! Returns the width of the column.
90 int width() const { return width_; }
92 * Returns the first line offset for the current row.
94 * Note that the return value may be outside the printed lines if
97 int firstLine() const { return firstLine_; }
100 * Resets the formatting state.
102 * After this call, textForNextLine() and hasLinesRemaining() can
103 * be used to format the lines for the column.
105 void startFormatting()
107 nextLineIndex_ = (!lines_.empty() ? -firstLine_ : 0);
110 //! Whether there are lines remaining for textForNextLine().
111 bool hasLinesRemaining() const
113 return nextLineIndex_ < static_cast<int>(lines_.size());
116 * Returns the text for the next line.
118 * \param[in] columnWidth Width to wrap the text to.
119 * \returns Text for the next line, or empty string if there is
120 * no text for this column.
122 std::string textForNextLine(int columnWidth)
124 if (nextLineIndex_ < 0 || !hasLinesRemaining())
127 return std::string();
131 TextLineWrapperSettings settings;
132 settings.setLineLength(columnWidth);
133 TextLineWrapper wrapper(settings);
134 const std::string ¤tLine = lines_[nextLineIndex_];
135 const size_t prevOffset = nextLineOffset_;
136 const size_t nextOffset
137 = wrapper.findNextLine(currentLine, prevOffset);
138 if (nextOffset >= currentLine.size())
145 nextLineOffset_ = nextOffset;
147 return wrapper.formatLine(currentLine, prevOffset, nextOffset);
151 return lines_[nextLineIndex_++];
155 //! Statit data: title of the column.
157 //! Static data: width of the column.
159 //! Static data: whether to automatically wrap input text.
161 //! First line offset for the current row.
163 //! Text lines for the current row.
164 std::vector<std::string> lines_;
165 //! Formatting state: index in `lines_` for the next line.
167 //! Formatting state: offset within line `nextLineIndex_` for the next line.
168 size_t nextLineOffset_;
171 //! Container type for column data.
172 typedef std::vector<ColumnData> ColumnList;
174 //! Initializes data for an empty formatter.
178 * Convenience method for checked access to data for a column.
180 * \param[in] index Zero-based column index.
181 * \returns \c columns_[index]
183 ColumnData &columnData(int index)
185 GMX_ASSERT(index >= 0 && index < static_cast<int>(columns_.size()),
186 "Invalid column index");
187 return columns_[index];
189 //! \copydoc columnData()
190 const ColumnData &columnData(int index) const
192 return const_cast<Impl *>(this)->columnData(index);
195 //! Container for column data.
197 //! Indentation before the first column.
198 int firstColumnIndent_;
199 //! Indentation before the last column if folded.
200 int foldLastColumnToNextLineIndent_;
201 //! If true, no output has yet been produced.
203 //! If true, a header will be printed before the first row.
207 TextTableFormatter::Impl::Impl()
208 : firstColumnIndent_(0), foldLastColumnToNextLineIndent_(-1),
209 bFirstRow_(true), bPrintHeader_(false)
213 /********************************************************************
217 TextTableFormatter::TextTableFormatter()
222 TextTableFormatter::~TextTableFormatter()
226 void TextTableFormatter::addColumn(const char *title, int width, bool bWrap)
228 if (title != NULL && title[0] != '\0')
230 impl_->bPrintHeader_ = true;
232 impl_->columns_.push_back(Impl::ColumnData(title, width, bWrap));
235 void TextTableFormatter::setFirstColumnIndent(int indent)
237 GMX_RELEASE_ASSERT(indent >= 0, "Negative indentation not allowed");
238 impl_->firstColumnIndent_ = indent;
241 void TextTableFormatter::setFoldLastColumnToNextLine(int indent)
243 impl_->foldLastColumnToNextLineIndent_ = indent;
246 bool TextTableFormatter::didOutput() const
248 return !impl_->bFirstRow_;
251 void TextTableFormatter::clear()
253 Impl::ColumnList::iterator i;
254 for (i = impl_->columns_.begin(); i != impl_->columns_.end(); ++i)
261 void TextTableFormatter::addColumnLine(int index, const std::string &text)
263 Impl::ColumnData &column = impl_->columnData(index);
264 TextLineWrapper wrapper;
265 std::vector<std::string> lines(wrapper.wrapToVector(text));
266 column.lines_.insert(column.lines_.end(), lines.begin(), lines.end());
269 void TextTableFormatter::addColumnHelpTextBlock(
270 int index, const HelpWriterContext &context, const std::string &text)
272 Impl::ColumnData &column = impl_->columnData(index);
273 TextLineWrapperSettings settings;
274 // TODO: If in the future, there is actually a coupling between the markup
275 // and the wrapping, this must be postponed into formatRow(), where we do
276 // the actual line wrapping.
277 std::vector<std::string> lines(
278 context.substituteMarkupAndWrapToVector(settings, text));
279 column.lines_.insert(column.lines_.end(), lines.begin(), lines.end());
282 void TextTableFormatter::setColumnFirstLineOffset(int index, int firstLine)
284 GMX_ASSERT(firstLine >= 0, "Invalid first line");
285 Impl::ColumnData &column = impl_->columnData(index);
286 column.firstLine_ = firstLine;
289 std::string TextTableFormatter::formatRow()
292 Impl::ColumnList::iterator column;
293 // Print a header if this is the first line.
294 if (impl_->bPrintHeader_ && impl_->bFirstRow_)
296 size_t totalWidth = 0;
297 result.append(impl_->firstColumnIndent_, ' ');
298 for (column = impl_->columns_.begin();
299 column != impl_->columns_.end();
302 std::string title(column->title());
303 if (column != impl_->columns_.end() - 1)
305 title.resize(column->width() + 1, ' ');
306 totalWidth += title.length();
310 totalWidth += std::min(column->width(),
311 static_cast<int>(title.length() + 13));
313 result.append(title);
316 result.append(impl_->firstColumnIndent_, ' ');
317 result.append(totalWidth, '-');
321 // Format all the lines, one column at a time.
322 std::vector<std::string> lines;
323 std::vector<std::string> columnLines;
324 int currentWidth = 0;
325 bool bFoldLastColumn = false;
326 for (column = impl_->columns_.begin();
327 column != impl_->columns_.end();
330 // Format the column into columnLines.
331 column->startFormatting();
333 columnLines.reserve(lines.size());
334 for (size_t line = 0; column->hasLinesRemaining(); ++line)
336 int columnWidth = column->width();
337 if (line < lines.size())
339 const int overflow = static_cast<int>(lines[line].length()) - currentWidth;
342 if (overflow > columnWidth && column->bWrap_)
344 columnLines.push_back(std::string());
347 columnWidth -= overflow;
350 columnLines.push_back(column->textForNextLine(columnWidth));
352 if (column == impl_->columns_.end() - 1
353 && impl_->foldLastColumnToNextLineIndent_ >= 0
354 && columnLines.size() >= lines.size() + column->lines_.size())
356 bFoldLastColumn = true;
357 currentWidth += column->width();
360 // Add columnLines into lines.
361 if (lines.size() < columnLines.size())
363 lines.resize(columnLines.size());
365 for (size_t line = 0; line < columnLines.size(); ++line)
367 if (column != impl_->columns_.begin() && !columnLines[line].empty())
369 lines[line].append(" ");
370 if (static_cast<int>(lines[line].length()) < currentWidth)
372 lines[line].resize(currentWidth, ' ');
375 lines[line].append(columnLines[line]);
377 currentWidth += column->width() + 1;
380 // Construct the result by concatenating all the lines.
381 std::vector<std::string>::const_iterator line;
382 for (line = lines.begin(); line != lines.end(); ++line)
384 result.append(impl_->firstColumnIndent_, ' ');
385 result.append(*line);
391 Impl::ColumnList::reference lastColumn = impl_->columns_.back();
392 const int totalIndent
393 = impl_->firstColumnIndent_ + impl_->foldLastColumnToNextLineIndent_;
394 lastColumn.startFormatting();
395 currentWidth -= impl_->foldLastColumnToNextLineIndent_;
396 while (lastColumn.hasLinesRemaining())
398 result.append(totalIndent, ' ');
399 result.append(lastColumn.textForNextLine(currentWidth));
404 impl_->bFirstRow_ = false;