Code Search for Developers
 
 
  

Font.cpp from FreeOrion at Krugle


Show Font.cpp syntax highlighted

/* GG is a GUI for SDL and OpenGL.
   Copyright (C) 2003 T. Zachary Laine

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public License
   as published by the Free Software Foundation; either version 2.1
   of the License, or (at your option) any later version.
   
   This library 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
   Lesser General Public License for more details.
    
   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA

   If you do not wish to comply with the terms of the LGPL please
   contact the author as other terms are available for a fee.
    
   Zach Laine
   whatwasthataddress@hotmail.com */

#include <GG/Font.h>

#include <GG/GUI.h>
#include <GG/Base.h>
#include <GG/DrawUtil.h>

#include <ft2build.h>
#include FT_FREETYPE_H

#include <boost/lexical_cast.hpp>
#include <boost/spirit.hpp>

#include <cmath>
#include <numeric>
#include <sstream>

#define DEBUG_DETERMINELINES 0

using namespace GG;

namespace {
    int NextPowerOfTwo(int input)
    {
        int value = 1;
        while (value < input)
            value <<= 1;
        return value;
    }

    inline FT_ULong CharToFT_ULong(char c)
    {
        return static_cast<FT_ULong>(c < 0 ? static_cast<unsigned char>(256 + c) : static_cast<unsigned char>(c));
    }

    /** This is used to collect data on the glyphs as they are recorded into buffers, for use in creating Glyph objects at the end
        of Font's constructor.*/
    struct TempGlyphData
    {
        TempGlyphData() {} ///< default ctor
        TempGlyphData(int i, int _x1, int _y1, int _x2, int _y2, int lb, int a) : idx(i), x1(_x1), y1(_y1), x2(_x2), y2(_y2), left_b(lb), adv(a) {} ///< ctor
        int      idx;              ///< index into m_textures of texture that contains this glyph
        int      x1, y1, x2, y2;   ///< area of glyph subtexture within texture
        int      left_b;           ///< left bearing (see Glyph)
        int      adv;              ///< advance of glyph (see Glyph)
    };

    struct FTLibraryWrapper
    {
        FTLibraryWrapper() : library(0)
        {
            if (!library && FT_Init_FreeType(&library)) // if no library exists and we can't create one...
                throw FailedFTLibraryInit("Unable to initialize FreeType font library object");
        }
        ~FTLibraryWrapper() {FT_Done_FreeType(library);}
        FT_Library library;
    } g_library;

    struct AppendToken
    {
        AppendToken(std::vector<std::string>& token_vec) : tokens(token_vec) {}
        void operator()(const char* first, const char* last) const {tokens.push_back(std::string(first, last));}
    private:
        std::vector<std::string>& tokens;
    };

    struct HandlePreTagFunctor
    {
        HandlePreTagFunctor(std::vector<boost::shared_ptr<Font::TextElement> >& text_elements, bool& ignore_tag_status, bool close_tag) :
            elements(text_elements), ignore_tags(ignore_tag_status), close(close_tag) {}
        void operator()(const char* first, const char* last) const
            {
                if (ignore_tags && !close) {
                    boost::shared_ptr<Font::TextElement> element(new Font::TextElement(false, false));
                    element->text = std::string(first, last);
                    elements.push_back(element);
                } else {
                    boost::shared_ptr<Font::FormattingTag> element(new Font::FormattingTag(close));
                    element->text = "pre";
                    element->original_tag_text = std::string(first, last);
                    elements.push_back(element);
                    ignore_tags = !close;
                }
            }
    private:
        std::vector<boost::shared_ptr<Font::TextElement> >& elements;
        bool& ignore_tags;
        const bool close;
    };

    struct HandleTextFunctor
    {
        HandleTextFunctor(std::vector<boost::shared_ptr<Font::TextElement> >& text_elements) : elements(text_elements) {}
        void operator()(const char* first, const char* last) const
            {
                boost::shared_ptr<Font::TextElement> element(new Font::TextElement(false, false));
                element->text = std::string(first, last);
                elements.push_back(element);
            }
    private:
        std::vector<boost::shared_ptr<Font::TextElement> >& elements;
    };

    boost::spirit::rule<> ws_p = (*boost::spirit::blank_p >> (boost::spirit::eol_p | '\n' | '\r' | '\f')) | +boost::spirit::blank_p;

    struct HandleWhitespaceFunctor
    {
        HandleWhitespaceFunctor(std::vector<boost::shared_ptr<Font::TextElement> >& text_elements) : elements(text_elements) {}
        void operator()(const char* first, const char* last) const
            {
                std::vector<std::string> string_vec;
                using namespace boost::spirit;
                std::string ws_str = std::string(first, last);
                parse(ws_str.c_str(), ws_p[AppendToken(string_vec)]);
                for (unsigned int i = 0; i < string_vec.size(); ++i) {
                    boost::shared_ptr<Font::TextElement> element(new Font::TextElement(true, false));
                    element->text = std::string(first, last);
                    elements.push_back(element);
                    if (string_vec[i].substr(string_vec[i].size() - 1).find_first_of("\n\f\r") != std::string::npos) {
                        boost::shared_ptr<Font::TextElement> element(new Font::TextElement(false, true));
                        elements.push_back(element);
                    }
                }
            }
    private:
        std::vector<boost::shared_ptr<Font::TextElement> >& elements;
    };

    void SetJustification(bool& last_line_of_curr_just, Font::LineData& line_data, Alignment orig_just, Alignment prev_just)
    {
        if (last_line_of_curr_just) {
            line_data.justification = orig_just;
            last_line_of_curr_just = false;
        } else {
            line_data.justification = prev_just;
        }
    }

    const double ITALICS_SLANT_ANGLE = 12; // degrees
    const double ITALICS_FACTOR = 1.0 / tan((90 - ITALICS_SLANT_ANGLE) * 3.1415926 / 180.0); // factor used to shear glyphs ITALICS_SLANT_ANGLE degrees CW from straight up

    const int FT_MAGIC_NUMBER = 4; // taken from the ftview FreeType demo (I have no idea....)
}


///////////////////////////////////////
// function GG::RgbaTag
///////////////////////////////////////
std::string GG::RgbaTag(const Clr& c)
{
    std::stringstream stream;
    stream << "<rgba " << static_cast<int>(c.r) << " " << static_cast<int>(c.g) << " " << 
        static_cast<int>(c.b) << " " << static_cast<int>(c.a) << ">";
    return stream.str();
}


