/* Functions to format text into lines of a specified length. Copyright (C) 2006 Jussi Peltola 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 #include #include #include 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; }