CLI  2.9
ui_text.cpp
1 /*
2  Copyright (c) 2006-2018, Alexis Royer, http://alexis.royer.free.fr/CLI
3 
4  All rights reserved.
5 
6  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
7 
8  * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
9  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation
10  and/or other materials provided with the distribution.
11  * Neither the name of the CLI library project nor the names of its contributors may be used to endorse or promote products derived from this software
12  without specific prior written permission.
13 
14  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
22  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26 
27 
28 #include "cli/pch.h"
29 
30 #include "cli/assert.h"
31 #include "ui_text.h"
32 
33 
35 
36  CLI_NS_BEGIN(ui)
37 
38  Text::Text(const unsigned int UI_MaxLines, const unsigned int UI_MaxLineLength)
39  : OutputDevice("less", false),
40  m_tkLines(UI_MaxLines), m_bNewLineRequired(true), m_uiMaxLineLength(UI_MaxLineLength)
41  {
42  }
43 
45  {
46  }
47 
48  const bool Text::OpenDevice(void)
49  {
50  return true;
51  }
52 
53  const bool Text::CloseDevice(void)
54  {
55  return true;
56  }
57 
58  void Text::PutString(const char* const STR_Out) const
59  {
60  for (const char* pc_Out = STR_Out; (pc_Out != NULL) && (*pc_Out != '\0'); pc_Out ++)
61  {
62  if (m_bNewLineRequired)
63  {
64  m_tkLines.AddTail(tk::String(m_uiMaxLineLength));
65  m_bNewLineRequired = false;
66  }
67 
68  if (*pc_Out == '\n')
69  {
70  m_bNewLineRequired = true;
71  }
72  else
73  {
74  CLI_ASSERT(! m_tkLines.IsEmpty());
75  if (! m_tkLines.IsEmpty())
76  {
77  m_tkLines.GetTail().Append(*pc_Out);
78  }
79  }
80  }
81  }
82 
83  void Text::Beep(void) const
84  {
85  // Do nothing.
86  }
87 
88  void Text::CleanScreen(void) const
89  {
90  // Not sure there is an interest for cleaning a 'less' object...
91  // Whatever...
92  m_tkLines.Reset();
93  }
94 
95  const bool Text::WouldOutput(const OutputDevice& CLI_Device) const
96  {
97  if (OutputDevice::WouldOutput(CLI_Device))
98  {
99  return true;
100  }
101 
102  return false;
103  }
104 
105  void Text::Begin(TextIterator& it) const
106  {
107  // Set cursor at the beginning of the text.
108  it.m_tkTopLine = m_tkLines.GetIterator();
109  it.m_uiTopChar = 0;
110  it.m_bBottomIsUpToDate = false; // Bottom position is out of date until PrintPage() is called.
111  }
112 
113  const bool Text::PageUp(TextIterator& it) const
114  {
115  // Try to skeep as many lines as the screen is high.
116  for (unsigned int ui = 0; ui < it.m_uiPageHeight; ui ++)
117  {
118  if (! LineUp(it))
119  {
120  // If the iterator could not be moved one single line up, return false.
121  return (ui > 0);
122  }
123  }
124 
125  return true;
126  }
127 
128  const bool Text::LineUp(TextIterator& it) const
129  {
130  if (m_tkLines.IsValid(it.m_tkTopLine))
131  {
132  // Check whether the current top character position is not aligned to the beginning of a line.
133  if (it.m_uiTopChar >= it.m_cliScreenInfo.GetSafeWidth())
134  {
135  it.m_uiTopChar -= it.m_cliScreenInfo.GetSafeWidth();
136  it.m_bBottomIsUpToDate = false; // Bottom position is out of date until PrintPage() is called.
137  return true;
138  }
139 
140  // Try to decrement the current top line position.
141  if (m_tkLines.MovePrevious(it.m_tkTopLine))
142  {
143  CLI_ASSERT(it.m_cliScreenInfo.GetSafeWidth() > 0);
144  if (it.m_cliScreenInfo.GetSafeWidth() <= 0)
145  {
146  // Should not happen unless there is a bug in OutputDevice::ScreenDimension.
147  // If ever, let's start at the beginning of the upper line.
148  it.m_uiTopChar = 0;
149  }
150  else
151  {
152  it.m_uiTopChar = m_tkLines.GetAt(it.m_tkTopLine).GetLength();
153  if (m_tkLines.GetAt(it.m_tkTopLine).GetLength() % it.m_cliScreenInfo.GetSafeWidth() == 0)
154  {
155  it.m_uiTopChar -= it.m_cliScreenInfo.GetSafeWidth();
156  }
157  else
158  {
159  it.m_uiTopChar -= m_tkLines.GetAt(it.m_tkTopLine).GetLength() % it.m_cliScreenInfo.GetSafeWidth();
160  }
161  }
162  it.m_bBottomIsUpToDate = false; // Bottom position is out of date until PrintPage() is called.
163 
164  return true;
165  }
166  else
167  {
168  // Restore position to beginning.
169  CLI_ASSERT(it.m_uiTopChar == 0);
170  Begin(it);
171  }
172  }
173 
174  return false;
175  }
176 
177  const bool Text::LineDown(TextIterator& it, const OutputDevice* const PCLI_Out) const
178  {
179  // First check bottom position is up to date according to top position.
180  if (! it.m_bBottomIsUpToDate)
181  {
183  }
184 
185  // Check whether the bottom line can be incremented.
186  if (it.m_bBottomIsUpToDate && m_tkLines.IsValid(it.m_tkBottomLine))
187  {
188  CLI_ASSERT(m_tkLines.IsValid(it.m_tkTopLine));
189  if (m_tkLines.IsValid(it.m_tkTopLine))
190  {
191  // OK let's go.
192  it.m_uiTopChar += it.m_cliScreenInfo.GetSafeWidth();
193  if (it.m_uiTopChar >= m_tkLines.GetAt(it.m_tkTopLine).GetLength())
194  {
195  m_tkLines.MoveNext(it.m_tkTopLine);
196  it.m_uiTopChar = 0;
197  }
198 
199  if (PCLI_Out != NULL)
200  {
201  PrintBottomLine(it, *PCLI_Out);
202  }
203  else
204  {
205  it.m_uiBottomChar += it.m_cliScreenInfo.GetSafeWidth();
206  if (it.m_uiBottomChar >= m_tkLines.GetAt(it.m_tkBottomLine).GetLength())
207  {
208  m_tkLines.MoveNext(it.m_tkBottomLine);
209  it.m_uiBottomChar = 0;
210  }
211  }
212 
213  return true;
214  }
215  }
216 
217  return false;
218  }
219 
220  const bool Text::PageDown(TextIterator& it, const OutputDevice* const PCLI_Out) const
221  {
222  // Try to skeep as many lines as the screen is high -1 for the status line.
223  for (unsigned int ui = 0; ui < it.m_uiPageHeight; ui ++)
224  {
225  if (! LineDown(it, PCLI_Out))
226  {
227  // If the display could not be moved one single line down, return false.
228  return (ui > 0);
229  }
230  }
231 
232  return true;
233  }
234 
235  void Text::End(TextIterator& it, const OutputDevice* const PCLI_Out) const
236  {
237  // Keep moving one line down until it stucks.
238  while (LineDown(it, PCLI_Out));
239  }
240 
241  void Text::PrintPage(TextIterator& it, const OutputDevice& CLI_Out, const bool B_FillPageWithBlankLines) const
242  {
243  // Align current bottom position with current top position.
244  it.m_tkBottomLine = it.m_tkTopLine;
245  it.m_uiBottomChar = it.m_uiTopChar;
246  it.m_bBottomIsUpToDate = true; // Not already true, but the flag is set right away.
247 
248  // Then let current bottom position move one page down while printing the page.
249  unsigned int ui_LineCount = 0;
250  for ( ;
251  m_tkLines.IsValid(it.m_tkBottomLine) // While there are still lines to display
252  && (ui_LineCount < it.m_uiPageHeight); // Fill the page
253  ui_LineCount ++)
254  {
255  PrintBottomLine(it, CLI_Out);
256  }
257  // Possibly finish with blank lines.
258  if (B_FillPageWithBlankLines)
259  {
260  for (; ui_LineCount < it.m_uiPageHeight; ui_LineCount ++)
261  {
262  CLI_Out << endl;
263  }
264  }
265  }
266 
267  void Text::PrintBottomLine(TextIterator& it, const OutputDevice& CLI_Out) const
268  {
269  for ( unsigned int ui_CharCount = 0;
270  m_tkLines.IsValid(it.m_tkBottomLine); // While there are still lines to display.
271  ui_CharCount ++)
272  {
273  const tk::String tk_Line = m_tkLines.GetAt(it.m_tkBottomLine);
274 
275  if (it.m_uiBottomChar >= tk_Line.GetLength())
276  {
277  // End of line.
278  CLI_ASSERT(it.m_cliScreenInfo.GetSafeWidth() > 0);
279  if ((! it.m_cliScreenInfo.GetbWrapLines())
280  // When line wrapping is on, a line ending at the right end of the screen should not cause an empty line below.
281  // Thus, print a carriage return only in the following cases:
282  || (it.m_uiBottomChar == 0) // Regular empty line.
283  || (it.m_cliScreenInfo.GetSafeWidth() <= 0) // (Avoid invalid modulo, should never occur however)
284  || (it.m_uiBottomChar % it.m_cliScreenInfo.GetSafeWidth() != 0)) // Line not ending at the right end the screen.
285  {
286  CLI_Out << endl;
287  }
288  m_tkLines.MoveNext(it.m_tkBottomLine);
289  it.m_uiBottomChar = 0;
290  break;
291  }
292  else if (ui_CharCount >= it.m_cliScreenInfo.GetSafeWidth())
293  {
294  // Out of line.
295  if (! it.m_cliScreenInfo.GetbWrapLines())
296  {
297  CLI_Out << endl;
298  }
299  break;
300  }
301  else
302  {
303  // Regular character display.
304  CLI_Out << tk_Line.GetChar(it.m_uiBottomChar);
305  it.m_uiBottomChar ++;
306  }
307  }
308  }
309 
310 
311  static const tk::Queue<tk::String>::Iterator InitIterator(void)
312  {
313  tk::Queue<tk::String> it(0);
314  return it.GetIterator();
315  }
316 
317  TextIterator::TextIterator(const OutputDevice::ScreenInfo& CLI_ScreenInfo, const unsigned int UI_PageHeight)
318  : m_cliScreenInfo(CLI_ScreenInfo), m_uiPageHeight(UI_PageHeight),
319  m_tkTopLine(InitIterator()), m_uiTopChar(0),
320  m_tkBottomLine(InitIterator()), m_uiBottomChar(0), m_bBottomIsUpToDate(false)
321  {
322  }
323 
324  TextIterator::TextIterator(const TextIterator& it)
325  : m_cliScreenInfo(it.m_cliScreenInfo), m_uiPageHeight(it.m_uiPageHeight),
326  m_tkTopLine(it.m_tkTopLine), m_uiTopChar(it.m_uiTopChar),
327  m_tkBottomLine(it.m_tkBottomLine), m_uiBottomChar(it.m_uiBottomChar), m_bBottomIsUpToDate(it.m_bBottomIsUpToDate)
328  {
329  }
330 
331  CLI_NS_END(ui)
332 
334 
static OutputDevice & GetNullDevice(void)
Null device singleton.
Main namespace of the CLI library.
#define CLI_ASSERT(a)
CLI assertion macro.
Definition: assert.h:49
const bool GetbWrapLines(void) const
Line wrapping characteristic accessor.
Definition: io_device.h:526
void Begin(TextIterator &it) const
Retrieves a text head iterator.
Definition: ui_text.cpp:105
virtual const bool WouldOutput(const OutputDevice &CLI_Device) const
Stack overflow protection.
Text class definition.
virtual const bool WouldOutput(const OutputDevice &CLI_Device) const
Stack overflow protection.
Definition: ui_text.cpp:95
#define CLI_NS_END(__ns)
End a namespace definition.
Definition: namespace.h:45
virtual const bool CloseDevice(void)
Device closure handler.
Definition: ui_text.cpp:53
virtual void Beep(void) const
Beep handler.
Definition: ui_text.cpp:83
virtual void CleanScreen(void) const
Clean screen.
Definition: ui_text.cpp:88
Assertion facilities.
Screen information.
Definition: io_device.h:461
const bool PageUp(TextIterator &it) const
Moves iterator one page up.
Definition: ui_text.cpp:113
virtual void PutString(const char *const STR_Out) const
Output handler.
Definition: ui_text.cpp:58
CLI library default pre-compiled header.
virtual const bool OpenDevice(void)
Device opening handler.
Definition: ui_text.cpp:48
Generic output device.
Definition: io_device.h:256
void End(TextIterator &it, const OutputDevice *const PCLI_Out) const
Retrieves a text end iterator.
Definition: ui_text.cpp:235
#define CLI_NS_BEGIN(__ns)
Begin a namespace definition.
Definition: namespace.h:38
const bool LineDown(TextIterator &it, const OutputDevice *const PCLI_Out) const
Moves iterator one line down.
Definition: ui_text.cpp:177
const bool LineUp(TextIterator &it) const
Moves iterator one line up.
Definition: ui_text.cpp:128
const unsigned int GetSafeWidth(void) const
Safe screen width accessor.
Definition: io_device.h:514
const bool PageDown(TextIterator &it, const OutputDevice *const PCLI_Out) const
Moves iterator one page down.
Definition: ui_text.cpp:220
Text iterator class.
Definition: ui_text.h:145
virtual ~Text(void)
Destructor.
Definition: ui_text.cpp:44
const IOEndl endl
The common IOEndl object.
Simple line user interface object.
Definition: ui_text.h:47
void PrintPage(TextIterator &it, const OutputDevice &CLI_Out, const bool B_FillPageWithBlankLines) const
Print out a page of text.
Definition: ui_text.cpp:241