///////////////////////////////////////
// TextFormat
///////////////////////////////////////
const TextFormat GG::FORMAT_NONE         (0);
const TextFormat GG::FORMAT_VCENTER      (1 << 0);
const TextFormat GG::FORMAT_TOP          (1 << 1);
const TextFormat GG::FORMAT_BOTTOM       (1 << 2);
const TextFormat GG::FORMAT_CENTER       (1 << 3);
const TextFormat GG::FORMAT_LEFT         (1 << 4);
const TextFormat GG::FORMAT_RIGHT        (1 << 5);
const TextFormat GG::FORMAT_WORDBREAK    (1 << 6);
const TextFormat GG::FORMAT_LINEWRAP     (1 << 7);
const TextFormat GG::FORMAT_IGNORETAGS   (1 << 8);

GG_FLAGSPEC_IMPL(TextFormat);

namespace {
    bool RegisterTextFormats()
    {
        FlagSpec<TextFormat>& spec = FlagSpec<TextFormat>::instance();
        spec.insert(FORMAT_NONE, "FORMAT_NONE", true);
        spec.insert(FORMAT_VCENTER, "FORMAT_VCENTER", true);
        spec.insert(FORMAT_TOP, "FORMAT_TOP", true);
        spec.insert(FORMAT_BOTTOM, "FORMAT_BOTTOM", true);
        spec.insert(FORMAT_CENTER, "FORMAT_CENTER", true);
        spec.insert(FORMAT_LEFT, "FORMAT_LEFT", true);
        spec.insert(FORMAT_RIGHT, "FORMAT_RIGHT", true);
        spec.insert(FORMAT_WORDBREAK, "FORMAT_WORDBREAK", true);
        spec.insert(FORMAT_LINEWRAP, "FORMAT_LINEWRAP", true);
        spec.insert(FORMAT_IGNORETAGS, "FORMAT_IGNORETAGS", true);
        return true;
    }
    bool dummy = RegisterTextFormats();
}


///////////////////////////////////////
// class GG::Font::TextElement
///////////////////////////////////////
Font::TextElement::TextElement() :
    whitespace(false),
    newline(false)
{
}

Font::TextElement::TextElement(bool ws, bool nl) :
    whitespace(ws),
    newline(nl)
{
}

Font::TextElement::~TextElement()
{
}

int Font::TextElement::Width() const
{
    return std::accumulate(widths.begin(), widths.end(), 0);
}

Font::TextElement::TextElementType Font::TextElement::Type() const
{
    return newline ? NEWLINE : (whitespace ? WHITESPACE : TEXT);
}

int Font::TextElement::OriginalStringChars() const
{
    return text.size();
}


///////////////////////////////////////
// class GG::Font::FormattingTag
///////////////////////////////////////
Font::FormattingTag::FormattingTag() :
    TextElement(),
    close_tag(false)
{
}

Font::FormattingTag::FormattingTag(bool close) :
    TextElement(false, false),
    close_tag(close)
{
}

Font::FormattingTag::TextElementType Font::FormattingTag::Type() const
{
    return close_tag ? CLOSE_TAG : OPEN_TAG;
}

int Font::FormattingTag::OriginalStringChars() const
{
    return original_tag_text.size();
}


///////////////////////////////////////
// class GG::Font::LineData
///////////////////////////////////////
Font::LineData::LineData() :
    justification(ALIGN_CENTER)
{
}

int Font::LineData::Width() const
{
    return char_data.empty() ? 0 : char_data.back().extent;
}

bool Font::LineData::Empty() const
{
    return char_data.empty();
}

///////////////////////////////////////
// class GG::Font::RenderState
///////////////////////////////////////
Font::RenderState::RenderState() :
    ignore_tags(false),
    use_italics(false),
    draw_underline(false),
    color_set(false)
{}


///////////////////////////////////////
// class GG::Font::LineData::CharData
///////////////////////////////////////
Font::LineData::CharData::CharData() :
    extent(0),
    original_char_index(0)
{
}

Font::LineData::CharData::CharData(int extent_, int original_index, const std::vector<boost::shared_ptr<TextElement> >& tags_) :
    extent(extent_),
    original_char_index(original_index),
    tags()
{
    for (unsigned int i = 0; i < tags_.size(); ++i) {
        tags.push_back(boost::dynamic_pointer_cast<FormattingTag>(tags_[i]));
    }
}


///////////////////////////////////////
// struct GG::Font::HandleTagFunctor
///////////////////////////////////////
struct Font::HandleTagFunctor
{
    HandleTagFunctor(std::vector<boost::shared_ptr<Font::TextElement> >& text_elements, const bool& ignore_tag_status, bool close_tag) :
        elements(text_elements), ignore_tags(ignore_tag_status), close(close_tag) {}
    void operator()(const char* first, const char* last) const
        {
            std::string tag_str = std::string(first, last);
            using namespace boost::spirit;
            std::vector<std::string> param_vec;
            rule<> tag_begin = ch_p('<');
            if (close)
                tag_begin = str_p("</");
            rule<> tag_token_parser = tag_begin >> +(*space_p >> (+(anychar_p - (space_p | '>')))[AppendToken(param_vec)]) >> '>';
            parse(tag_str.c_str(), tag_token_parser);
            if (!ignore_tags && Font::s_known_tags.find(param_vec.front()) != Font::s_known_tags.end()) {
                boost::shared_ptr<Font::FormattingTag> element(new Font::FormattingTag(close));
                element->text = param_vec.front();
                if (!close)
                    element->params.insert(element->params.end(), param_vec.begin() + 1, param_vec.end());
                element->original_tag_text = tag_str;
                elements.push_back(element);
            } else {
                boost::shared_ptr<Font::TextElement> text_element(new Font::TextElement(false, false));
                text_element->text = tag_str;
                elements.push_back(text_element);
            }
        }
private:
    std::vector<boost::shared_ptr<Font::TextElement> >& elements;
    const bool& ignore_tags;
    const bool close;
};


///////////////////////////////////////
// class GG::Font
///////////////////////////////////////
std::set<std::string> Font::s_action_tags;
std::set<std::string> Font::s_known_tags;

Font::Font() :
    m_pt_sz(0),
    m_glyph_range(0),
    m_ascent(0),
    m_descent(0),
    m_height(0),
    m_lineskip(0),
    m_underline_offset(0.0),
    m_underline_height(0.0),
    m_italics_offset(0.0),
    m_space_width(0)
{
}

