/*
Functions to format text into lines of a specified length.
Copyright (C) 2006 Jussi Peltola <pelzi@pelzi.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <cassert>
#include <iostream>
#include <string>
#include <vector>
int find_line_end(const std::string& message, std::string::size_type start, int screen_width);
int calculate_height(const std::string& message, int screen_width, int indent)
{
/* Calculates the amount of lines format will break message into. */
assert(screen_width > 0);
if (screen_width - indent < 4)
indent = 0;
std::string::size_type i = 0; /* The current character */
int lines = 0; /* Line count */
while (i < message.length())
{
/* Find out where this line ends. */
if (i == 0) /* The first line is not indented. */
i = find_line_end(message, i, screen_width);
else
i = find_line_end(message, i, screen_width - indent);
lines++;
i++; /* The beginning of the new line is one after the end of the previous. */
/* Skip whitespace for the next line */
for (; i < message.length() && isspace(message[i]); i++);
/* If the previous loop got to the end, the rest is just whitespace
* The while condition takes care of that. */
}
return lines;
}
int find_line_end(const std::string& message, std::string::size_type start, int screen_width)
{
/* Helper function.
* Finds the position of the last character which is the last character of a word
* that fits in its entirety in screen_width.
*/
assert(screen_width > 0);
assert(start < message.length());
/* Is the tail shorter than or equal to the width? */
if (start + screen_width >= message.length())
return message.length()-1;
std::string::size_type i = -1;
const std::string::size_type too_long_word_length = screen_width / 2;
/* The tail is longer than the width, so message[start+screen_width] is a valid offset.
* If message[start+screen_width], which is the character after the maximum length of the line
* is whitespace, this loop does nothing, since the whole line fits.
* If there is a word there, the loop will go back until it sees whitespace.
* However, there is also the possibility of very long words.
* If the following moves closer than screen_width/2 to start, it is reasonable to forcibly
* break the line.
*/
for (i = start+screen_width; i - start > too_long_word_length && !isspace(message[i]); i--);
/* If the last word was too long, forcibly break it */
if (i - start <= too_long_word_length)
return start+screen_width-1;
/* Ignore trailing whitespace */
for (; isspace(message[i]); i--);
/* i now points at the last character. */
return i;
}
int main()
{
std::string msg = "aaaa bb cc dd ee ff gggg";
std::cout << msg.length() << std::endl;
int h = calculate_height(msg, 4, 2);
std::cout << h << std::endl;
msg = "aaaa bbcc ddeeff gggg";
h = calculate_height(msg, 5, 3);
std::cout << h << std::endl;
h = calculate_height("aaaaaaaaaaabbbbbbbbbbbbbbcccccccccc", 5, 3);
std::cout << h << std::endl;
}