Example #1
0
// ----------------------------------------------------------------------------
void RibbonWidget::add()
{
    assert(m_magic_number == 0xCAFEC001);
    assert(m_x > -10.0f);
    assert(m_y > -10.0f);
    assert(m_w > 0.0f);
    assert(m_h > 0.0f);


    m_labels.clearWithoutDeleting();

    rect<s32> widget_size = rect<s32>(m_x, m_y, m_x + m_w, m_y + m_h);

    int id = (m_reserved_id == -1 ? getNewID() : m_reserved_id);

    IGUIButton * btn = GUIEngine::getGUIEnv()->addButton(widget_size,
                                                         m_parent, id, L"");
    m_element = btn;

    m_active_children.clearWithoutDeleting(); // Is just a copy of m_children without the deactivated children. m_children takes care of memory.
    for (unsigned int i=0; i<m_children.size(); i++)
    {
        if (m_children[i].isVisible())
        {
            m_active_children.push_back(m_children.get(i));
        }
    }
    const int subbuttons_amount = m_active_children.size();

    // For some ribbon types, we can have unequal sizes depending on whether
    // items have labels or not
    int with_label = 0;
    int without_label = 0;

    // ---- check how much space each child button will take and fit
    // them within available space
    int total_needed_space = 0;
    for (int i=0; i<subbuttons_amount; i++)
    {
        // FIXME: why do I manually invoke the Layout Manager here?
        LayoutManager::readCoords(m_active_children.get(i));
        LayoutManager::applyCoords(m_active_children.get(i), NULL, this);

        if (m_active_children[i].m_type != WTYPE_ICON_BUTTON &&
            m_active_children[i].m_type != WTYPE_BUTTON)
        {
            Log::warn("RiggonWidget", "Ribbon widgets can only have "
                            "(icon)button widgets as children");
            continue;
        }

        // ribbon children must not be keyboard navigatable, the parent
        // ribbon takes care of that
        if (m_active_children[i].m_type == WTYPE_ICON_BUTTON)
        {
            IconButtonWidget* icon = ((IconButtonWidget*)m_active_children.get(i));
            icon->m_tab_stop = false;
        }


        bool has_label_underneath = m_active_children[i].m_text.size() > 0;
        if (m_active_children[i].m_properties[PROP_LABELS_LOCATION].size() > 0)
        {
            has_label_underneath = false;
        }

        if (has_label_underneath) with_label++;
        else                      without_label++;

        total_needed_space += m_active_children[i].m_w;
    }

    //int biggest_y = 0;
    const int button_y = 10;

    const int one_button_space =
        int(roundf((float)m_w / (float)subbuttons_amount));

    int widget_x = -1;

    // ---- add children
    for (int i=0; i<subbuttons_amount; i++)
    {
        // ---- tab ribbons
        if (getRibbonType() == RIBBON_TABS)
        {
            const int large_tab = (int)((with_label + without_label)
                                        *one_button_space
                                        / (with_label + without_label/2.0f));
            const int small_tab = large_tab/2;

            stringw& message = m_active_children[i].m_text;


            if (message.size() == 0)
            {
                if (widget_x == -1) widget_x = small_tab/2;
                else widget_x += small_tab/2;
            }
            else
            {
                if (widget_x == -1) widget_x = large_tab/2;
                else widget_x += large_tab/2;
            }

            IGUIButton * subbtn = NULL;
            rect<s32> subsize = rect<s32>(widget_x - large_tab/2+2,  0,
                                          widget_x + large_tab/2-2,  m_h);

            if (message.size() == 0)
            {
                subsize = rect<s32>(widget_x - small_tab/2+2,  0,
                                    widget_x + small_tab/2-2,  m_h);
            }

            if (m_active_children[i].m_type == WTYPE_BUTTON)
            {
                subbtn = GUIEngine::getGUIEnv()
                       ->addButton(subsize, btn, getNewNoFocusID(),
                                   message.c_str(), L"");
                subbtn->setTabStop(false);
                subbtn->setTabGroup(false);

                if ((int)GUIEngine::getFont()->getDimension(message.c_str())
                                              .Width > subsize.getWidth()  &&
                    message.findFirst(L' ') == -1                          &&
                    message.findFirst(L'\u00AD') == -1                        )
                {
                    // if message too long and contains no space and no soft
                    // hyphen, make the font smaller
                    subbtn->setOverrideFont(GUIEngine::getSmallFont());
                }
            }
            else if (m_active_children[i].m_type == WTYPE_ICON_BUTTON)
            {
                rect<s32> icon_part = rect<s32>(15,
                                                0,
                                                subsize.getHeight()+15,
                                                subsize.getHeight());

                if (message.size() == 0)
                {
                    const int x = subsize.getWidth()/2 - subsize.getHeight()/2;
                    // no label, only icon, so center the icon
                    icon_part = rect<s32>(x,
                                          0,
                                          x + subsize.getHeight(),
                                          subsize.getHeight());
                }

                // label at the *right* of the icon (for tabs)
                rect<s32> label_part = rect<s32>(subsize.getHeight()+15,
                                                 0,
                                                 subsize.getWidth()-15,
                                                 subsize.getHeight());

                // use the same ID for all subcomponents; since event handling
                // is done per-ID, no matter which one your hover, this
                // widget will get it
                int same_id = getNewNoFocusID();
                subbtn = GUIEngine::getGUIEnv()->addButton(subsize, btn,
                                                           same_id, L"", L"");

                IGUIButton* icon =
                    GUIEngine::getGUIEnv()->addButton(icon_part, subbtn,
                                                      same_id, L"");
                icon->setScaleImage(true);
                std::string filename = file_manager->getAsset(
                                     m_active_children[i].m_properties[PROP_ICON]);
                icon->setImage( irr_driver->getTexture(filename.c_str()) );
                icon->setUseAlphaChannel(true);
                icon->setDrawBorder(false);
                icon->setTabStop(false);

                IGUIStaticText* label =
                    GUIEngine::getGUIEnv()->addStaticText(message.c_str(),
                                                          label_part,
                                                          false /* border */,
                                                          true /* word wrap */,
                                                          subbtn, same_id);

                if ((int)GUIEngine::getFont()->getDimension(message.c_str())
                                              .Width > label_part.getWidth()&&
                    message.findFirst(L' ') == -1                           &&
                    message.findFirst(L'\u00AD') == -1                        )
                {
                    // if message too long and contains no space and no soft
                    // hyphen, make the font smaller
                    label->setOverrideFont(GUIEngine::getSmallFont());
                }
                label->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER);
                label->setTabStop(false);
                label->setNotClipped(true);
                label->setRightToLeft(translations->isRTLText(message));
                m_labels.push_back(label);

                subbtn->setTabStop(false);
                subbtn->setTabGroup(false);
            }
            else
            {
                Log::error("RibbonWidget", "Invalid tab bar contents");
            }

            m_active_children[i].m_element = subbtn;

            if (message.size() == 0) widget_x += small_tab/2;
            else                     widget_x += large_tab/2;
        }
        // ---- icon ribbons
        else if (m_active_children[i].m_type == WTYPE_ICON_BUTTON)
        {
            if (widget_x == -1) widget_x = one_button_space/2;

            // find how much space to keep for the label under the button.
            // consider font size, whether the label is multiline, etc...
            bool has_label = m_active_children[i].m_text.size() > 0;

            if (m_active_children[i].m_properties[PROP_LABELS_LOCATION].size() > 0)
            {
                has_label = false;
            }

            const int needed_space_under_button = has_label
                                                ? GUIEngine::getFontHeight()
                                                : 10;

            float imageRatio =
                (float)m_active_children[i].m_w / (float)m_active_children[i].m_h;

            // calculate the size of the image
            std::string filename =
                file_manager->getAsset(m_active_children[i].m_properties[PROP_ICON]);
            video::ITexture* image =
                irr_driver->getTexture((filename).c_str());
            if(!image)
            {
                std::string file = file_manager->getAsset(FileManager::GUI,"main_help.png");
                image = irr_driver->getTexture(file);
                if(!image)
                    Log::fatal("RibbonWidget",
                        "Can't find fallback texture 'gui/main_help.png, aborting.");
            }

            float image_h = (float)image->getSize().Height;
            float image_w = image_h*imageRatio;
            float zoom = (float) (m_h - button_y - needed_space_under_button) / image_h;
            float zoom_x = (float) one_button_space / image_w;
            if(zoom_x < zoom)
                zoom = zoom_x;

            // ---- add bitmap button part
            // backup and restore position in case the same object is added
            // multiple times (FIXME: unclean)
            int old_x = m_active_children[i].m_x;
            int old_y = m_active_children[i].m_y;
            int old_w = m_active_children[i].m_w;
            int old_h = m_active_children[i].m_h;

            m_active_children[i].m_x = widget_x - int(image_w*zoom/2.0f);
            m_active_children[i].m_y = button_y;
            m_active_children[i].m_w = int(image_w*zoom);
            m_active_children[i].m_h = int(image_h*zoom);

            IconButtonWidget* icon = ((IconButtonWidget*)m_active_children.get(i));

            if (icon->m_properties[PROP_EXTEND_LABEL].size() == 0)
            {
                icon->m_properties[PROP_EXTEND_LABEL] =
                    StringUtils::toString(one_button_space - icon->m_w);
            }

            m_active_children.get(i)->m_parent = btn;
            m_active_children.get(i)->add();

            // restore backuped size and location (see above for more info)
            m_active_children[i].m_x = old_x;
            m_active_children[i].m_y = old_y;
            m_active_children[i].m_w = old_w;
            m_active_children[i].m_h = old_h;

            // the label itself will be added by the icon widget. since it
            // adds the label outside of the widget area it is assigned to,
            // the label will appear in the area we want at the bottom

            widget_x += one_button_space;
        }
        else
        {
            Log::warn("RiggonWidget", "Invalid contents type in ribbon");
        }


        //m_children[i].id = subbtn->getID();
        m_active_children[i].m_event_handler = this;
    }// next sub-button

    id = m_element->getID();
    m_element->setTabOrder(id);
    m_element->setTabGroup(false);
    updateSelection();

    if (!m_is_visible)
        setVisible(false);
}   // add
// -----------------------------------------------------------------------------
void IconButtonWidget::add()
{
    // ---- Icon
    if (m_texture == NULL)
    {
        if (m_icon_path_type == ICON_PATH_TYPE_ABSOLUTE)
        {
            setTexture(irr_driver->getTexture(m_properties[PROP_ICON]));
        }
        else if (m_icon_path_type == ICON_PATH_TYPE_RELATIVE)
        {
            // Avoid warning about missing texture in case of e.g.
            // screenshot widget
            if(m_properties[PROP_ICON] != "")
            {
                std::string file = file_manager->getAsset(m_properties[PROP_ICON]);
                setTexture(irr_driver->getTexture(file));
            }
        }
    }

    if (m_texture == NULL)
    {
        if (m_properties[PROP_ICON].size() > 0)
        {
            Log::error("icon_button",
                "add() : error, cannot find texture '%s' in iconbutton '%s'.",
                m_properties[PROP_ICON].c_str(), m_properties[PROP_ID].c_str());
        }
        std::string file = file_manager->getAsset(FileManager::GUI,"main_help.png");
        setTexture(irr_driver->getTexture(file));
        if(!m_texture)
            Log::fatal("IconButtonWidget",
                  "Can't find fallback texture 'gui/main_help.png, aborting.");
    }

    if (m_properties[PROP_FOCUS_ICON].size() > 0)
    {
        if (m_icon_path_type == ICON_PATH_TYPE_ABSOLUTE)
        {
            m_highlight_texture =
                irr_driver->getTexture(m_properties[PROP_FOCUS_ICON]);
        }
        else if (m_icon_path_type == ICON_PATH_TYPE_RELATIVE)
        {
            m_highlight_texture =
                irr_driver->getTexture(file_manager->getAsset(
                                       m_properties[PROP_FOCUS_ICON]));
        }

    }

    // irrlicht widgets don't support scaling while keeping aspect ratio
    // so, happily, let's implement it ourselves
    float useAspectRatio = -1.0f;

    if (m_properties[PROP_CUSTOM_RATIO] != "")
    {
        StringUtils::fromString(m_properties[PROP_CUSTOM_RATIO],
                                m_custom_aspect_ratio);
        m_scale_mode = SCALE_MODE_KEEP_CUSTOM_ASPECT_RATIO;
    }

    if (m_scale_mode == SCALE_MODE_KEEP_TEXTURE_ASPECT_RATIO)
    {
        useAspectRatio = (float)m_texture_w / (float)m_texture_h;
    }
    else if (m_scale_mode == SCALE_MODE_KEEP_CUSTOM_ASPECT_RATIO)
    {
        useAspectRatio = m_custom_aspect_ratio;
    }

    int suggested_h = m_h;
    int suggested_w = (int)((useAspectRatio < 0 ? m_w : useAspectRatio * suggested_h));

    if (suggested_w > m_w)
    {
        const float needed_scale_factor = (float)m_w / (float)suggested_w;
        suggested_w = (int)(suggested_w*needed_scale_factor);
        suggested_h = (int)(suggested_h*needed_scale_factor);
    }
    const int x_from = m_x + (m_w - suggested_w)/2; // center horizontally
    const int y_from = m_y + (m_h - suggested_h)/2; // center vertically

    rect<s32> widget_size = rect<s32>(x_from,
                                      y_from,
                                      x_from + suggested_w,
                                      y_from + suggested_h);

    IGUIButton* btn = GUIEngine::getGUIEnv()->addButton(widget_size,
                                                        m_parent,
                                                        (m_tab_stop ? getNewID() : getNewNoFocusID()),
                                                        L"");

    btn->setTabStop(m_tab_stop);
    m_element = btn;
    m_id = m_element->getID();

    // ---- label if any
    const stringw& message = getText();
    if (message.size() > 0)
    {
        const int label_extra_size =
            ( m_properties[PROP_EXTEND_LABEL].size() == 0 ?
               0 : atoi(m_properties[PROP_EXTEND_LABEL].c_str()) );

        const bool word_wrap = (m_properties[PROP_WORD_WRAP] == "true");

        if (m_properties[PROP_LABELS_LOCATION] == "hover")
        {
            core::dimension2du text_size = GUIEngine::getFont()->getDimension(message.c_str());
            core::recti pos = btn->getAbsolutePosition();
            int center_x = pos.UpperLeftCorner.X + pos.getWidth() / 2;
            int x1 = center_x - text_size.Width / 2 - label_extra_size / 2;
            int y1 = pos.UpperLeftCorner.Y - (word_wrap ? GUIEngine::getFontHeight() * 2 :
                GUIEngine::getFontHeight()) - 15;
            int x2 = center_x + text_size.Width / 2 + label_extra_size / 2;
            int y2 = pos.UpperLeftCorner.Y - 15;

            if (x1 < 0)
            {
                int diff = -x1;
                x1 += diff;
                x2 += diff;
            }
            else if (x2 > (int)irr_driver->getActualScreenSize().Width)
            {
                int diff = x2 - irr_driver->getActualScreenSize().Width;
                x2 -= diff;
                x1 -= diff;
            }

            core::recti parent_pos = m_parent->getAbsolutePosition();
            x1 -= parent_pos.UpperLeftCorner.X;
            x2 -= parent_pos.UpperLeftCorner.X;
            y1 -= parent_pos.UpperLeftCorner.Y;
            y2 -= parent_pos.UpperLeftCorner.Y;
            widget_size = rect<s32>(x1, y1, x2, y2);
        }
        else
        {
            // leave enough room for two lines of text if word wrap is enabled, otherwise a single line
            widget_size = rect<s32>(m_x - label_extra_size/2,
                                    m_y + m_h,
                                    m_x + m_w + label_extra_size/2,
                                    m_y + m_h + (word_wrap ? GUIEngine::getFontHeight()*2 :
                                                             GUIEngine::getFontHeight()));
        }

        m_label = GUIEngine::getGUIEnv()->addStaticText(message.c_str(), widget_size,
                                                        false, word_wrap, m_parent);
        m_label->setTextAlignment(EGUIA_CENTER, EGUIA_UPPERLEFT);
        m_label->setTabStop(false);
        m_label->setNotClipped(true);

        if (m_properties[PROP_LABELS_LOCATION] == "hover")
        {
            m_label->setVisible(false);
        }

        setLabelFont();

        m_label->setRightToLeft(translations->isRTLText(message));
        m_label->setTextRestrainedInside(false);
    }

    // ---- IDs
    m_id = m_element->getID();
    if (m_tab_stop) m_element->setTabOrder(m_id);
    m_element->setTabGroup(false);

    if (!m_is_visible)
        m_element->setVisible(false);
}
Example #3
0
void RibbonWidget::add()
{
    m_labels.clearWithoutDeleting();
    
    
    rect<s32> widget_size = rect<s32>(m_x, m_y, m_x + m_w, m_y + m_h);
    
    int id = (m_reserved_id == -1 ? getNewID() : m_reserved_id);
    
    IGUIButton * btn = GUIEngine::getGUIEnv()->addButton(widget_size, m_parent, id, L"");
    m_element = btn;
    
    const int subbuttons_amount = m_children.size();
    
    // ---- check how much space each child button will take and fit them within available space
    int total_needed_space = 0;
    for (int i=0; i<subbuttons_amount; i++)
    {
        // FIXME: a little unclean to invoke layout code here?
        LayoutManager::readCoords(m_children.get(i), NULL, this);
        
        if (m_children[i].m_type != WTYPE_ICON_BUTTON && m_children[i].m_type != WTYPE_BUTTON)
        {
            std::cerr << "/!\\ Warning /!\\ : ribbon widgets can only have (icon)button widgets as children "
                      << std::endl;
            continue;
        }
        
        // ribbon children must not be keyboard navigatable, the parent ribbon takes care of that
        if (m_children[i].m_type == WTYPE_ICON_BUTTON)
        {
            IconButtonWidget* icon = ((IconButtonWidget*)m_children.get(i));
            icon->m_tab_stop = false;
        }
        
        total_needed_space += m_children[i].m_w;
    }
    
    int free_w_space = m_w - total_needed_space;
    
    //int biggest_y = 0;
    const int button_y = 10;
    float global_zoom = 1;
    
    const int min_free_space = 50;
    global_zoom = (float)m_w / (float)( m_w - free_w_space + min_free_space );
    //free_w_space = (int)(m_w - total_needed_space*global_zoom);
    
    const int one_button_space = (int)round((float)m_w / (float)subbuttons_amount);
    
    // ---- add children
    for (int i=0; i<subbuttons_amount; i++)
    {
        
        const int widget_x = one_button_space*(i+1) - one_button_space/2;
        
        // ---- tab ribbons
        if (getRibbonType() == RIBBON_TABS)
        {
            IGUIButton * subbtn = NULL;
            rect<s32> subsize = rect<s32>(widget_x - one_button_space/2+2,  0,
                                          widget_x + one_button_space/2-2,  m_h);
            
            stringw& message = m_children[i].m_text;
            
            if (m_children[i].m_type == WTYPE_BUTTON)
            {
                subbtn = GUIEngine::getGUIEnv()->addButton(subsize, btn, getNewNoFocusID(), message.c_str(), L"");
                subbtn->setTabStop(false);
                subbtn->setTabGroup(false);
            }
            else if (m_children[i].m_type == WTYPE_ICON_BUTTON)
            {
                rect<s32> icon_part = rect<s32>(15,
                                                0,
                                                subsize.getHeight()+15,
                                                subsize.getHeight());
                // label at the *right* of the icon (for tabs)
                rect<s32> label_part = rect<s32>(subsize.getHeight()+15,
                                                 0,
                                                 subsize.getWidth()-15,
                                                 subsize.getHeight());
                
                // use the same ID for all subcomponents; since event handling is done per-ID, no matter
                // which one your hover, this widget will get it
                int same_id = getNewNoFocusID();
                subbtn = GUIEngine::getGUIEnv()->addButton(subsize, btn, same_id, L"", L"");
                
                //MyGUIButton* icon = new MyGUIButton(GUIEngine::getGUIEnv(), subbtn, same_id, icon_part, true);
                IGUIButton* icon = GUIEngine::getGUIEnv()->addButton(icon_part, subbtn, same_id, L"");
                icon->setScaleImage(true);
                icon->setImage( irr_driver->getTexture((file_manager->getDataDir() + "/" + m_children[i].m_properties[PROP_ICON]).c_str()) );
                icon->setUseAlphaChannel(true);
                icon->setDrawBorder(false);
                icon->setTabStop(false);
                
                IGUIStaticText* label = GUIEngine::getGUIEnv()->addStaticText(message.c_str(), label_part,
                                                                              false /* border */,
                                                                              true /* word wrap */,
                                                                              subbtn, same_id);
                label->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER);
                label->setTabStop(false);
                label->setNotClipped(true);
                m_labels.push_back(label);
                
                subbtn->setTabStop(false);
                subbtn->setTabGroup(false);
                
            }
            else
            {
                std::cerr << "Invalid tab bar contents\n";
            }
            
            m_children[i].m_element = subbtn;
        }
        // ---- icon ribbons
        else if (m_children[i].m_type == WTYPE_ICON_BUTTON)
        {
            // find how much space to keep for the label under the button.
            // consider font size, whether the label is multiline, etc...
            const bool has_label = m_children[i].m_text.size() > 0;
            
            const int needed_space_under_button = has_label ? GUIEngine::getFontHeight() : 10;
            
            float imageRatio = (float)m_children[i].m_w / (float)m_children[i].m_h;
            
            // calculate the size of the image
            video::ITexture* image = irr_driver->getTexture((file_manager->getDataDir() + "/" + m_children[i].m_properties[PROP_ICON]).c_str());
            float image_h = (float)image->getSize().Height;
            float image_w = image_h*imageRatio;

            // scale to fit (FIXME: calculate the right value directly...)
            float zoom = global_zoom;
            
            if (button_y + image_h*zoom + needed_space_under_button > m_h)
            {
                // scale down
                while (button_y + image_h*zoom + needed_space_under_button > m_h) zoom -= 0.01f;
            }
            else
            {
                // scale up
                while (button_y + image_h*zoom + needed_space_under_button < m_h) zoom += 0.01f;
            }
            
            // ---- add bitmap button part
            // backup and restore position in case the same object is added multiple times (FIXME: unclean)
            int old_x = m_children[i].m_x;
            int old_y = m_children[i].m_y;
            int old_w = m_children[i].m_w;
            int old_h = m_children[i].m_h;
            
            m_children[i].m_x = widget_x - (int)(image_w*zoom/2.0f);
            m_children[i].m_y = button_y;
            m_children[i].m_w = (int)(image_w*zoom);
            m_children[i].m_h = (int)(image_h*zoom);
            
            IconButtonWidget* icon = ((IconButtonWidget*)m_children.get(i));
            icon->m_properties[PROP_EXTEND_LABEL] = StringUtils::toString(one_button_space - icon->m_w);

            m_children.get(i)->m_parent = btn;
            m_children.get(i)->add();

            // restore backuped size and location (see above for more info)
            m_children[i].m_x = old_x;
            m_children[i].m_y = old_y;
            m_children[i].m_w = old_w;
            m_children[i].m_h = old_h;
            
            // the label itself will be added by the icon widget. since it adds the label outside of the
            // widget area it is assigned to, the label will appear in the area we want at the bottom
        }
        else
        {
            std::cerr << "/!\\ Warning /!\\ : Invalid contents type in ribbon" << std::endl;
        }
        
        
        //m_children[i].id = subbtn->getID();
        m_children[i].m_event_handler = this;
    }// next sub-button
    
    id = m_element->getID();
    m_element->setTabOrder(id);
    m_element->setTabGroup(false);
    updateSelection();
}
// -----------------------------------------------------------------------------
void IconButtonWidget::add()
{
    // ---- Icon
    if (m_texture == NULL)
    {
        if (m_icon_path_type == ICON_PATH_TYPE_ABSOLUTE)
        {
            m_texture = irr_driver->getTexture(m_properties[PROP_ICON].c_str());
        }
        else if (m_icon_path_type == ICON_PATH_TYPE_RELATIVE)
        {
            m_texture = irr_driver->getTexture((file_manager->getDataDir() + "/" +
                                                m_properties[PROP_ICON]).c_str());
        }
    }
    
    if (m_texture == NULL)
    {
        std::cerr << "IconButtonWidget::add() : error, cannot find texture "
                  << m_properties[PROP_ICON].c_str() << std::endl;
        m_texture = irr_driver->getTexture((file_manager->getDataDir() + "/gui/main_help.png").c_str());
    }
    m_texture_w = m_texture->getSize().Width;
    m_texture_h = m_texture->getSize().Height;

    if (m_properties[PROP_FOCUS_ICON].size() > 0)
    {
        if (m_icon_path_type == ICON_PATH_TYPE_ABSOLUTE)
        {
            m_highlight_texture = irr_driver->getTexture(m_properties[PROP_FOCUS_ICON].c_str());
        }
        else if (m_icon_path_type == ICON_PATH_TYPE_RELATIVE)
        {
            m_highlight_texture = irr_driver->getTexture((file_manager->getDataDir() + "/" +
                                                          m_properties[PROP_FOCUS_ICON]).c_str());
        }
        
    }
    
    // irrlicht widgets don't support scaling while keeping aspect ratio
    // so, happily, let's implement it ourselves
    float useAspectRatio = -1.0f;
    
    if (m_scale_mode == SCALE_MODE_KEEP_TEXTURE_ASPECT_RATIO)
    {
        useAspectRatio = (float)m_texture_w / (float)m_texture_h;
        //std::cout << "m_texture_h=" << m_texture_h << "; m_texture_w="<< m_texture_w
        //          << "; useAspectRatio=" << useAspectRatio << std::endl;
    }
    else if (m_scale_mode == SCALE_MODE_KEEP_CUSTOM_ASPECT_RATIO)
    {
        useAspectRatio = m_custom_aspect_ratio;
    }
    
    int suggested_h = m_h;
    int suggested_w = (int)((useAspectRatio < 0 ? m_w : useAspectRatio * suggested_h));
    
    if (suggested_w > m_w)
    {
        const float needed_scale_factor = (float)m_w / (float)suggested_w;
        suggested_w = (int)(suggested_w*needed_scale_factor);
        suggested_h = (int)(suggested_h*needed_scale_factor);
    }
    const int x_from = m_x + (m_w - suggested_w)/2; // center horizontally
    const int y_from = m_y + (m_h - suggested_h)/2; // center vertically
    
    rect<s32> widget_size = rect<s32>(x_from,
                                      y_from,
                                      x_from + suggested_w,
                                      y_from + suggested_h);
    
    IGUIButton* btn = GUIEngine::getGUIEnv()->addButton(widget_size,
                                                        m_parent,
                                                        (m_tab_stop ? getNewID() : getNewNoFocusID()),
                                                        L"");

    btn->setTabStop(m_tab_stop);
    m_element = btn;
    m_id = m_element->getID();
    
    // ---- label if any
    const stringw& message = getText();
    if (message.size() > 0)
    {
        //std::cout << "Adding label of icon widget, m_properties[PROP_EXTEND_LABEL] = "
        //          << m_properties[PROP_EXTEND_LABEL] << std::endl;
        const int label_extra_size = ( m_properties[PROP_EXTEND_LABEL].size() == 0 ?
                                       0 : atoi(m_properties[PROP_EXTEND_LABEL].c_str()) );
        
        const bool word_wrap = (m_properties[PROP_WORD_WRAP] == "true");
        
        if (m_properties[PROP_LABELS_LOCATION] == "hover")
        {
            widget_size = rect<s32>(m_x - label_extra_size/2,
                                    m_y - (word_wrap ? GUIEngine::getFontHeight()*2 :
                                                 GUIEngine::getFontHeight()) - 15,
                                    m_x + m_w + label_extra_size/2,
                                    m_y - 15);
        }
        else
        {
            // leave enough room for two lines of text if word wrap is enabled, otherwise a single line
            widget_size = rect<s32>(m_x - label_extra_size/2,
                                    m_y + m_h,
                                    m_x + m_w + label_extra_size/2,
                                    m_y + m_h + (word_wrap ? GUIEngine::getFontHeight()*2 :
                                                             GUIEngine::getFontHeight()));
        }
        
        m_label = GUIEngine::getGUIEnv()->addStaticText(message.c_str(), widget_size,
                                                        false, word_wrap, m_parent);
        m_label->setTextAlignment(EGUIA_CENTER, EGUIA_UPPERLEFT);
        m_label->setTabStop(false);
        m_label->setNotClipped(true);
        
        if (m_properties[PROP_LABELS_LOCATION] == "hover")
        {
            m_label->setVisible(false);
        }
        
        const int max_w = m_label->getAbsolutePosition().getWidth();
        
        if (!word_wrap &&
            (int)GUIEngine::getFont()->getDimension(message.c_str()).Width > max_w + 4) // arbitrarily allow for 4 pixels
        {
            m_label->setOverrideFont( GUIEngine::getSmallFont() );
        }
        
#if IRRLICHT_VERSION_MAJOR > 1 || (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR >= 8)
        m_label->setRightToLeft( translations->isRTLLanguage() );
        m_label->setTextRestrainedInside(false);
#endif
    }
    
    // ---- IDs
    m_id = m_element->getID();
    if (m_tab_stop) m_element->setTabOrder(m_id);
    m_element->setTabGroup(false);
}
// -----------------------------------------------------------------------------
void IconButtonWidget::add()
{
    // ---- Icon
    if (m_texture == NULL)
    {
        if (m_icon_path_type == ICON_PATH_TYPE_ABSOLUTE)
        {
            setTexture(irr_driver->getTexture(m_properties[PROP_ICON]));
        }
        else if (m_icon_path_type == ICON_PATH_TYPE_RELATIVE)
        {
            std::string file = file_manager->getAsset(m_properties[PROP_ICON]);
            setTexture(irr_driver->getTexture(file));
        }
    }

    if (m_texture == NULL)
    {
        Log::error("icon_button",
                    "add() : error, cannot find texture '%s'.",
                   m_properties[PROP_ICON].c_str());
        std::string file = file_manager->getAsset(FileManager::GUI,"main_help.png");
        setTexture(irr_driver->getTexture(file));
        if(!m_texture)
            Log::fatal("IconButtonWidget",
                  "Can't find fallback texture 'gui/main_help.png, aborting.");
    }

    if (m_properties[PROP_FOCUS_ICON].size() > 0)
    {
        if (m_icon_path_type == ICON_PATH_TYPE_ABSOLUTE)
        {
            m_highlight_texture =
                irr_driver->getTexture(m_properties[PROP_FOCUS_ICON]);
        }
        else if (m_icon_path_type == ICON_PATH_TYPE_RELATIVE)
        {
            m_highlight_texture =
                irr_driver->getTexture(file_manager->getAsset(
                                       m_properties[PROP_FOCUS_ICON]));
        }

    }

    // irrlicht widgets don't support scaling while keeping aspect ratio
    // so, happily, let's implement it ourselves
    float useAspectRatio = -1.0f;

    if (m_scale_mode == SCALE_MODE_KEEP_TEXTURE_ASPECT_RATIO)
    {
        useAspectRatio = (float)m_texture_w / (float)m_texture_h;
    }
    else if (m_scale_mode == SCALE_MODE_KEEP_CUSTOM_ASPECT_RATIO)
    {
        useAspectRatio = m_custom_aspect_ratio;
    }

    int suggested_h = m_h;
    int suggested_w = (int)((useAspectRatio < 0 ? m_w : useAspectRatio * suggested_h));

    if (suggested_w > m_w)
    {
        const float needed_scale_factor = (float)m_w / (float)suggested_w;
        suggested_w = (int)(suggested_w*needed_scale_factor);
        suggested_h = (int)(suggested_h*needed_scale_factor);
    }
    const int x_from = m_x + (m_w - suggested_w)/2; // center horizontally
    const int y_from = m_y + (m_h - suggested_h)/2; // center vertically

    rect<s32> widget_size = rect<s32>(x_from,
                                      y_from,
                                      x_from + suggested_w,
                                      y_from + suggested_h);

    IGUIButton* btn = GUIEngine::getGUIEnv()->addButton(widget_size,
                                                        m_parent,
                                                        (m_tab_stop ? getNewID() : getNewNoFocusID()),
                                                        L"");

    btn->setTabStop(m_tab_stop);
    m_element = btn;
    m_id = m_element->getID();

    // ---- label if any
    const stringw& message = getText();
    if (message.size() > 0)
    {
        const int label_extra_size =
            ( m_properties[PROP_EXTEND_LABEL].size() == 0 ?
               0 : atoi(m_properties[PROP_EXTEND_LABEL].c_str()) );

        const bool word_wrap = (m_properties[PROP_WORD_WRAP] == "true");

        if (m_properties[PROP_LABELS_LOCATION] == "hover")
        {
            widget_size = rect<s32>(m_x - label_extra_size/2,
                                    m_y - (word_wrap ? GUIEngine::getFontHeight()*2 :
                                                 GUIEngine::getFontHeight()) - 15,
                                    m_x + m_w + label_extra_size/2,
                                    m_y - 15);
        }
        else
        {
            // leave enough room for two lines of text if word wrap is enabled, otherwise a single line
            widget_size = rect<s32>(m_x - label_extra_size/2,
                                    m_y + m_h,
                                    m_x + m_w + label_extra_size/2,
                                    m_y + m_h + (word_wrap ? GUIEngine::getFontHeight()*2 :
                                                             GUIEngine::getFontHeight()));
        }

        m_label = GUIEngine::getGUIEnv()->addStaticText(message.c_str(), widget_size,
                                                        false, word_wrap, m_parent);
        m_label->setTextAlignment(EGUIA_CENTER, EGUIA_UPPERLEFT);
        m_label->setTabStop(false);
        m_label->setNotClipped(true);

        if (m_properties[PROP_LABELS_LOCATION] == "hover")
        {
            m_label->setVisible(false);
        }

        setLabelFont();

#if IRRLICHT_VERSION_MAJOR > 1 || (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR >= 8)
        m_label->setRightToLeft( translations->isRTLLanguage() );
        m_label->setTextRestrainedInside(false);
#endif
    }

    // ---- IDs
    m_id = m_element->getID();
    if (m_tab_stop) m_element->setTabOrder(m_id);
    m_element->setTabGroup(false);
}