Font::Font(const std::string& font_filename, int pts, unsigned int range/* = ALL_DEFINED_RANGES*/) :
    m_font_filename(font_filename),
    m_pt_sz(pts),
    m_glyph_range(range),
    m_ascent(0),
    m_descent(0),
    m_height(0),
    m_lineskip(0),
    m_underline_offset(0.0),
    m_underline_height(0.0),
    m_italics_offset(0.0),
    m_space_width(0)
{
    if (font_filename != "")
        Init(font_filename, pts, range);
}

Font::~Font()
{
}

const std::string& Font::FontName() const     
{
    return m_font_filename;
}

int Font::PointSize() const    
{
    return m_pt_sz;
}

int Font::GetGlyphRange() const
{
    return m_glyph_range;
}

int Font::Ascent() const       
{
    return m_ascent;
}

int Font::Descent() const      
{
    return m_descent;
}

int Font::Height() const       
{
    return m_height;
}

int Font::Lineskip() const     
{
    return m_lineskip;
}

int Font::SpaceWidth() const   
{
    return m_space_width;
}

int Font::RenderGlyph(const Pt& pt, char c) const
{
    return RenderGlyph(pt.x, pt.y, c);
}

int Font::RenderGlyph(int x, int y, char c) const
{
    std::map<FT_ULong, Glyph>::const_iterator it = m_glyphs.find(CharToFT_ULong(c));
    if (it == m_glyphs.end())
        it = m_glyphs.find(CharToFT_ULong(' ')); // print a space when an unrendered glyph is requested
    return RenderGlyph(x, y, it->second, 0);
}

int Font::RenderText(const Pt& pt, const std::string& text) const
{
    return RenderText(pt.x, pt.y, text);
}

int Font::RenderText(int x, int y, const std::string& text) const
{
    int orig_x = x;
    for (unsigned int i = 0; i < text.length(); ++i) {
        x += RenderGlyph(x, y, text[i]);
    }
    return x - orig_x;
}

void Font::RenderText(const Pt& pt1, const Pt& pt2, const std::string& text, Flags<TextFormat>& format, const std::vector<LineData>* line_data/* = 0*/,
                      RenderState* render_state/* = 0*/) const
{
    RenderText(pt1.x, pt1.y, pt2.x, pt2.y, text, format, line_data, render_state);
}

void Font::RenderText(int x1, int y1, int x2, int y2, const std::string& text, Flags<TextFormat>& format, const std::vector<LineData>* line_data/* = 0*/,
                      RenderState* render_state/* = 0*/) const
{
    RenderState state;
    if (!render_state)
        render_state = &state;

    // get breakdown of how text is divided into lines
    std::vector<LineData> lines;
    if (!line_data) {
        DetermineLines(text, format, x2 - x1, lines);
        line_data = &lines;
    }

    RenderText(x1, y1, x2, y2, text, format, *line_data, *render_state, 0, 0, line_data->size(), line_data->back().char_data.size());
}

void Font::RenderText(const Pt& pt1, const Pt& pt2, const std::string& text, Flags<TextFormat>& format, const std::vector<LineData>& line_data, RenderState& render_state,
                      int begin_line, int begin_char, int end_line, int end_char) const
{
    RenderText(pt1.x, pt1.y, pt2.x, pt2.y, text, format, line_data, render_state, begin_line, begin_char, end_line, end_char);
}

void Font::RenderText(int x1, int y1, int x2, int y2, const std::string& text, Flags<TextFormat>& format, const std::vector<LineData>& line_data, RenderState& render_state,
                      int begin_line, int begin_char, int end_line, int end_char) const
{
    double orig_color[4];
    glGetDoublev(GL_CURRENT_COLOR, orig_color);
    
    if (render_state.color_set)
        glColor(render_state.curr_color);

    int y_origin = y1; // default value for FORMAT_TOP
    if (format & FORMAT_BOTTOM)
        y_origin = y2 - ((end_line - begin_line - 1) * m_lineskip + m_height);
    else if (format & FORMAT_VCENTER)
        y_origin = y1 + static_cast<int>(((y2 - y1) - ((end_line - begin_line - 1) * m_lineskip + m_height)) / 2.0);

    for (int i = begin_line; i < end_line; ++i) {
        const LineData& line = line_data[i];
        int x_origin = x1; // default value for FORMAT_LEFT
        if (line.justification == ALIGN_RIGHT)
            x_origin = x2 - line.Width();
        else if (line.justification == ALIGN_CENTER)
            x_origin = x1 + static_cast<int>(((x2 - x1) - line.Width()) / 2.0);
        int y = y_origin + (i - begin_line) * m_lineskip;
        int x = x_origin;

        for (int j = ((i == begin_line) ? begin_char : 0); j < ((i == end_line - 1) ? end_char : static_cast<int>(line.char_data.size())); ++j) {
            for (unsigned int k = 0; k < line.char_data[j].tags.size(); ++k) {
                HandleTag(line.char_data[j].tags[k], orig_color, render_state);
            }
            if (text[line.char_data[j].original_char_index] == '\n')
                continue;
            FT_ULong c = CharToFT_ULong(text[line.char_data[j].original_char_index]);
            std::map<FT_ULong, Glyph>::const_iterator it = m_glyphs.find(c);
            if (it == m_glyphs.end())
                x = x_origin + line.char_data[j].extent; // move forward by the extent of the character when a whitespace or unprintable glyph is requested
            else
                x += RenderGlyph(x, y, it->second, &render_state);
        }
    }

    glColor4dv(orig_color);
}

Pt Font::DetermineLines(const std::string& text, Flags<TextFormat>& format, int box_width, std::vector<LineData>& line_data) const
{
    Pt retval;

    // correct any disagreements in the format flags
    int dup_ct = 0;   // duplication count
    if (format & FORMAT_LEFT) ++dup_ct;
    if (format & FORMAT_RIGHT) ++dup_ct;
    if (format & FORMAT_CENTER) ++dup_ct;
    if (dup_ct != 1) {   // exactly one must be picked; when none or multiples are picked, use FORMAT_LEFT by default
        format &= ~(FORMAT_RIGHT | FORMAT_CENTER);
        format |= FORMAT_LEFT;
    }
    dup_ct = 0;
    if (format & FORMAT_TOP) ++dup_ct;
    if (format & FORMAT_BOTTOM) ++dup_ct;
    if (format & FORMAT_VCENTER) ++dup_ct;
    if (dup_ct != 1) {   // exactly one must be picked; when none or multiples are picked, use FORMAT_TOP by default
        format &= ~(FORMAT_BOTTOM | FORMAT_VCENTER);
        format |= FORMAT_TOP;
    }
    if ((format & FORMAT_WORDBREAK) && (format & FORMAT_LINEWRAP))   // only one of these can be picked; FORMAT_WORDBREAK overrides FORMAT_LINEWRAP
        format &= ~FORMAT_LINEWRAP;

#if DEBUG_DETERMINELINES
    std::cout << "Font::DetermineLines(text=\"" << text << "\" format=" << format << " box_width=" << box_width << ")" << std::endl;
#endif

    std::vector<boost::shared_ptr<TextElement> > text_elements;
    using namespace boost::spirit;
    rule<> open_pre_tag_p = str_p("<pre>");
    rule<> close_pre_tag_p = str_p("</pre>");
    rule<> close_tag_p = str_p("</") >> +(anychar_p - '>') >> '>';
    rule<> open_tag_p = ch_p('<') >> +(anychar_p - '>') >> '>';
    rule<> text_p = +(anychar_p - (close_tag_p | open_tag_p | space_p));
    if (format & FORMAT_IGNORETAGS) {
        text_p = +(anychar_p - space_p);
        rule<> lines_parser = *(text_p[HandleTextFunctor(text_elements)] |
                                ws_p[HandleWhitespaceFunctor(text_elements)]);
        parse(text.c_str(), lines_parser);
    } else {
        bool ignore_tags = false;
        rule<> lines_parser = *(open_pre_tag_p[HandlePreTagFunctor(text_elements, ignore_tags, false)] |
                                close_pre_tag_p[HandlePreTagFunctor(text_elements, ignore_tags, true)] |
                                close_tag_p[HandleTagFunctor(text_elements, ignore_tags, true)] |
                                open_tag_p[HandleTagFunctor(text_elements, ignore_tags, false)] |
                                text_p[HandleTextFunctor(text_elements)] |
                                ws_p[HandleWhitespaceFunctor(text_elements)]);
        parse(text.c_str(), lines_parser);
    }

    // fill in the widths of characters in the element std::strings
    for (unsigned int i = 0; i < text_elements.size(); ++i) {
        text_elements[i]->widths.resize(text_elements[i]->text.size());
        for (unsigned int j = 0; j < text_elements[i]->text.size(); ++j) {
            if (text_elements[i]->text[j] == '\n') {
                text_elements[i]->widths[j] = 0;
                continue;
            }
            FT_ULong c = text_elements[i]->text[j];
            std::map<FT_ULong, Glyph>::const_iterator it = m_glyphs.find(c);
            if (it == m_glyphs.end())
                it = m_glyphs.find(CharToFT_ULong(' ')); // use a space when an unrendered glyph is requested (the space chararacter is always renderable)
            text_elements[i]->widths[j] = it->second.advance;
        }
    }

#if DEBUG_DETERMINELINES
    std::cout << "parsing results:\n";
    for (unsigned int i = 0; i < text_elements.size(); ++i) {
        if (boost::shared_ptr<FormattingTag> tag_elem = boost::dynamic_pointer_cast<FormattingTag>(text_elements[i])) {
            std::cout << "FormattingTag\n    text=\"" << tag_elem->text << "\"\n    widths=" << StringFromContainer(tag_elem->widths)
                      << "\n    whitespace=" << tag_elem->whitespace << "\n    newline=" << tag_elem->newline << "\n    params=\n";
            for (unsigned int j = 0; j < tag_elem->params.size(); ++j) {
                std::cout << "        \"" << tag_elem->params[j] << "\"\n";
            }
            std::cout << "    original_tag_text=\"" << tag_elem->original_tag_text << "\"\n    close_tag=" << tag_elem->close_tag << "\n";
        } else {
            boost::shared_ptr<TextElement> elem = text_elements[i];
            std::cout << "TextElement\n    text=\"" << elem->text << "\"\n    widths=" << StringFromContainer(elem->widths)
                      << "\n    whitespace=" << elem->whitespace << "\n    newline=" << elem->newline << "\n";
        }
        std::cout << "\n";
    }
    std::cout << std::endl;
#endif

    RenderState render_state;
    int tab_width = 8; // default tab width
    int tab_pixel_width = tab_width * m_space_width; // get the length of a tab stop
    bool expand_tabs = format & FORMAT_LEFT; // tab expansion only takes place when the lines are left-justified (otherwise, tabs are just spaces)
    Alignment orig_just = ALIGN_NONE;
    if (format & FORMAT_LEFT)
        orig_just = ALIGN_LEFT;
    if (format & FORMAT_CENTER)
        orig_just = ALIGN_CENTER;
    if (format & FORMAT_RIGHT)
        orig_just = ALIGN_RIGHT;
    bool last_line_of_curr_just = false; // is this the last line of the current justification? (for instance when a </right> tag is encountered)

    line_data.clear();
    line_data.push_back(LineData());
    line_data.back().justification = orig_just;

    int x = 0;
    int original_string_offset = 0; // the position within the original string of the current TextElement
    std::vector<boost::shared_ptr<TextElement> > pending_formatting_tags;
    for (unsigned int i = 0; i < text_elements.size(); ++i) {
        boost::shared_ptr<TextElement> elem = text_elements[i];
        if (elem->Type() == TextElement::NEWLINE) { // if a newline is explicitly requested, start a new one
            line_data.push_back(LineData());
            SetJustification(last_line_of_curr_just, line_data.back(), orig_just, line_data[line_data.size() - 2].justification);
            x = 0; // reset the x-position to 0
        } else if (elem->Type() == TextElement::WHITESPACE) {
            for (unsigned int j = 0; j < elem->text.size(); ++j) {
                FT_ULong c = CharToFT_ULong(elem->text[j]);
                if (c != '\r' && c != '\f') {
                    int advance_position = x + m_space_width;
                    if (c == '\t' && expand_tabs) {
                        advance_position = (((x / tab_pixel_width) + 1) * tab_pixel_width);
                    } else if (c == '\n') {
                        advance_position = x;
                    }
                    int advance = advance_position - x;
                    if ((format & FORMAT_LINEWRAP) && box_width < advance_position) { // if we're using linewrap and this space won't fit on this line,
                        if (!x && box_width < advance) {
                            // if the space is larger than the line and alone on the line, let the space overrun this
                            // line and then start a new one
                            line_data.push_back(LineData());
                            x = 0; // reset the x-position to 0
                            SetJustification(last_line_of_curr_just, line_data.back(), orig_just, line_data[line_data.size() - 2].justification);
                        } else { // otherwise start a new line and put the space there:
                            line_data.push_back(LineData());
                            x = advance;
                            line_data.back().char_data.push_back(LineData::CharData(x, original_string_offset + j, pending_formatting_tags));
                            pending_formatting_tags.clear();
                            SetJustification(last_line_of_curr_just, line_data.back(), orig_just, line_data[line_data.size() - 2].justification);
                        }
                    } else { // there's room for the space, or we're not using linewrap
                        x += advance;
                        line_data.back().char_data.push_back(LineData::CharData(x, original_string_offset + j, pending_formatting_tags));
                        pending_formatting_tags.clear();
                    }
                }
            }
        } else if (elem->Type() == TextElement::TEXT) {
            if (format & FORMAT_WORDBREAK) {
                if (box_width < x + elem->Width() && x) { // if the text "word" overruns this line, and isn't alone on this line, move it down to the next line
                    line_data.push_back(LineData());
                    x = 0;
                    SetJustification(last_line_of_curr_just, line_data.back(), orig_just, line_data[line_data.size() - 2].justification);
                }
                for (unsigned int j = 0; j < elem->text.size(); ++j) {
                    x += elem->widths[j];
                    line_data.back().char_data.push_back(LineData::CharData(x, original_string_offset + j, pending_formatting_tags));
                    pending_formatting_tags.clear();
                }
            } else {
                for (unsigned int j = 0; j < elem->text.size(); ++j) {
                    if ((format & FORMAT_LINEWRAP) && box_width < x + elem->widths[j] && x) { // if the char overruns this line, and isn't alone on this line, move it down to the next line
                        line_data.push_back(LineData());
                        x = elem->widths[j];
                        line_data.back().char_data.push_back(LineData::CharData(x, original_string_offset + j, pending_formatting_tags));
                        pending_formatting_tags.clear();
                        SetJustification(last_line_of_curr_just, line_data.back(), orig_just, line_data[line_data.size() - 2].justification);
                    } else { // there's room for this char on this line, or there's no wrapping in use
                        x += elem->widths[j];
                        line_data.back().char_data.push_back(LineData::CharData(x, original_string_offset + j, pending_formatting_tags));
                        pending_formatting_tags.clear();
                    }
                }
            }
        } else if (elem->Type() == TextElement::OPEN_TAG) {
            if (elem->text == "left")
                line_data.back().justification = ALIGN_LEFT;
            else if (elem->text == "center")
                line_data.back().justification = ALIGN_CENTER;
            else if (elem->text == "right")
                line_data.back().justification = ALIGN_RIGHT;
            else if (elem->text != "pre")
                pending_formatting_tags.push_back(elem);
            last_line_of_curr_just = false;
        } else if (elem->Type() == TextElement::CLOSE_TAG) {
            if ((elem->text == "left" && line_data.back().justification == ALIGN_LEFT) ||
                (elem->text == "center" && line_data.back().justification == ALIGN_CENTER) ||
                (elem->text == "right" && line_data.back().justification == ALIGN_RIGHT))
                last_line_of_curr_just = true;
            else if (elem->text != "pre")
                pending_formatting_tags.push_back(elem);
        }
        original_string_offset += elem->OriginalStringChars();
    }
    // disregard the final pending formatting tag, if any, since this is the end of the text, and so it cannot have any effect

#if DEBUG_DETERMINELINES
    std::cout << "Line breakdown:\n";
    for (unsigned int i = 0; i < line_data.size(); ++i) {
        std::cout << "Line " << i << ":\n    extents=";
        for (unsigned int j = 0; j < line_data[i].char_data.size(); ++j) {
            std::cout << line_data[i].char_data[j].extent << " ";
        }
        std::cout << "\n    chars on line: \"";
        for (unsigned int j = 0; j < line_data[i].char_data.size(); ++j) {
            std::cout << text[line_data[i].char_data[j].original_char_index];
        }
        std::cout << "\"\n" << std::endl;
        for (unsigned int j = 0; j < line_data[i].char_data.size(); ++j) {
            for (unsigned int k = 0; k < line_data[i].char_data[j].tags.size(); ++k) {
                if (boost::shared_ptr<FormattingTag> tag_elem = line_data[i].char_data[j].tags[k]) {
                    std::cout << "FormattingTag @" << j << "\n    text=\"" << tag_elem->text << "\"\n    widths="
                              << StringFromContainer(tag_elem->widths) << "\n    whitespace=" << tag_elem->whitespace
                              << "\n    newline=" << tag_elem->newline << "\n    params=\n";
                    for (unsigned int l = 0; l < tag_elem->params.size(); ++l) {
                        std::cout << "        \"" << tag_elem->params[l] << "\"\n";
                    }
                    std::cout << "    original_tag_text=\"" << tag_elem->original_tag_text << "\"\n    close_tag="
                              << tag_elem->close_tag << std::endl;
                }
            }
        }
        std::cout << "\n    justification=" << line_data[i].justification << std::endl;
    }
#endif

    for (unsigned int i = 0; i < line_data.size(); ++i) {
        if (retval.x < line_data[i].Width())
            retval.x = line_data[i].Width();
    }
    retval.y = text.empty() ? 0 : (static_cast<int>(line_data.size()) - 1) * m_lineskip + m_height;

#if DEBUG_DETERMINELINES
    std::cout << "String Size:(" << retval.x << ", " << retval.y << ")\n" << std::endl;
#endif

    return retval;
}

Pt Font::TextExtent(const std::string& text, Flags<TextFormat> format/* = FORMAT_NONE*/, int box_width/* = 0*/) const
{
    std::vector<LineData> lines;
    return DetermineLines(text, format, box_width ? box_width : 1 << 15, lines);
}

void Font::RegisterKnownTag(const std::string& tag)
{
    s_known_tags.insert(tag);
}

void Font::RemoveKnownTag(const std::string& tag)
{
    if (s_action_tags.find(tag) == s_action_tags.end())
        s_known_tags.erase(tag);
}

void Font::ClearKnownTags()
{
    s_action_tags.clear();
    s_action_tags.insert("i");
    s_action_tags.insert("u");
    s_action_tags.insert("rgba");
    s_action_tags.insert("left");
    s_action_tags.insert("center");
    s_action_tags.insert("right");
    s_action_tags.insert("pre");
    s_known_tags = s_action_tags;
}

void Font::Init(const std::string& font_filename, int pts, unsigned int range)
{
    if (s_action_tags.empty()) // if this is the first Font to get initialized, it needs to initialize some static members
        ClearKnownTags();

    FT_Error error;
    FT_Face face;
    FT_Fixed scale;

    // Open the font and create ancillary data
    error = FT_New_Face(g_library.library, font_filename.c_str(), 0, &face);
    if (error || !face)
        throw BadFile("Face object created from \'" + font_filename + "\' was invalid");
    if (pts <= 0)
        throw InvalidPointSize("Attempted to create font \'" + font_filename + "\' with non-positive point size");
    if (range > ALL_CHARS)
        throw InvalidRangeFlags("Attempted to create font \'" + font_filename + "\' with invalid range flags");
    if (!FT_IS_SCALABLE(face))
        throw UnscalableFont("Attempted to create font \'" + font_filename + "\' with uscalable font face");
    // Set the character size and use default 72 DPI
    if (FT_Set_Char_Size(face, 0, pts * 64, 0, 0)) // if error is returned
        throw BadPointSize("Could not set font size while attempting to create font \'" + font_filename + "\'");

    // Get the scalable font metrics for this font
    scale = face->size->metrics.y_scale;
    m_ascent  = static_cast<int>(face->size->metrics.ascender / 64.0); // convert from fixed-point 26.6 format
    m_descent  = static_cast<int>(face->size->metrics.descender / 64.0); // convert from fixed-point 26.6 format
    m_height  = m_ascent - m_descent + 1;
    m_lineskip = static_cast<int>(face->size->metrics.height / 64.0);
    // underline info
    m_underline_offset = std::floor(FT_MulFix(face->underline_position, scale) / 64.0);
    m_underline_height = std::ceil(FT_MulFix(face->underline_thickness, scale) / 64.0);
    if (m_underline_height < 1.0) {
        m_underline_height = 1.0;
    }
    // italics info
    m_italics_offset = ITALICS_FACTOR * m_height / 2.0;

    // these are the ranges of interest in the ASCII table that correspond to the values of GlyphRange
    enum {NUMBER_START = 0x30, NUMBER_STOP = 0x3A, ALPHA_UPPER_START = 0x41, ALPHA_UPPER_STOP = 0x5B, ALPHA_LOWER_START = 0x61,
          ALPHA_LOWER_STOP = 0x7B, SYMBOL_START1 = 0x21, SYMBOL_STOP1 = 0x30, SYMBOL_START2 = 0x3A, SYMBOL_STOP2 = 0x41,
          SYMBOL_START3 = 0x5B, SYMBOL_STOP3 = 0x61, SYMBOL_START4 = 0x7B, SYMBOL_STOP4 = 0x7F};

    std::vector<unsigned int> range_vec;
    if (range < ALL_CHARS) { // if certain specified ranges of glyphs are to be used
        range_vec.push_back(0x20); // the space character is always needed
        range_vec.push_back(0x21);
        if (range & NUMBER) {
            range_vec.push_back(NUMBER_START);        // '0'
            range_vec.push_back(NUMBER_STOP);         // '9' + 1
        }
        if (range & ALPHA_UPPER) {
            range_vec.push_back(ALPHA_UPPER_START);   // 'A'
            range_vec.push_back(ALPHA_UPPER_STOP);    // 'Z' + 1
        }
        if (range & ALPHA_LOWER) {
            range_vec.push_back(ALPHA_LOWER_START);   // 'a'
            range_vec.push_back(ALPHA_LOWER_STOP);    // 'z' + 1
        }
        if (range & SYMBOL) {
            range_vec.push_back(SYMBOL_START1);       // '!'
            range_vec.push_back(SYMBOL_STOP1);        // '/' + 1
            range_vec.push_back(SYMBOL_START2);       // ':'
            range_vec.push_back(SYMBOL_STOP2);        // '@' + 1
            range_vec.push_back(SYMBOL_START3);       // '['
            range_vec.push_back(SYMBOL_STOP3);        // '`' + 1
            range_vec.push_back(SYMBOL_START4);       // '{'
            range_vec.push_back(SYMBOL_STOP4);        // '~' + 1
        }
    } else { // all ASCII glyphs are to be used, even non-printable ones
        range_vec.push_back(0x00);
        range_vec.push_back(0xFF);
    }

    // define default image buffer size
    const int BUF_WIDTH = 256;
    const int BUF_HEIGHT = 256;
    const int BUF_SZ = BUF_WIDTH * BUF_HEIGHT;

    // declare std::vector of image buffers into which we will copy glyph images and create first buffer
    std::vector<unsigned short*> buffer_vec; // 16 bpp: we are creating a luminance + alpha image
    std::vector<Pt> buffer_sizes;
    std::map<FT_ULong, TempGlyphData> temp_glyph_data;
    typedef unsigned short ushort;
    unsigned short* temp_buf = new ushort[BUF_SZ]; // 16 bpp: we are creating a luminance + alpha image
    for (int i = 0; i < BUF_SZ; ++i)
        temp_buf[i] = 0;
    buffer_vec.push_back(temp_buf);
    buffer_sizes.push_back(Pt(BUF_WIDTH, BUF_HEIGHT));

    int x = 0, y = 0, max_x = 0;
    while (!range_vec.empty()) {
        unsigned int high = range_vec.back();
        range_vec.pop_back();
        unsigned int low = range_vec.back();
        range_vec.pop_back();

        // copy glyph images
        for (unsigned int c = low; c < high; ++c) {
            if (GenerateGlyph(face, static_cast<FT_ULong>(c))) {
                const FT_Bitmap& glyph_bitmap = face->glyph->bitmap;

                if (x + glyph_bitmap.width >= BUF_WIDTH) { // start a new row of glyph images
                    if (x > max_x) max_x = x;
                    x = 0;
                    y += m_height;
                    if (y + m_height >= BUF_HEIGHT) { // if there's not enough room for another row, create a new buffer
                        // cut off bottom portion of buffer just written, if it is possible to do so and maintain power-of-two height
                        int pow_of_2_x = NextPowerOfTwo(max_x), pow_of_2_y = NextPowerOfTwo(y + m_height);
                        if (pow_of_2_y < buffer_sizes.back().y)
                            buffer_sizes.back().y = pow_of_2_y;
                        if (pow_of_2_x < buffer_sizes.back().x)
                            buffer_sizes.back().x = pow_of_2_x;
                        x = y = 0;
                        typedef unsigned short ushort;
                        temp_buf = new ushort[BUF_SZ];
                        for (int i = 0; i < BUF_SZ; ++i)
                            temp_buf[i] = 0;
                        buffer_vec.push_back(temp_buf);
                        buffer_sizes.push_back(Pt(BUF_WIDTH, BUF_HEIGHT));
                    }
                }

                unsigned char*  src_start = glyph_bitmap.buffer;
                unsigned short* dst_start = buffer_vec.back() + y * BUF_WIDTH + x;

                int y_offset = m_height - 1 + m_descent - face->glyph->bitmap_top + FT_MAGIC_NUMBER;

                for (int row = 0; row < glyph_bitmap.rows; ++row) {
                    unsigned char*  src = src_start + row * glyph_bitmap.pitch;
                    unsigned short* dst = dst_start + (row + y_offset) * BUF_WIDTH;
                    for (int col = 0; col < glyph_bitmap.width; ++col) {
#ifdef __BIG_ENDIAN__
                        *dst++ = *src++ | (255 << 8); // big-endian uses different byte ordering
#else
                        *dst++ = (*src++ << 8) | 255; // alpha is the value from glyph_bitmap; luminance is always 100% white
#endif
                    }
                }

                // record info on how to find and use this glyph later
                temp_glyph_data[c] = TempGlyphData(static_cast<int>(buffer_vec.size()) - 1,
                                                   x, y + FT_MAGIC_NUMBER, x + glyph_bitmap.width, y + m_height + FT_MAGIC_NUMBER,
                                                   static_cast<int>((std::ceil(face->glyph->metrics.horiBearingX / 64.0))), // convert from 26.6 fixed point format and round up
                                                   static_cast<int>((std::ceil(face->glyph->metrics.horiAdvance / 64.0)))); // convert from 26.6 fixed point format and round up

                // advance buffer write-position
                x += glyph_bitmap.width;
            }
        }
    }

    // cut off bottom portion of last buffer, if it is possible to do so and maintain power-of-two height
    if (x > max_x) max_x = x;
    int pow_of_2_x = NextPowerOfTwo(max_x), pow_of_2_y = NextPowerOfTwo(y + m_height);
    if (pow_of_2_y < buffer_sizes.back().y)
        buffer_sizes.back().y = pow_of_2_y;
    if (pow_of_2_x < buffer_sizes.back().x)
        buffer_sizes.back().x = pow_of_2_x;

    // create opengl texture from buffer(s) and release buffer(s)
    for (unsigned int i = 0; i < buffer_vec.size(); ++i) {
        boost::shared_ptr<Texture> temp_texture(new Texture);
        temp_texture->Init(0, 0, buffer_sizes[i].x, buffer_sizes[i].y, BUF_WIDTH, (unsigned char*)(buffer_vec[i]), GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 2);
        m_textures.push_back(temp_texture);
        delete [] buffer_vec[i];
    }

    // create Glyph objects from temp glyph data
    for (std::map<FT_ULong, TempGlyphData>::iterator it = temp_glyph_data.begin(); it != temp_glyph_data.end(); ++it)
        m_glyphs[it->first] = Glyph(m_textures[it->second.idx], it->second.x1, it->second.y1, it->second.x2, it->second.y2, it->second.left_b, it->second.adv);

    // record the width of the space character
    std::map<FT_ULong, Glyph>::const_iterator glyph_it = m_glyphs.find(CharToFT_ULong(' '));
    assert(glyph_it != m_glyphs.end());
    m_space_width = glyph_it->second.advance;
}

bool Font::GenerateGlyph(FT_Face face, FT_ULong ch)
{
    bool retval = true;

    // load the glyph
    if (!face)
        throw BadFace("GG::Font::GetGlyphBitmap : invalid font or font face");

    using boost::lexical_cast;
    FT_UInt index = FT_Get_Char_Index(face, ch);
    if (index) {
        if (FT_Load_Glyph(face, index, FT_LOAD_DEFAULT))
            throw BadGlyph((std::string("GG::Font::GetGlyphBitmap : Freetype could not load the glyph for character \'") + 
                            (ch < 256 ? lexical_cast<std::string>(char(ch)) : lexical_cast<std::string>(ch))) + "\'");

        FT_GlyphSlot glyph = face->glyph;

        // render the glyph
        if (FT_Render_Glyph(glyph, ft_render_mode_normal))
            throw BadGlyph((std::string("GG::Font::GetGlyphBitmap : Freetype could not render the glyph for character \'") + 
                            (ch < 256 ? lexical_cast<std::string>(char(ch)) : lexical_cast<std::string>(ch))) + "\'");
    } else {
        retval = false;
    }

    return retval;
}

int Font::RenderGlyph(int x, int y, const Glyph& glyph, const Font::RenderState* render_state) const
{
    if (render_state && render_state->use_italics) {
        // render subtexture to tilted rhombus instead of rectangle
        glBindTexture(GL_TEXTURE_2D, glyph.sub_texture.GetTexture()->OpenGLId());
        glBegin(GL_TRIANGLE_STRIP);
        glTexCoord2f(glyph.sub_texture.TexCoords()[0], glyph.sub_texture.TexCoords()[1]);
        glVertex2d(x + glyph.left_bearing + m_italics_offset, y);
        glTexCoord2f(glyph.sub_texture.TexCoords()[2], glyph.sub_texture.TexCoords()[1]);
        glVertex2d(x + glyph.left_bearing + m_italics_offset + glyph.sub_texture.Width(), y);
        glTexCoord2f(glyph.sub_texture.TexCoords()[0], glyph.sub_texture.TexCoords()[3]);
        glVertex2d(x + glyph.left_bearing - m_italics_offset, y + glyph.sub_texture.Height());
        glTexCoord2f(glyph.sub_texture.TexCoords()[2], glyph.sub_texture.TexCoords()[3]);
        glVertex2d(x + glyph.left_bearing - m_italics_offset + glyph.sub_texture.Width(), y + glyph.sub_texture.Height());
        glEnd();
    } else {
        glyph.sub_texture.OrthoBlit(Pt(x + glyph.left_bearing, y));
    }
    if (render_state && render_state->draw_underline) {
        double x1 = x;
        double y1 = y + m_height + m_descent - m_underline_offset;
        double x2 = x1 + glyph.advance;
        double y2 = y1 + m_underline_height;
        glDisable(GL_TEXTURE_2D);
        glBegin(GL_QUADS);
        glVertex2d(x1, y2);
        glVertex2d(x1, y1);
        glVertex2d(x2, y1);
        glVertex2d(x2, y2);
        glEnd();
        glEnable(GL_TEXTURE_2D);
    }
    return glyph.advance;
}

void Font::HandleTag(const boost::shared_ptr<FormattingTag>& tag, double* orig_color, RenderState& render_state) const
{
    using boost::lexical_cast;
    if (tag->text == "i") {
        render_state.use_italics = !tag->close_tag;
    } else if (tag->text == "u") {
        render_state.draw_underline = !tag->close_tag;
    } else if (tag->text == "rgba") {
        if (tag->close_tag) {
            glColor4dv(orig_color);
            render_state.color_set = false;
        } else {
            bool well_formed_tag = true;
            if (4 <= tag->params.size()) {
                try {
                    int temp_color[4];
                    GLubyte color[4];
                    temp_color[0] = lexical_cast<int>(tag->params[0]);
                    temp_color[1] = lexical_cast<int>(tag->params[1]);
                    temp_color[2] = lexical_cast<int>(tag->params[2]);
                    temp_color[3] = lexical_cast<int>(tag->params[3]);
                    if (0 <= temp_color[0] && temp_color[0] <= 255 && 0 <= temp_color[1] && temp_color[1] <= 255 &&
                        0 <= temp_color[2] && temp_color[2] <= 255 && 0 <= temp_color[3] && temp_color[3] <= 255) {
                        color[0] = temp_color[0];
                        color[1] = temp_color[1];
                        color[2] = temp_color[2];
                        color[3] = temp_color[3];
                        glColor4ubv(color);
                        render_state.curr_color = Clr(color[0], color[1], color[2], color[3]);
                        render_state.color_set = true;
                    } else {
                        well_formed_tag = false;
                    }
                } catch (boost::bad_lexical_cast) {
                    try {
                        double color[4];
                        color[0] = lexical_cast<double>(tag->params[0]);
                        color[1] = lexical_cast<double>(tag->params[1]);
                        color[2] = lexical_cast<double>(tag->params[2]);
                        color[3] = lexical_cast<double>(tag->params[3]);
                        if (0.0 <= color[0] && color[0] <= 1.0 && 0.0 <= color[1] && color[1] <= 1.0 &&
                            0.0 <= color[2] && color[2] <= 1.0 && 0.0 <= color[3] && color[3] <= 1.0) {
                            glColor4dv(color);
                            render_state.curr_color = FloatClr(color[0], color[1], color[2], color[3]);
                            render_state.color_set = true;
                        } else {
                            well_formed_tag = false;
                        }
                    } catch (boost::bad_lexical_cast) {
                        well_formed_tag = false;
                    }
                }
            } else {
                well_formed_tag = false;
            }
            if (!well_formed_tag)
                std::cerr << "GG::Font : Encountered malformed <rgba> formatting tag: " << tag->original_tag_text;
        }
    }
}


///////////////////////////////////////
// class GG::FontManager
///////////////////////////////////////
// FontKey 
FontManager::FontKey::FontKey(const std::string& str, int pts) :
    filename(str),
    points(pts)
{
}

bool FontManager::FontKey::operator<(const FontKey& rhs) const
{
    return (filename < rhs.filename || (filename == rhs.filename && points < rhs.points));
}

// FontManager
FontManager::FontManager()
{}

boost::shared_ptr<Font> FontManager::GetFont(const std::string& font_filename, int pts, unsigned int range/* = Font::ALL_DEFINED_RANGES*/)
{
    static const boost::shared_ptr<Font> EMPTY_FONT(new Font("", 0));
    FontKey key(font_filename, pts);
    std::map<FontKey, boost::shared_ptr<Font> >::iterator it = m_rendered_fonts.find(key);
    if (it == m_rendered_fonts.end()) { // if no such font has been created, create it now
        if (font_filename == "")
            return EMPTY_FONT; // keeps this function from throwing; "" is the only invalid font filename that shouldn't throw
        else
            return (m_rendered_fonts[key] = boost::shared_ptr<Font>(new Font(font_filename, pts, range)));
        // if a font like this has been created, but it doesn't have all the right glyphs, release it and create a new one
    } else if ((it->second->GetGlyphRange() & range) != range) {
        range |= it->second->GetGlyphRange();
        m_rendered_fonts.erase(it);
        return (m_rendered_fonts[key] = boost::shared_ptr<Font>(new Font(font_filename, pts, range)));
    } else { // otherwise, the font we found works, so just return it
        return it->second;
    }
}

void FontManager::FreeFont(const std::string& font_filename, int pts)
{
    FontKey key(font_filename, pts);
    std::map<FontKey, boost::shared_ptr<Font> >::iterator it = m_rendered_fonts.find(key);
    if (it != m_rendered_fonts.end())
        m_rendered_fonts.erase(it);
}

FontManager& GG::GetFontManager()
{
    static FontManager manager;
    return manager;
}




See more files for this project here

FreeOrion

FreeOrion brings nation building to a galactic scale with its full-featured grand campaign and in-game racial histories, in addition to the classic 4X model of galactic conquest and tactical combat.

Project homepage: http://sourceforge.net/projects/freeorion
Programming language(s): C,C++
License: other

  Ogre/
    Plugins/
      OISInput.cfg
      OISInput.cpp
      OISInput.h
      OgreGUIInputPlugin.cpp
      OgreGUIInputPlugin.h
      SConscript
    OgreGUI.cpp
    SConscript
  SDL/
    SConscript
    SDLGUI.cpp
  dialogs/
    ColorDlg.cpp
    FileDlg.cpp
    SConscript
    ThreeButtonDlg.cpp
  AlignmentFlags.cpp
  Base.cpp
  BrowseInfoWnd.cpp
  Button.cpp
  Clr.cpp
  Control.cpp
  Cursor.cpp
  DrawUtil.cpp
  DropDownList.cpp
  DynamicGraphic.cpp
  Edit.cpp
  EventPump.cpp
  Font.cpp
  GUI.cpp
  Layout.cpp
  ListBox.cpp
  Menu.cpp
  MultiEdit.cpp
  Plugin.cpp
  PluginInterface.cpp
  PtRect.cpp
  SConscript
  Scroll.cpp
  Slider.cpp
  StaticGraphic.cpp
  StyleFactory.cpp
  TabWnd.cpp
  TextControl.cpp
  Texture.cpp
  Timer.cpp
  Wnd.cpp
  WndEditor.cpp
  WndEvent.cpp
  ZList.cpp
  _vsnprintf.c