<commit_before>/*      _______   __   __   __   ______   __   __   _______   __   __
 *     / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___  /\ /  |\/ /\
 *    / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
 *   / / /__   / / // / // / // / /    / ___  / // ___  / // /| ' / /
 *  / /_// /\ / /_// / // / // /_/_   / / // / // /\_/ / // / |  / /
 * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
 * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
 *
 * Copyright (c) 2004 - 2008 Olof Naessn and Per Larsson
 *
 *
 * Per Larsson a.k.a finalman
 * Olof Naessn a.k.a jansem/yakslem
 *
 * Visit: http://guichan.sourceforge.net
 *
 * License: (BSD)
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name of Guichan nor the names of its contributors may
 *    be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * For comments regarding functions please see the header file.
 */

#include "guichan/widgets/tabbedarea.hpp"

#include "guichan/exception.hpp"
#include "guichan/focushandler.hpp"
#include "guichan/font.hpp"
#include "guichan/graphics.hpp"

#include "guichan/widgets/container.hpp"
#include "guichan/widgets/tab.hpp"

#include <algorithm>

namespace gcn
{
    TabbedArea::TabbedArea()
            :mSelectedTab(NULL),
             mOpaque(false)
    {
        setFocusable(true);
        addKeyListener(this);
        addMouseListener(this);

        mTabContainer = new Container();
        mTabContainer->setOpaque(false);
        mWidgetContainer = new Container();

        add(mTabContainer);
        add(mWidgetContainer);
    }

    TabbedArea::~TabbedArea()
    {
        remove(mTabContainer);
        remove(mWidgetContainer);

        delete mTabContainer;
        delete mWidgetContainer;

        for (unsigned int i = 0; i < mTabsToDelete.size(); i++)
        {
            delete mTabsToDelete[i];
        }
    }

    void TabbedArea::addTab(const std::string& caption, Widget* widget)
    {
        Tab* tab = new Tab();
        tab->setCaption(caption);
        mTabsToDelete.push_back(tab);

        addTab(tab, widget);
    }

    void TabbedArea::addTab(Tab* tab, Widget* widget)
    {
        tab->setTabbedArea(this);
        tab->addActionListener(this);

        mTabContainer->add(tab);
        mTabs.push_back(std::pair<Tab*, Widget*>(tab, widget));

        if (mSelectedTab == NULL)
        {
            setSelectedTab(tab);
        }

        adjustTabPositions();
        adjustSize();
    }

    void TabbedArea::removeTabWithIndex(unsigned int index)
    {
        if (index >= mTabs.size())
        {
            throw GCN_EXCEPTION("No such tab index.");
        }

        removeTab(mTabs[index].first);
    }

    void TabbedArea::removeTab(Tab* tab)
    {
        int tabIndexToBeSelected = - 1;

        if (tab == mSelectedTab)
        {
            int index = getSelectedTabIndex();

            if (index == (int)mTabs.size() - 1
                && mTabs.size() >= 2)
            {
                tabIndexToBeSelected = index--;
            }
            else if (index == (int)mTabs.size() - 1
                     && mTabs.size() == 1)
            {
                tabIndexToBeSelected = -1;
            }
            else
            {
                tabIndexToBeSelected = index;
            }
        }

        std::vector<std::pair<Tab*, Widget*> >::iterator iter;
        for (iter = mTabs.begin(); iter != mTabs.end(); iter++)
        {
            if (iter->first == tab)
            {
                mTabContainer->remove(tab);
                mTabs.erase(iter);
                break;
            }
        }

        std::vector<Tab*>::iterator iter2;
        for (iter2 = mTabsToDelete.begin(); iter2 != mTabsToDelete.end(); iter2++)
        {
            if (*iter2 == tab)
            {
                mTabsToDelete.erase(iter2);
                delete tab;
                break;
            }
        }

        if (tabIndexToBeSelected == -1)
        {
            mSelectedTab = NULL;
            mWidgetContainer->clear();
        }
        else
        {
            setSelectedTab(tabIndexToBeSelected);
        }

        adjustSize();
        adjustTabPositions();
    }

    bool TabbedArea::isTabSelected(unsigned int index) const
    {
        if (index >= mTabs.size())
        {
            throw GCN_EXCEPTION("No such tab index.");
        }

        return mSelectedTab == mTabs[index].first;
    }

    bool TabbedArea::isTabSelected(Tab* tab)
    {
        return mSelectedTab == tab;
    }

    void TabbedArea::setSelectedTab(unsigned int index)
    {
        if (index >= mTabs.size())
        {
            throw GCN_EXCEPTION("No such tab index.");
        }

        setSelectedTab(mTabs[index].first);
    }

    void TabbedArea::setSelectedTab(Tab* tab)
    {
        unsigned int i;
        for (i = 0; i < mTabs.size(); i++)
        {
            if (mTabs[i].first == mSelectedTab)
            {
                mWidgetContainer->remove(mTabs[i].second);
            }
        }

        for (i = 0; i < mTabs.size(); i++)
        {
            if (mTabs[i].first == tab)
            {
                mSelectedTab = tab;
                mWidgetContainer->add(mTabs[i].second);
            }
        }
    }

    int TabbedArea::getSelectedTabIndex() const
    {
        unsigned int i;
        for (i = 0; i < mTabs.size(); i++)
        {
            if (mTabs[i].first == mSelectedTab)
            {
                return i;
            }
        }

        return -1;
    }

    Tab* TabbedArea::getSelectedTab()
    {
        return mSelectedTab;
    }

    void TabbedArea::setOpaque(bool opaque)
    {
        mOpaque = opaque;
    }

    bool TabbedArea::isOpaque() const
    {
        return mOpaque;
    }

    void TabbedArea::draw(Graphics *graphics)
    {
        const Color &faceColor = getBaseColor();
        const int alpha = getBaseColor().a;
        Color highlightColor = faceColor + 0x303030;
        highlightColor.a = alpha;
        Color shadowColor = faceColor - 0x303030;
        shadowColor.a = alpha;

        // Draw a border.
        graphics->setColor(highlightColor);
        graphics->drawLine(0,
                           mTabContainer->getHeight(),
                           0,
                           getHeight() - 2);
        graphics->setColor(shadowColor);
        graphics->drawLine(getWidth() - 1,
                           mTabContainer->getHeight() + 1,
                           getWidth() - 1,
                           getHeight() - 1);
        graphics->drawLine(1,
                           getHeight() - 1,
                           getWidth() - 1,
                           getHeight() - 1);

        if (isOpaque())
        {
            graphics->setColor(getBaseColor());
            graphics->fillRectangle(Rectangle(1, 1,
                                              getWidth() - 2,
                                              getHeight() - 2));
        }

        // Draw a line underneath the tabs.
        graphics->setColor(highlightColor);
        graphics->drawLine(1,
                           mTabContainer->getHeight(),
                           getWidth() - 1,
                           mTabContainer->getHeight());

        // If a tab is selected, remove the line right underneath
        // the selected tab.
        if (mSelectedTab != NULL)
        {
            graphics->setColor(getBaseColor());
            graphics->drawLine(mSelectedTab->getX() + 1,
                               mTabContainer->getHeight(),
                               mSelectedTab->getX() + mSelectedTab->getWidth() - 2,
                               mTabContainer->getHeight());

        }

        drawChildren(graphics);
    }

    void TabbedArea::adjustSize()
    {
        int maxTabHeight = 0;

        for (unsigned int i = 0; i < mTabs.size(); i++)
        {
            if (mTabs[i].first->getHeight() > maxTabHeight)
            {
                maxTabHeight = mTabs[i].first->getHeight();
            }
        }

        mTabContainer->setSize(getWidth() - 2,
                               maxTabHeight);

        mWidgetContainer->setPosition(1, maxTabHeight + 1);
        mWidgetContainer->setSize(getWidth() - 2,
                                  getHeight() - maxTabHeight - 2);
    }

    void TabbedArea::adjustTabPositions()
    {
        int maxTabHeight = 0;
        unsigned int i;
        for (i = 0; i < mTabs.size(); i++)
        {
            if (mTabs[i].first->getHeight() > maxTabHeight)
            {
                maxTabHeight = mTabs[i].first->getHeight();
            }
        }

        int x = 0;
        for (i = 0; i < mTabs.size(); i++)
        {
            Tab* tab = mTabs[i].first;
            tab->setPosition(x, maxTabHeight - tab->getHeight());
            x += tab->getWidth();
        }
    }

    void TabbedArea::setWidth(int width)
    {
        Widget::setWidth(width);
        adjustSize();
    }

    void TabbedArea::setHeight(int height)
    {
        gcn::Rectangle dim = mDimension;
        mDimension.height = height;
        adjustSize();
        mDimension = dim;
        Widget::setHeight(height);
    }

    void TabbedArea::setSize(int width, int height)
    {
        gcn::Rectangle dim = mDimension;
        mDimension.width = width;
        mDimension.height = height;
        adjustSize();
        mDimension = dim;
        Widget::setSize(width, height);
    }

    void TabbedArea::setDimension(const Rectangle& dimension)
    {
        gcn::Rectangle dim = mDimension;
        mDimension.x = dimension.x;
        mDimension.y = dimension.y;
        mDimension.width = dimension.width;
        mDimension.height = dimension.height;
        adjustSize();
        Widget::setDimension(dimension);
    }

    void TabbedArea::keyPressed(KeyEvent& keyEvent)
    {
        if (keyEvent.isConsumed() || !isFocused())
        {
            return;
        }

        if (keyEvent.getKey().getValue() == Key::Left)
        {
            int index = getSelectedTabIndex();
            index--;

            if (index < 0)
            {
                return;
            }
            else
            {
                setSelectedTab(mTabs[index].first);
            }

            keyEvent.consume();
        }
        else if (keyEvent.getKey().getValue() == Key::Right)
        {
            int index = getSelectedTabIndex();
            index++;

            if (index >= (int)mTabs.size())
            {
                return;
            }
            else
            {
                setSelectedTab(mTabs[index].first);
            }

            keyEvent.consume();
        }
    }

    void TabbedArea::mousePressed(MouseEvent& mouseEvent)
    {
        if (mouseEvent.isConsumed())
        {
            return;
        }

        if (mouseEvent.getButton() == MouseEvent::Left)
        {
            Widget* widget = mTabContainer->getWidgetAt(mouseEvent.getX(), mouseEvent.getY());
            Tab* tab = dynamic_cast<Tab*>(widget);

            if (tab != NULL)
            {
                setSelectedTab(tab);
            }
        }

        // Request focus only if the source of the event
        // is not focusble. If the source of the event
        // is focused we don't want to steal the focus.
        if (!mouseEvent.getSource()->isFocusable())
        {
            requestFocus();
        }
    }

    void TabbedArea::death(const Event& event)
    {
        Widget* source = event.getSource();
        Tab* tab = dynamic_cast<Tab*>(source);

        if (tab != NULL)
        {
            removeTab(tab);
        }
        else
        {
            BasicContainer::death(event);
        }
    }

    void TabbedArea::action(const ActionEvent& actionEvent)
    {
        Widget* source = actionEvent.getSource();
        Tab* tab = dynamic_cast<Tab*>(source);

        if (tab == NULL)
        {
            throw GCN_EXCEPTION("Received an action from a widget that's not a tab!");
        }

        setSelectedTab(tab);
    }

    void TabbedArea::setBaseColor(const Color& color)
    {
        Widget::setBaseColor(color);
        mWidgetContainer->setBaseColor(color);
        mTabContainer->setBaseColor(color);
    }
}
<commit_msg>Comments about resizing have been added. setWidth now properly adjusts the size before calling Widget::setWidth.<commit_after>/*      _______   __   __   __   ______   __   __   _______   __   __
 *     / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___  /\ /  |\/ /\
 *    / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
 *   / / /__   / / // / // / // / /    / ___  / // ___  / // /| ' / /
 *  / /_// /\ / /_// / // / // /_/_   / / // / // /\_/ / // / |  / /
 * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
 * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
 *
 * Copyright (c) 2004 - 2008 Olof Naessn and Per Larsson
 *
 *
 * Per Larsson a.k.a finalman
 * Olof Naessn a.k.a jansem/yakslem
 *
 * Visit: http://guichan.sourceforge.net
 *
 * License: (BSD)
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name of Guichan nor the names of its contributors may
 *    be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * For comments regarding functions please see the header file.
 */

#include "guichan/widgets/tabbedarea.hpp"

#include "guichan/exception.hpp"
#include "guichan/focushandler.hpp"
#include "guichan/font.hpp"
#include "guichan/graphics.hpp"

#include "guichan/widgets/container.hpp"
#include "guichan/widgets/tab.hpp"

#include <algorithm>

namespace gcn
{
    TabbedArea::TabbedArea()
            :mSelectedTab(NULL),
             mOpaque(false)
    {
        setFocusable(true);
        addKeyListener(this);
        addMouseListener(this);

        mTabContainer = new Container();
        mTabContainer->setOpaque(false);
        mWidgetContainer = new Container();

        add(mTabContainer);
        add(mWidgetContainer);
    }

    TabbedArea::~TabbedArea()
    {
        remove(mTabContainer);
        remove(mWidgetContainer);

        delete mTabContainer;
        delete mWidgetContainer;

        for (unsigned int i = 0; i < mTabsToDelete.size(); i++)
        {
            delete mTabsToDelete[i];
        }
    }

    void TabbedArea::addTab(const std::string& caption, Widget* widget)
    {
        Tab* tab = new Tab();
        tab->setCaption(caption);
        mTabsToDelete.push_back(tab);

        addTab(tab, widget);
    }

    void TabbedArea::addTab(Tab* tab, Widget* widget)
    {
        tab->setTabbedArea(this);
        tab->addActionListener(this);

        mTabContainer->add(tab);
        mTabs.push_back(std::pair<Tab*, Widget*>(tab, widget));

        if (mSelectedTab == NULL)
        {
            setSelectedTab(tab);
        }

        adjustTabPositions();
        adjustSize();
    }

    void TabbedArea::removeTabWithIndex(unsigned int index)
    {
        if (index >= mTabs.size())
        {
            throw GCN_EXCEPTION("No such tab index.");
        }

        removeTab(mTabs[index].first);
    }

    void TabbedArea::removeTab(Tab* tab)
    {
        int tabIndexToBeSelected = - 1;

        if (tab == mSelectedTab)
        {
            int index = getSelectedTabIndex();

            if (index == (int)mTabs.size() - 1
                && mTabs.size() >= 2)
            {
                tabIndexToBeSelected = index--;
            }
            else if (index == (int)mTabs.size() - 1
                     && mTabs.size() == 1)
            {
                tabIndexToBeSelected = -1;
            }
            else
            {
                tabIndexToBeSelected = index;
            }
        }

        std::vector<std::pair<Tab*, Widget*> >::iterator iter;
        for (iter = mTabs.begin(); iter != mTabs.end(); iter++)
        {
            if (iter->first == tab)
            {
                mTabContainer->remove(tab);
                mTabs.erase(iter);
                break;
            }
        }

        std::vector<Tab*>::iterator iter2;
        for (iter2 = mTabsToDelete.begin(); iter2 != mTabsToDelete.end(); iter2++)
        {
            if (*iter2 == tab)
            {
                mTabsToDelete.erase(iter2);
                delete tab;
                break;
            }
        }

        if (tabIndexToBeSelected == -1)
        {
            mSelectedTab = NULL;
            mWidgetContainer->clear();
        }
        else
        {
            setSelectedTab(tabIndexToBeSelected);
        }

        adjustSize();
        adjustTabPositions();
    }

    bool TabbedArea::isTabSelected(unsigned int index) const
    {
        if (index >= mTabs.size())
        {
            throw GCN_EXCEPTION("No such tab index.");
        }

        return mSelectedTab == mTabs[index].first;
    }

    bool TabbedArea::isTabSelected(Tab* tab)
    {
        return mSelectedTab == tab;
    }

    void TabbedArea::setSelectedTab(unsigned int index)
    {
        if (index >= mTabs.size())
        {
            throw GCN_EXCEPTION("No such tab index.");
        }

        setSelectedTab(mTabs[index].first);
    }

    void TabbedArea::setSelectedTab(Tab* tab)
    {
        unsigned int i;
        for (i = 0; i < mTabs.size(); i++)
        {
            if (mTabs[i].first == mSelectedTab)
            {
                mWidgetContainer->remove(mTabs[i].second);
            }
        }

        for (i = 0; i < mTabs.size(); i++)
        {
            if (mTabs[i].first == tab)
            {
                mSelectedTab = tab;
                mWidgetContainer->add(mTabs[i].second);
            }
        }
    }

    int TabbedArea::getSelectedTabIndex() const
    {
        unsigned int i;
        for (i = 0; i < mTabs.size(); i++)
        {
            if (mTabs[i].first == mSelectedTab)
            {
                return i;
            }
        }

        return -1;
    }

    Tab* TabbedArea::getSelectedTab()
    {
        return mSelectedTab;
    }

    void TabbedArea::setOpaque(bool opaque)
    {
        mOpaque = opaque;
    }

    bool TabbedArea::isOpaque() const
    {
        return mOpaque;
    }

    void TabbedArea::draw(Graphics *graphics)
    {
        const Color &faceColor = getBaseColor();
        const int alpha = getBaseColor().a;
        Color highlightColor = faceColor + 0x303030;
        highlightColor.a = alpha;
        Color shadowColor = faceColor - 0x303030;
        shadowColor.a = alpha;

        // Draw a border.
        graphics->setColor(highlightColor);
        graphics->drawLine(0,
                           mTabContainer->getHeight(),
                           0,
                           getHeight() - 2);
        graphics->setColor(shadowColor);
        graphics->drawLine(getWidth() - 1,
                           mTabContainer->getHeight() + 1,
                           getWidth() - 1,
                           getHeight() - 1);
        graphics->drawLine(1,
                           getHeight() - 1,
                           getWidth() - 1,
                           getHeight() - 1);

        if (isOpaque())
        {
            graphics->setColor(getBaseColor());
            graphics->fillRectangle(Rectangle(1, 1,
                                              getWidth() - 2,
                                              getHeight() - 2));
        }

        // Draw a line underneath the tabs.
        graphics->setColor(highlightColor);
        graphics->drawLine(1,
                           mTabContainer->getHeight(),
                           getWidth() - 1,
                           mTabContainer->getHeight());

        // If a tab is selected, remove the line right underneath
        // the selected tab.
        if (mSelectedTab != NULL)
        {
            graphics->setColor(getBaseColor());
            graphics->drawLine(mSelectedTab->getX() + 1,
                               mTabContainer->getHeight(),
                               mSelectedTab->getX() + mSelectedTab->getWidth() - 2,
                               mTabContainer->getHeight());

        }

        drawChildren(graphics);
    }

    void TabbedArea::adjustSize()
    {
        int maxTabHeight = 0;

        for (unsigned int i = 0; i < mTabs.size(); i++)
        {
            if (mTabs[i].first->getHeight() > maxTabHeight)
            {
                maxTabHeight = mTabs[i].first->getHeight();
            }
        }

        mTabContainer->setSize(getWidth() - 2,
                               maxTabHeight);

        mWidgetContainer->setPosition(1, maxTabHeight + 1);
        mWidgetContainer->setSize(getWidth() - 2,
                                  getHeight() - maxTabHeight - 2);
    }

    void TabbedArea::adjustTabPositions()
    {
        int maxTabHeight = 0;
        unsigned int i;
        for (i = 0; i < mTabs.size(); i++)
        {
            if (mTabs[i].first->getHeight() > maxTabHeight)
            {
                maxTabHeight = mTabs[i].first->getHeight();
            }
        }

        int x = 0;
        for (i = 0; i < mTabs.size(); i++)
        {
            Tab* tab = mTabs[i].first;
            tab->setPosition(x, maxTabHeight - tab->getHeight());
            x += tab->getWidth();
        }
    }

    void TabbedArea::setWidth(int width)
    {
        // This may seem odd, but we want the TabbedArea to adjust
        // it's size properly before we call Widget::setWidth as
        // Widget::setWidth might distribute a resize event.
        gcn::Rectangle dim = mDimension;
        mDimension.width = width;
        adjustSize();
        mDimension = dim;
        Widget::setWidth(width);
    }

    void TabbedArea::setHeight(int height)
    {
        // This may seem odd, but we want the TabbedArea to adjust
        // it's size properly before we call Widget::setHeight as
        // Widget::setHeight might distribute a resize event.
        gcn::Rectangle dim = mDimension;
        mDimension.height = height;
        adjustSize();
        mDimension = dim;
        Widget::setHeight(height);
    }

    void TabbedArea::setSize(int width, int height)
    {
        // This may seem odd, but we want the TabbedArea to adjust
        // it's size properly before we call Widget::setSize as
        // Widget::setSize might distribute a resize event.
        gcn::Rectangle dim = mDimension;
        mDimension.width = width;
        mDimension.height = height;
        adjustSize();
        mDimension = dim;
        Widget::setSize(width, height);
    }

    void TabbedArea::setDimension(const Rectangle& dimension)
    {
        // This may seem odd, but we want the TabbedArea to adjust
        // it's size properly before we call Widget::setDimension as
        // Widget::setDimension might distribute a resize event.
        gcn::Rectangle dim = mDimension;
        mDimension = dimension;
        adjustSize();
        Widget::setDimension(dimension);
    }

    void TabbedArea::keyPressed(KeyEvent& keyEvent)
    {
        if (keyEvent.isConsumed() || !isFocused())
        {
            return;
        }

        if (keyEvent.getKey().getValue() == Key::Left)
        {
            int index = getSelectedTabIndex();
            index--;

            if (index < 0)
            {
                return;
            }
            else
            {
                setSelectedTab(mTabs[index].first);
            }

            keyEvent.consume();
        }
        else if (keyEvent.getKey().getValue() == Key::Right)
        {
            int index = getSelectedTabIndex();
            index++;

            if (index >= (int)mTabs.size())
            {
                return;
            }
            else
            {
                setSelectedTab(mTabs[index].first);
            }

            keyEvent.consume();
        }
    }

    void TabbedArea::mousePressed(MouseEvent& mouseEvent)
    {
        if (mouseEvent.isConsumed())
        {
            return;
        }

        if (mouseEvent.getButton() == MouseEvent::Left)
        {
            Widget* widget = mTabContainer->getWidgetAt(mouseEvent.getX(), mouseEvent.getY());
            Tab* tab = dynamic_cast<Tab*>(widget);

            if (tab != NULL)
            {
                setSelectedTab(tab);
            }
        }

        // Request focus only if the source of the event
        // is not focusble. If the source of the event
        // is focused we don't want to steal the focus.
        if (!mouseEvent.getSource()->isFocusable())
        {
            requestFocus();
        }
    }

    void TabbedArea::death(const Event& event)
    {
        Widget* source = event.getSource();
        Tab* tab = dynamic_cast<Tab*>(source);

        if (tab != NULL)
        {
            removeTab(tab);
        }
        else
        {
            BasicContainer::death(event);
        }
    }

    void TabbedArea::action(const ActionEvent& actionEvent)
    {
        Widget* source = actionEvent.getSource();
        Tab* tab = dynamic_cast<Tab*>(source);

        if (tab == NULL)
        {
            throw GCN_EXCEPTION("Received an action from a widget that's not a tab!");
        }

        setSelectedTab(tab);
    }

    void TabbedArea::setBaseColor(const Color& color)
    {
        Widget::setBaseColor(color);
        mWidgetContainer->setBaseColor(color);
        mTabContainer->setBaseColor(color);
    }
}
<|endoftext|>


<commit_before>// For conditions of distribution and use, see copyright notice in LICENSE

#include "StableHeaders.h"
#define MATH_BULLET_INTEROP
#include "DebugOperatorNew.h"

#include "PhysicsModule.h"
#include "PhysicsWorld.h"
#include "PhysicsUtils.h"
#include "Profiler.h"
#include "Scene/Scene.h"
#include "OgreWorld.h"
#include "EC_RigidBody.h"
#include "LoggingFunctions.h"
#include "Geometry/LineSegment.h"
#include "Geometry/OBB.h"
#include "Math/float3x3.h"
#include "Math/Quat.h"
#include "Entity.h"

#include <LinearMath/btIDebugDraw.h>
// Disable unreferenced formal parameter coming from Bullet
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4100)
#endif
#include <btBulletDynamicsCommon.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif

#include <Ogre.h>

#include "MemoryLeakCheck.h"

namespace
{

struct CollisionSignal
{
    EC_RigidBody *bodyA;
    EC_RigidBody *bodyB;
    float3 position;
    float3 normal;
    float distance;
    float impulse;
    bool newCollision;
};

struct ObbCallback : public btCollisionWorld::ContactResultCallback
{
    ObbCallback(std::set<btCollisionObjectWrapper*>& result) : result_(result) {}

    virtual btScalar addSingleResult(btManifoldPoint &cp, const btCollisionObjectWrapper *colObj0, int, int, const btCollisionObjectWrapper *colObj1, int, int)
    {
        result_.insert((btCollisionObjectWrapper*)(colObj0));
        result_.insert((btCollisionObjectWrapper*)(colObj1));
        return 0.0f;
    }

    std::set<btCollisionObjectWrapper*>& result_;
};

void TickCallback(btDynamicsWorld *world, btScalar timeStep)
{
    static_cast<PhysicsWorld*>(world->getWorldUserInfo())->ProcessPostTick(timeStep);
}

} // ~unnamed namespace

struct PhysicsWorld::Impl : public btIDebugDraw
{
    explicit Impl(PhysicsWorld *owner) :
        collisionConfiguration(0),
        collisionDispatcher(0),
        broadphase(0),
        solver(0),
        world(0),
        debugDrawMode(0),
        cachedOgreWorld(0)
    {
#include "DisableMemoryLeakCheck.h"
        collisionConfiguration = new btDefaultCollisionConfiguration();
        collisionDispatcher = new btCollisionDispatcher(collisionConfiguration);
        broadphase = new btDbvtBroadphase();
        solver = new btSequentialImpulseConstraintSolver();
        world = new btDiscreteDynamicsWorld(collisionDispatcher, broadphase, solver, collisionConfiguration);
        world->setDebugDrawer(this);
        world->setInternalTickCallback(TickCallback, (void*)owner, false);
#include "EnableMemoryLeakCheck.h"
    }

    ~Impl()
    {
        delete world;
        delete solver;
        delete broadphase;
        delete collisionDispatcher;
        delete collisionConfiguration;
    }

    /// btIDebugDraw override
    virtual void drawLine(const btVector3& from, const btVector3& to, const btVector3& color)
    {
        if (IsDebugGeometryEnabled() && cachedOgreWorld)
            cachedOgreWorld->DebugDrawLine(from, to, color.x(), color.y(), color.z());
    }

    /// btIDebugDraw override
    virtual void reportErrorWarning(const char* warningString)
    {
        LogWarning("Physics: " + std::string(warningString));
    }

    /// btIDebugDraw override, does nothing.
    /// @todo Now that we have EC_PhysicsConstraint, implement this!
    virtual void drawContactPoint(const btVector3& /*pointOnB*/, const btVector3& /*normalOnB*/, btScalar /*distance*/, int /*lifeTime*/, const btVector3& /*color*/) {}

    /// btIDebugDraw override, does nothing.
    virtual void draw3dText(const btVector3& /*location*/, const char* /*textString*/) {}

    /// btIDebugDraw override
    virtual void setDebugMode(int debugMode) { debugDrawMode = debugMode; }

    /// btIDebugDraw override
    virtual int getDebugMode() const { return debugDrawMode; }

    bool IsDebugGeometryEnabled() const { return getDebugMode() != btIDebugDraw::DBG_NoDebug; }

    /// Bullet collision config
    btCollisionConfiguration* collisionConfiguration;
    /// Bullet collision dispatcher
    btDispatcher* collisionDispatcher;
    /// Bullet collision broadphase
    btBroadphaseInterface* broadphase;
    /// Bullet constraint equation solver
    btConstraintSolver* solver;
    /// Bullet physics world
    btDiscreteDynamicsWorld* world;
    /// Bullet debug draw / debug behaviour flags
    int debugDrawMode;
    /// Cached OgreWorld pointer for drawing debug geometry
    OgreWorld* cachedOgreWorld;
};

PhysicsWorld::PhysicsWorld(const ScenePtr &scene, bool isClient) :
    scene_(scene),
    physicsUpdatePeriod_(1.0f / 60.0f),
    maxSubSteps_(6), // If fps is below 10, we start to slow down physics
    isClient_(isClient),
    runPhysics_(true),
    drawDebugManuallySet_(false),
    useVariableTimestep_(false),
    impl(new Impl(this))
{
    if (scene->GetFramework()->HasCommandLineParameter("--variablephysicsstep"))
        useVariableTimestep_ = true;
}

PhysicsWorld::~PhysicsWorld()
{
    delete impl;
}

void PhysicsWorld::SetPhysicsUpdatePeriod(float updatePeriod)
{
    // Allow max.1000 fps
    if (updatePeriod <= 0.001f)
        updatePeriod = 0.001f;
    physicsUpdatePeriod_ = updatePeriod;
}

void PhysicsWorld::SetMaxSubSteps(int steps)
{
    if (steps > 0)
        maxSubSteps_ = steps;
}

void PhysicsWorld::SetGravity(const float3& gravity)
{
    impl->world->setGravity(gravity);
}

float3 PhysicsWorld::Gravity() const
{
    return impl->world->getGravity();
}

btDiscreteDynamicsWorld* PhysicsWorld::BulletWorld() const
{
    return impl->world;
}

void PhysicsWorld::Simulate(f64 frametime)
{
    if (!runPhysics_)
        return;

    PROFILE(PhysicsWorld_Simulate);

    emit AboutToUpdate((float)frametime);

    {
        PROFILE(Bullet_stepSimulation); ///\note Do not delete or rename this PROFILE() block. The DebugStats profiler uses this string as a label to know where to inject the Bullet internal profiling data.

        // Use variable timestep if enabled, and if frame timestep exceeds the single physics simulation substep
        if (useVariableTimestep_ && frametime > physicsUpdatePeriod_)
        {
            float clampedTimeStep = (float)frametime;
            if (clampedTimeStep > 0.1f)
                clampedTimeStep = 0.1f; // Advance max. 1/10 sec. during one frame
            impl->world->stepSimulation(clampedTimeStep, 0, clampedTimeStep);
        }
        else
            impl->world->stepSimulation((float)frametime, maxSubSteps_, physicsUpdatePeriod_);
    }

    // Automatically enable debug geometry if at least one debug-enabled rigidbody. Automatically disable if no debug-enabled rigidbodies
    // However, do not do this if user has used the physicsdebug console command
    if (!drawDebugManuallySet_)
    {
        if (!IsDebugGeometryEnabled() && !debugRigidBodies_.empty())
            SetDebugGeometryEnabled(true);
        if (IsDebugGeometryEnabled() && debugRigidBodies_.empty())
            SetDebugGeometryEnabled(false);
    }

    if (IsDebugGeometryEnabled())
        DrawDebugGeometry();
}

void PhysicsWorld::ProcessPostTick(float substeptime)
{
    PROFILE(PhysicsWorld_ProcessPostTick);
    // Check contacts and send collision signals for them
    int numManifolds = impl->collisionDispatcher->getNumManifolds();

    std::set<std::pair<const btCollisionObject*, const btCollisionObject*> > currentCollisions;

    // Collect all collision signals to a list before emitting any of them, in case a collision
    // handler changes physics state before the loop below is over (which would lead into catastrophic
    // consequences)
    std::vector<CollisionSignal> collisions;
    collisions.reserve(numManifolds * 3); // Guess some initial memory size for the collision list.

    if (numManifolds > 0)
    {
        PROFILE(PhysicsWorld_SendCollisions);

        for(int i = 0; i < numManifolds; ++i)
        {
            btPersistentManifold* contactManifold = impl->collisionDispatcher->getManifoldByIndexInternal(i);
            int numContacts = contactManifold->getNumContacts();
            if (numContacts == 0)
                continue;

            const btCollisionObject* objectA = contactManifold->getBody0();
            const btCollisionObject* objectB = contactManifold->getBody1();

            std::pair<const btCollisionObject*, const btCollisionObject*> objectPair;
            if (objectA < objectB)
                objectPair = std::make_pair(objectA, objectB);
            else
                objectPair = std::make_pair(objectB, objectA);

            EC_RigidBody* bodyA = static_cast<EC_RigidBody*>(objectA->getUserPointer());
            EC_RigidBody* bodyB = static_cast<EC_RigidBody*>(objectB->getUserPointer());

            // We are only interested in collisions where both EC_RigidBody components are known
            if (!bodyA || !bodyB)
            {
                LogError("Inconsistent Bullet physics scene state! An object exists in the physics scene which does not have an associated EC_RigidBody!");
                continue;
            }
            // Also, both bodies should have valid parent entities
            Entity* entityA = bodyA->ParentEntity();
            Entity* entityB = bodyB->ParentEntity();
            if (!entityA || !entityB)
            {
                LogError("Inconsistent Bullet physics scene state! A parentless EC_RigidBody exists in the physics scene!");
                continue;
            }
            // Check that at least one of the bodies is active
            if (!objectA->isActive() && !objectB->isActive())
                continue;

            bool newCollision = previousCollisions_.find(objectPair) == previousCollisions_.end();

            for(int j = 0; j < numContacts; ++j)
            {
                btManifoldPoint& point = contactManifold->getContactPoint(j);

                CollisionSignal s;
                s.bodyA = bodyA;
                s.bodyB = bodyB;
                s.position = point.m_positionWorldOnB;
                s.normal = point.m_normalWorldOnB;
                s.distance = point.m_distance1;
                s.impulse = point.m_appliedImpulse;
                s.newCollision = newCollision;
                collisions.push_back(s);

                // Report newCollision = true only for the first contact, in case there are several contacts, and application does some logic depending on it
                // (for example play a sound -> avoid multiple sounds being played)
                newCollision = false;
            }

            currentCollisions.insert(objectPair);
        }
    }

    // Now fire all collision signals.
    {
        PROFILE(PhysicsWorld_emit_PhysicsCollisions);
        for(size_t i = 0; i < collisions.size(); ++i)
        {
            emit PhysicsCollision(collisions[i].bodyA->ParentEntity(), collisions[i].bodyB->ParentEntity(), collisions[i].position, collisions[i].normal, collisions[i].distance, collisions[i].impulse, collisions[i].newCollision);
            collisions[i].bodyA->EmitPhysicsCollision(collisions[i].bodyB->ParentEntity(), collisions[i].position, collisions[i].normal, collisions[i].distance, collisions[i].impulse, collisions[i].newCollision);
            collisions[i].bodyB->EmitPhysicsCollision(collisions[i].bodyA->ParentEntity(), collisions[i].position, collisions[i].normal, collisions[i].distance, collisions[i].impulse, collisions[i].newCollision);
        }
    }

    previousCollisions_ = currentCollisions;

    {
        PROFILE(PhysicsWorld_ProcessPostTick_Updated);
        emit Updated(substeptime);
    }
}

PhysicsRaycastResult* PhysicsWorld::Raycast(const float3& origin, const float3& direction, float maxdistance, int collisiongroup, int collisionmask)
{
    PROFILE(PhysicsWorld_Raycast);

    static PhysicsRaycastResult result;

    float3 normalizedDir = direction.Normalized();

    btCollisionWorld::ClosestRayResultCallback rayCallback(origin, origin + maxdistance * normalizedDir);
    rayCallback.m_collisionFilterGroup = collisiongroup;
    rayCallback.m_collisionFilterMask = collisionmask;

    impl->world->rayTest(rayCallback.m_rayFromWorld, rayCallback.m_rayToWorld, rayCallback);

    result.entity = 0;
    result.distance = 0;

    if (rayCallback.hasHit())
    {
        result.pos = rayCallback.m_hitPointWorld;
        result.normal = rayCallback.m_hitNormalWorld;
        result.distance = (result.pos - origin).Length();
        if (rayCallback.m_collisionObject)
        {
            EC_RigidBody* body = static_cast<EC_RigidBody*>(rayCallback.m_collisionObject->getUserPointer());
            if (body)
                result.entity = body->ParentEntity();
        }
    }

    return &result;
}

EntityList PhysicsWorld::ObbCollisionQuery(const OBB &obb, int collisionGroup, int collisionMask)
{
    PROFILE(PhysicsWorld_ObbCollisionQuery);

    std::set<btCollisionObjectWrapper*> objects;
    EntityList entities;

    btBoxShape box(obb.HalfSize()); // Note: Bullet uses box halfsize
    float3x3 m(obb.axis[0], obb.axis[1], obb.axis[2]);
    btTransform t1(m.ToQuat(), obb.CenterPoint());
#include "DisableMemoryLeakCheck.h"
    btRigidBody* tempRigidBody = new btRigidBody(1.0f, 0, &box);
#include "EnableMemoryLeakCheck.h"
    tempRigidBody->setWorldTransform(t1);
    impl->world->addRigidBody(tempRigidBody, collisionGroup, collisionMask);
    tempRigidBody->activate(); // To make sure we get collision results from static sleeping rigidbodies, activate the temp rigid body

    ObbCallback resultCallback(objects);
    impl->world->contactTest(tempRigidBody, resultCallback);

    for (std::set<btCollisionObjectWrapper*>::iterator i = objects.begin(); i != objects.end(); ++i)
    {
        EC_RigidBody* body = static_cast<EC_RigidBody*>((*i)->getCollisionObject()->getUserPointer());
        if (body && body->ParentEntity())
            entities.push_back(body->ParentEntity()->shared_from_this());
    }

    impl->world->removeRigidBody(tempRigidBody);
    delete tempRigidBody;

    return entities;
}

void PhysicsWorld::SetDebugGeometryEnabled(bool enable)
{
    if (scene_.expired() || !scene_.lock()->ViewEnabled() || IsDebugGeometryEnabled() == enable)
        return;

    /// @todo Make possisble to set other debug modes too.
    impl->setDebugMode(enable ? btIDebugDraw::DBG_DrawWireframe | btIDebugDraw::DBG_DrawConstraintLimits | btIDebugDraw::DBG_DrawConstraints : btIDebugDraw::DBG_NoDebug);
}

bool PhysicsWorld::IsDebugGeometryEnabled() const
{
    return impl->IsDebugGeometryEnabled();
}

void PhysicsWorld::DrawDebugGeometry()
{
    if (!IsDebugGeometryEnabled())
        return;

    PROFILE(PhysicsModule_DrawDebugGeometry);

    // Draw debug only for the active (visible) scene
    OgreWorldPtr ogreWorld = scene_.lock()->GetWorld<OgreWorld>();
    impl->cachedOgreWorld = ogreWorld.get();
    if (!ogreWorld)
        return;
    if (!ogreWorld->IsActive())
        return;

    // Get all lines of the physics world
    impl->world->debugDrawWorld();
}
<commit_msg>Hold weak pointers to EC_RigidBody components during collision signaling to check and skip if they are deleted through signal handlers. Fixes #752.<commit_after>// For conditions of distribution and use, see copyright notice in LICENSE

#include "StableHeaders.h"
#define MATH_BULLET_INTEROP
#include "DebugOperatorNew.h"

#include "PhysicsModule.h"
#include "PhysicsWorld.h"
#include "PhysicsUtils.h"
#include "Profiler.h"
#include "Scene/Scene.h"
#include "OgreWorld.h"
#include "EC_RigidBody.h"
#include "LoggingFunctions.h"
#include "Geometry/LineSegment.h"
#include "Geometry/OBB.h"
#include "Math/float3x3.h"
#include "Math/Quat.h"
#include "Entity.h"

#include <LinearMath/btIDebugDraw.h>
// Disable unreferenced formal parameter coming from Bullet
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4100)
#endif
#include <btBulletDynamicsCommon.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif

#include <Ogre.h>

#include "MemoryLeakCheck.h"

namespace
{

struct CollisionSignal
{
    weak_ptr<EC_RigidBody> bodyA;
    weak_ptr<EC_RigidBody> bodyB;
    float3 position;
    float3 normal;
    float distance;
    float impulse;
    bool newCollision;
};

struct ObbCallback : public btCollisionWorld::ContactResultCallback
{
    ObbCallback(std::set<btCollisionObjectWrapper*>& result) : result_(result) {}

    virtual btScalar addSingleResult(btManifoldPoint &cp, const btCollisionObjectWrapper *colObj0, int, int, const btCollisionObjectWrapper *colObj1, int, int)
    {
        result_.insert((btCollisionObjectWrapper*)(colObj0));
        result_.insert((btCollisionObjectWrapper*)(colObj1));
        return 0.0f;
    }

    std::set<btCollisionObjectWrapper*>& result_;
};

void TickCallback(btDynamicsWorld *world, btScalar timeStep)
{
    static_cast<PhysicsWorld*>(world->getWorldUserInfo())->ProcessPostTick(timeStep);
}

} // ~unnamed namespace

struct PhysicsWorld::Impl : public btIDebugDraw
{
    explicit Impl(PhysicsWorld *owner) :
        collisionConfiguration(0),
        collisionDispatcher(0),
        broadphase(0),
        solver(0),
        world(0),
        debugDrawMode(0),
        cachedOgreWorld(0)
    {
#include "DisableMemoryLeakCheck.h"
        collisionConfiguration = new btDefaultCollisionConfiguration();
        collisionDispatcher = new btCollisionDispatcher(collisionConfiguration);
        broadphase = new btDbvtBroadphase();
        solver = new btSequentialImpulseConstraintSolver();
        world = new btDiscreteDynamicsWorld(collisionDispatcher, broadphase, solver, collisionConfiguration);
        world->setDebugDrawer(this);
        world->setInternalTickCallback(TickCallback, (void*)owner, false);
#include "EnableMemoryLeakCheck.h"
    }

    ~Impl()
    {
        delete world;
        delete solver;
        delete broadphase;
        delete collisionDispatcher;
        delete collisionConfiguration;
    }

    /// btIDebugDraw override
    virtual void drawLine(const btVector3& from, const btVector3& to, const btVector3& color)
    {
        if (IsDebugGeometryEnabled() && cachedOgreWorld)
            cachedOgreWorld->DebugDrawLine(from, to, color.x(), color.y(), color.z());
    }

    /// btIDebugDraw override
    virtual void reportErrorWarning(const char* warningString)
    {
        LogWarning("Physics: " + std::string(warningString));
    }

    /// btIDebugDraw override, does nothing.
    /// @todo Now that we have EC_PhysicsConstraint, implement this!
    virtual void drawContactPoint(const btVector3& /*pointOnB*/, const btVector3& /*normalOnB*/, btScalar /*distance*/, int /*lifeTime*/, const btVector3& /*color*/) {}

    /// btIDebugDraw override, does nothing.
    virtual void draw3dText(const btVector3& /*location*/, const char* /*textString*/) {}

    /// btIDebugDraw override
    virtual void setDebugMode(int debugMode) { debugDrawMode = debugMode; }

    /// btIDebugDraw override
    virtual int getDebugMode() const { return debugDrawMode; }

    bool IsDebugGeometryEnabled() const { return getDebugMode() != btIDebugDraw::DBG_NoDebug; }

    /// Bullet collision config
    btCollisionConfiguration* collisionConfiguration;
    /// Bullet collision dispatcher
    btDispatcher* collisionDispatcher;
    /// Bullet collision broadphase
    btBroadphaseInterface* broadphase;
    /// Bullet constraint equation solver
    btConstraintSolver* solver;
    /// Bullet physics world
    btDiscreteDynamicsWorld* world;
    /// Bullet debug draw / debug behaviour flags
    int debugDrawMode;
    /// Cached OgreWorld pointer for drawing debug geometry
    OgreWorld* cachedOgreWorld;
};

PhysicsWorld::PhysicsWorld(const ScenePtr &scene, bool isClient) :
    scene_(scene),
    physicsUpdatePeriod_(1.0f / 60.0f),
    maxSubSteps_(6), // If fps is below 10, we start to slow down physics
    isClient_(isClient),
    runPhysics_(true),
    drawDebugManuallySet_(false),
    useVariableTimestep_(false),
    impl(new Impl(this))
{
    if (scene->GetFramework()->HasCommandLineParameter("--variablephysicsstep"))
        useVariableTimestep_ = true;
}

PhysicsWorld::~PhysicsWorld()
{
    delete impl;
}

void PhysicsWorld::SetPhysicsUpdatePeriod(float updatePeriod)
{
    // Allow max.1000 fps
    if (updatePeriod <= 0.001f)
        updatePeriod = 0.001f;
    physicsUpdatePeriod_ = updatePeriod;
}

void PhysicsWorld::SetMaxSubSteps(int steps)
{
    if (steps > 0)
        maxSubSteps_ = steps;
}

void PhysicsWorld::SetGravity(const float3& gravity)
{
    impl->world->setGravity(gravity);
}

float3 PhysicsWorld::Gravity() const
{
    return impl->world->getGravity();
}

btDiscreteDynamicsWorld* PhysicsWorld::BulletWorld() const
{
    return impl->world;
}

void PhysicsWorld::Simulate(f64 frametime)
{
    if (!runPhysics_)
        return;

    PROFILE(PhysicsWorld_Simulate);

    emit AboutToUpdate((float)frametime);

    {
        PROFILE(Bullet_stepSimulation); ///\note Do not delete or rename this PROFILE() block. The DebugStats profiler uses this string as a label to know where to inject the Bullet internal profiling data.

        // Use variable timestep if enabled, and if frame timestep exceeds the single physics simulation substep
        if (useVariableTimestep_ && frametime > physicsUpdatePeriod_)
        {
            float clampedTimeStep = (float)frametime;
            if (clampedTimeStep > 0.1f)
                clampedTimeStep = 0.1f; // Advance max. 1/10 sec. during one frame
            impl->world->stepSimulation(clampedTimeStep, 0, clampedTimeStep);
        }
        else
            impl->world->stepSimulation((float)frametime, maxSubSteps_, physicsUpdatePeriod_);
    }

    // Automatically enable debug geometry if at least one debug-enabled rigidbody. Automatically disable if no debug-enabled rigidbodies
    // However, do not do this if user has used the physicsdebug console command
    if (!drawDebugManuallySet_)
    {
        if (!IsDebugGeometryEnabled() && !debugRigidBodies_.empty())
            SetDebugGeometryEnabled(true);
        if (IsDebugGeometryEnabled() && debugRigidBodies_.empty())
            SetDebugGeometryEnabled(false);
    }

    if (IsDebugGeometryEnabled())
        DrawDebugGeometry();
}

void PhysicsWorld::ProcessPostTick(float substeptime)
{
    PROFILE(PhysicsWorld_ProcessPostTick);
    // Check contacts and send collision signals for them
    int numManifolds = impl->collisionDispatcher->getNumManifolds();

    std::set<std::pair<const btCollisionObject*, const btCollisionObject*> > currentCollisions;

    // Collect all collision signals to a list before emitting any of them, in case a collision
    // handler changes physics state before the loop below is over (which would lead into catastrophic
    // consequences)
    std::vector<CollisionSignal> collisions;
    collisions.reserve(numManifolds * 3); // Guess some initial memory size for the collision list.

    if (numManifolds > 0)
    {
        PROFILE(PhysicsWorld_SendCollisions);

        for(int i = 0; i < numManifolds; ++i)
        {
            btPersistentManifold* contactManifold = impl->collisionDispatcher->getManifoldByIndexInternal(i);
            int numContacts = contactManifold->getNumContacts();
            if (numContacts == 0)
                continue;

            const btCollisionObject* objectA = contactManifold->getBody0();
            const btCollisionObject* objectB = contactManifold->getBody1();

            std::pair<const btCollisionObject*, const btCollisionObject*> objectPair;
            if (objectA < objectB)
                objectPair = std::make_pair(objectA, objectB);
            else
                objectPair = std::make_pair(objectB, objectA);

            EC_RigidBody* bodyA = static_cast<EC_RigidBody*>(objectA->getUserPointer());
            EC_RigidBody* bodyB = static_cast<EC_RigidBody*>(objectB->getUserPointer());

            // We are only interested in collisions where both EC_RigidBody components are known
            if (!bodyA || !bodyB)
            {
                LogError("Inconsistent Bullet physics scene state! An object exists in the physics scene which does not have an associated EC_RigidBody!");
                continue;
            }
            // Also, both bodies should have valid parent entities
            Entity* entityA = bodyA->ParentEntity();
            Entity* entityB = bodyB->ParentEntity();
            if (!entityA || !entityB)
            {
                LogError("Inconsistent Bullet physics scene state! A parentless EC_RigidBody exists in the physics scene!");
                continue;
            }
            // Check that at least one of the bodies is active
            if (!objectA->isActive() && !objectB->isActive())
                continue;

            bool newCollision = previousCollisions_.find(objectPair) == previousCollisions_.end();

            for(int j = 0; j < numContacts; ++j)
            {
                btManifoldPoint& point = contactManifold->getContactPoint(j);

                CollisionSignal s;
                s.bodyA = static_pointer_cast<EC_RigidBody>(bodyA->shared_from_this());
                s.bodyB = static_pointer_cast<EC_RigidBody>(bodyB->shared_from_this());
                s.position = point.m_positionWorldOnB;
                s.normal = point.m_normalWorldOnB;
                s.distance = point.m_distance1;
                s.impulse = point.m_appliedImpulse;
                s.newCollision = newCollision;
                collisions.push_back(s);

                // Report newCollision = true only for the first contact, in case there are several contacts, and application does some logic depending on it
                // (for example play a sound -> avoid multiple sounds being played)
                newCollision = false;
            }

            currentCollisions.insert(objectPair);
        }
    }

    // Now fire all collision signals. Safeguard for the body components expiring in case signal handlers delete them from the scene
    {
        PROFILE(PhysicsWorld_emit_PhysicsCollisions);
        for(size_t i = 0; i < collisions.size(); ++i)
        {
            if (collisions[i].bodyA.expired() || collisions[i].bodyB.expired())
                continue;
            emit PhysicsCollision(collisions[i].bodyA.lock()->ParentEntity(), collisions[i].bodyB.lock()->ParentEntity(), collisions[i].position, collisions[i].normal, collisions[i].distance, collisions[i].impulse, collisions[i].newCollision);

            if (collisions[i].bodyA.expired() || collisions[i].bodyB.expired())
                continue;
            collisions[i].bodyA.lock()->EmitPhysicsCollision(collisions[i].bodyB.lock()->ParentEntity(), collisions[i].position, collisions[i].normal, collisions[i].distance, collisions[i].impulse, collisions[i].newCollision);

            if (collisions[i].bodyA.expired() || collisions[i].bodyB.expired())
                continue;
            collisions[i].bodyB.lock()->EmitPhysicsCollision(collisions[i].bodyA.lock()->ParentEntity(), collisions[i].position, collisions[i].normal, collisions[i].distance, collisions[i].impulse, collisions[i].newCollision);
        }
    }

    previousCollisions_ = currentCollisions;

    {
        PROFILE(PhysicsWorld_ProcessPostTick_Updated);
        emit Updated(substeptime);
    }
}

PhysicsRaycastResult* PhysicsWorld::Raycast(const float3& origin, const float3& direction, float maxdistance, int collisiongroup, int collisionmask)
{
    PROFILE(PhysicsWorld_Raycast);

    static PhysicsRaycastResult result;

    float3 normalizedDir = direction.Normalized();

    btCollisionWorld::ClosestRayResultCallback rayCallback(origin, origin + maxdistance * normalizedDir);
    rayCallback.m_collisionFilterGroup = collisiongroup;
    rayCallback.m_collisionFilterMask = collisionmask;

    impl->world->rayTest(rayCallback.m_rayFromWorld, rayCallback.m_rayToWorld, rayCallback);

    result.entity = 0;
    result.distance = 0;

    if (rayCallback.hasHit())
    {
        result.pos = rayCallback.m_hitPointWorld;
        result.normal = rayCallback.m_hitNormalWorld;
        result.distance = (result.pos - origin).Length();
        if (rayCallback.m_collisionObject)
        {
            EC_RigidBody* body = static_cast<EC_RigidBody*>(rayCallback.m_collisionObject->getUserPointer());
            if (body)
                result.entity = body->ParentEntity();
        }
    }

    return &result;
}

EntityList PhysicsWorld::ObbCollisionQuery(const OBB &obb, int collisionGroup, int collisionMask)
{
    PROFILE(PhysicsWorld_ObbCollisionQuery);

    std::set<btCollisionObjectWrapper*> objects;
    EntityList entities;

    btBoxShape box(obb.HalfSize()); // Note: Bullet uses box halfsize
    float3x3 m(obb.axis[0], obb.axis[1], obb.axis[2]);
    btTransform t1(m.ToQuat(), obb.CenterPoint());
#include "DisableMemoryLeakCheck.h"
    btRigidBody* tempRigidBody = new btRigidBody(1.0f, 0, &box);
#include "EnableMemoryLeakCheck.h"
    tempRigidBody->setWorldTransform(t1);
    impl->world->addRigidBody(tempRigidBody, collisionGroup, collisionMask);
    tempRigidBody->activate(); // To make sure we get collision results from static sleeping rigidbodies, activate the temp rigid body

    ObbCallback resultCallback(objects);
    impl->world->contactTest(tempRigidBody, resultCallback);

    for (std::set<btCollisionObjectWrapper*>::iterator i = objects.begin(); i != objects.end(); ++i)
    {
        EC_RigidBody* body = static_cast<EC_RigidBody*>((*i)->getCollisionObject()->getUserPointer());
        if (body && body->ParentEntity())
            entities.push_back(body->ParentEntity()->shared_from_this());
    }

    impl->world->removeRigidBody(tempRigidBody);
    delete tempRigidBody;

    return entities;
}

void PhysicsWorld::SetDebugGeometryEnabled(bool enable)
{
    if (scene_.expired() || !scene_.lock()->ViewEnabled() || IsDebugGeometryEnabled() == enable)
        return;

    /// @todo Make possisble to set other debug modes too.
    impl->setDebugMode(enable ? btIDebugDraw::DBG_DrawWireframe | btIDebugDraw::DBG_DrawConstraintLimits | btIDebugDraw::DBG_DrawConstraints : btIDebugDraw::DBG_NoDebug);
}

bool PhysicsWorld::IsDebugGeometryEnabled() const
{
    return impl->IsDebugGeometryEnabled();
}

void PhysicsWorld::DrawDebugGeometry()
{
    if (!IsDebugGeometryEnabled())
        return;

    PROFILE(PhysicsModule_DrawDebugGeometry);

    // Draw debug only for the active (visible) scene
    OgreWorldPtr ogreWorld = scene_.lock()->GetWorld<OgreWorld>();
    impl->cachedOgreWorld = ogreWorld.get();
    if (!ogreWorld)
        return;
    if (!ogreWorld->IsActive())
        return;

    // Get all lines of the physics world
    impl->world->debugDrawWorld();
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2006, MassaRoddel, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/pch.hpp"

#ifdef _MSC_VER
#pragma warning(push, 1)
#endif

#include <boost/shared_ptr.hpp>

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#include "libtorrent/peer_connection.hpp"
#include "libtorrent/bt_peer_connection.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/torrent.hpp"
#include "libtorrent/extensions.hpp"

#include "libtorrent/extensions/ut_pex.hpp"

namespace libtorrent { namespace
{
	const char extension_name[] = "ut_pex";

	enum
	{
		extension_index = 1,
		max_peer_entries = 100
	};

	bool send_peer(peer_connection const& p)
	{
		// don't send out peers that we haven't connected to
		// (that have connected to us)
		if (!p.is_local()) return false;
		// don't send out peers that we haven't successfully connected to
		if (p.is_connecting()) return false;
		return true;
	}

	struct ut_pex_plugin: torrent_plugin
	{
		ut_pex_plugin(torrent& t): m_torrent(t), m_1_minute(55) {}

		virtual boost::shared_ptr<peer_plugin> new_connection(peer_connection* pc);

		std::vector<char>& get_ut_pex_msg()
		{
			return m_ut_pex_msg;
		}

		// the second tick of the torrent
		// each minute the new lists of "added" + "added.f" and "dropped"
		// are calculated here and the pex message is created
		// each peer connection will use this message
		// max_peer_entries limits the packet size
		virtual void tick()
		{
			if (++m_1_minute < 60) return;

			m_1_minute = 0;

			entry pex;
			std::string& pla = pex["added"].string();
			std::string& pld = pex["dropped"].string();
			std::string& plf = pex["added.f"].string();
			std::string& pla6 = pex["added6"].string();
			std::string& pld6 = pex["dropped6"].string();
			std::string& plf6 = pex["added6.f"].string();
			std::back_insert_iterator<std::string> pla_out(pla);
			std::back_insert_iterator<std::string> pld_out(pld);
			std::back_insert_iterator<std::string> plf_out(plf);
			std::back_insert_iterator<std::string> pla6_out(pla6);
			std::back_insert_iterator<std::string> pld6_out(pld6);
			std::back_insert_iterator<std::string> plf6_out(plf6);

			std::set<tcp::endpoint> dropped;
			m_old_peers.swap(dropped);

			int num_added = 0;
			for (torrent::peer_iterator i = m_torrent.begin()
				, end(m_torrent.end()); i != end; ++i)
			{
				peer_connection* peer = *i;
				if (!send_peer(*peer)) continue;

				tcp::endpoint const& remote = peer->remote();
				m_old_peers.insert(remote);

				std::set<tcp::endpoint>::iterator di = dropped.find(remote);
				if (di == dropped.end())
				{
					// don't write too big of a package
					if (num_added >= max_peer_entries) break;

					// only send proper bittorrent peers
					bt_peer_connection* p = dynamic_cast<bt_peer_connection*>(peer);
					if (!p) continue;

					// no supported flags to set yet
					// 0x01 - peer supports encryption
					// 0x02 - peer is a seed
					int flags = p->is_seed() ? 2 : 0;
#ifndef TORRENT_DISABLE_ENCRYPTION
					flags |= p->supports_encryption() ? 1 : 0;
#endif
					// i->first was added since the last time
					if (remote.address().is_v4())
					{
						detail::write_endpoint(remote, pla_out);
						detail::write_uint8(flags, plf_out);
					}
					else
					{
						detail::write_endpoint(remote, pla6_out);
						detail::write_uint8(flags, plf6_out);
					}
					++num_added;
				}
				else
				{
					// this was in the previous message
					// so, it wasn't dropped
					dropped.erase(di);
				}
			}

			for (std::set<tcp::endpoint>::const_iterator i = dropped.begin()
				, end(dropped.end()); i != end; ++i)
			{
				if (i->address().is_v4())
					detail::write_endpoint(*i, pld_out);
				else
					detail::write_endpoint(*i, pld6_out);
			}

			m_ut_pex_msg.clear();
			bencode(std::back_inserter(m_ut_pex_msg), pex);
		}

	private:
		torrent& m_torrent;

		std::set<tcp::endpoint> m_old_peers;
		int m_1_minute;
		std::vector<char> m_ut_pex_msg;
	};

	struct ut_pex_peer_plugin : peer_plugin
	{
		ut_pex_peer_plugin(torrent& t, peer_connection& pc, ut_pex_plugin& tp)
			: m_torrent(t)
			, m_pc(pc)
			, m_tp(tp)
			, m_1_minute(55)
			, m_message_index(0)
			, m_first_time(true)
		{}

		virtual void add_handshake(entry& h)
		{
			entry& messages = h["m"];
			messages[extension_name] = extension_index;
		}

		virtual bool on_extension_handshake(entry const& h)
		{
			entry const& messages = h["m"];

			if (entry const* index = messages.find_key(extension_name))
			{
				m_message_index = index->integer();
				return true;
			}
			else
			{
				m_message_index = 0;
				return false;
			}
		}

		virtual bool on_extended(int length, int msg, buffer::const_interval body)
		{
			if (msg != extension_index) return false;
			if (m_message_index == 0) return false;

			if (length > 500 * 1024)
				throw protocol_error("uT peer exchange message larger than 500 kB");

			if (body.left() < length) return true;

			try
			{
				entry pex_msg = bdecode(body.begin, body.end);
				std::string const& peers = pex_msg["added"].string();
				std::string const& peer_flags = pex_msg["added.f"].string();

				int num_peers = peers.length() / 6;
				char const* in = peers.c_str();
				char const* fin = peer_flags.c_str();

				if (int(peer_flags.size()) != num_peers)
					return true;

				peer_id pid(0);
				policy& p = m_torrent.get_policy();
				for (int i = 0; i < num_peers; ++i)
				{
					tcp::endpoint adr = detail::read_v4_endpoint<tcp::endpoint>(in);
					char flags = detail::read_uint8(fin);
					p.peer_from_tracker(adr, pid, peer_info::pex, flags);
				}

				if (entry const* p6 = pex_msg.find_key("added6"))
				{
					std::string const& peers6 = p6->string();
					std::string const& peer6_flags = pex_msg["added6.f"].string();

					int num_peers = peers6.length() / 18;
					char const* in = peers6.c_str();
					char const* fin = peer6_flags.c_str();

					if (int(peer6_flags.size()) != num_peers)
						return true;

					peer_id pid(0);
					policy& p = m_torrent.get_policy();
					for (int i = 0; i < num_peers; ++i)
					{
						tcp::endpoint adr = detail::read_v6_endpoint<tcp::endpoint>(in);
						char flags = detail::read_uint8(fin);
						p.peer_from_tracker(adr, pid, peer_info::pex, flags);
					}
				}
			}
			catch (std::exception&)
			{
				throw protocol_error("invalid uT peer exchange message");
			}
			return true;
		}

		// the peers second tick
		// every minute we send a pex message
		virtual void tick()
		{
			if (!m_message_index) return;	// no handshake yet
			if (++m_1_minute <= 60) return;

			if (m_first_time)
			{
				send_ut_peer_list();
				m_first_time = false;
			}
			else
			{
				send_ut_peer_diff();
			}
			m_1_minute = 0;
		}

	private:

		void send_ut_peer_diff()
		{
			std::vector<char> const& pex_msg = m_tp.get_ut_pex_msg();

			buffer::interval i = m_pc.allocate_send_buffer(6 + pex_msg.size());

			detail::write_uint32(1 + 1 + pex_msg.size(), i.begin);
			detail::write_uint8(bt_peer_connection::msg_extended, i.begin);
			detail::write_uint8(m_message_index, i.begin);
			std::copy(pex_msg.begin(), pex_msg.end(), i.begin);
			i.begin += pex_msg.size();

			TORRENT_ASSERT(i.begin == i.end);
			m_pc.setup_send();
		}

		void send_ut_peer_list()
		{
			entry pex;
			// leave the dropped string empty
			pex["dropped"].string();
			std::string& pla = pex["added"].string();
			std::string& plf = pex["added.f"].string();
			pex["dropped6"].string();
			std::string& pla6 = pex["added6"].string();
			std::string& plf6 = pex["added6.f"].string();
			std::back_insert_iterator<std::string> pla_out(pla);
			std::back_insert_iterator<std::string> plf_out(plf);
			std::back_insert_iterator<std::string> pla6_out(pla6);
			std::back_insert_iterator<std::string> plf6_out(plf6);

			int num_added = 0;
			for (torrent::peer_iterator i = m_torrent.begin()
				, end(m_torrent.end()); i != end; ++i)
			{
				peer_connection* peer = *i;
				if (!send_peer(*peer)) continue;

				// don't write too big of a package
				if (num_added >= max_peer_entries) break;

				// only send proper bittorrent peers
				bt_peer_connection* p = dynamic_cast<bt_peer_connection*>(peer);
				if (!p) continue;

				// no supported flags to set yet
				// 0x01 - peer supports encryption
				// 0x02 - peer is a seed
				int flags = p->is_seed() ? 2 : 0;
#ifndef TORRENT_DISABLE_ENCRYPTION
				flags |= p->supports_encryption() ? 1 : 0;
#endif
				tcp::endpoint const& remote = peer->remote();
				// i->first was added since the last time
				if (remote.address().is_v4())
				{
					detail::write_endpoint(remote, pla_out);
					detail::write_uint8(flags, plf_out);
				}
				else
				{
					detail::write_endpoint(remote, pla6_out);
					detail::write_uint8(flags, plf6_out);
				}
				++num_added;
			}
			std::vector<char> pex_msg;
			bencode(std::back_inserter(pex_msg), pex);

			buffer::interval i = m_pc.allocate_send_buffer(6 + pex_msg.size());

			detail::write_uint32(1 + 1 + pex_msg.size(), i.begin);
			detail::write_uint8(bt_peer_connection::msg_extended, i.begin);
			detail::write_uint8(m_message_index, i.begin);
			std::copy(pex_msg.begin(), pex_msg.end(), i.begin);
			i.begin += pex_msg.size();

			TORRENT_ASSERT(i.begin == i.end);
			m_pc.setup_send();
		}

		torrent& m_torrent;
		peer_connection& m_pc;
		ut_pex_plugin& m_tp;
		int m_1_minute;
		int m_message_index;

		// this is initialized to true, and set to
		// false after the first pex message has been sent.
		// it is used to know if a diff message or a full
		// message should be sent.
		bool m_first_time;
	};

	boost::shared_ptr<peer_plugin> ut_pex_plugin::new_connection(peer_connection* pc)
	{
		bt_peer_connection* c = dynamic_cast<bt_peer_connection*>(pc);
		if (!c) return boost::shared_ptr<peer_plugin>();
		return boost::shared_ptr<peer_plugin>(new ut_pex_peer_plugin(m_torrent
			, *pc, *this));
	}
}}

namespace libtorrent
{

	boost::shared_ptr<torrent_plugin> create_ut_pex_plugin(torrent* t, void*)
	{
		if (t->torrent_file().priv())
		{
			return boost::shared_ptr<torrent_plugin>();
		}
		return boost::shared_ptr<torrent_plugin>(new ut_pex_plugin(*t));
	}

}

<commit_msg>made ut_pex not rely on exceptions<commit_after>/*

Copyright (c) 2006, MassaRoddel, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/pch.hpp"

#ifdef _MSC_VER
#pragma warning(push, 1)
#endif

#include <boost/shared_ptr.hpp>

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#include "libtorrent/peer_connection.hpp"
#include "libtorrent/bt_peer_connection.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/torrent.hpp"
#include "libtorrent/extensions.hpp"

#include "libtorrent/extensions/ut_pex.hpp"

namespace libtorrent { namespace
{
	const char extension_name[] = "ut_pex";

	enum
	{
		extension_index = 1,
		max_peer_entries = 100
	};

	bool send_peer(peer_connection const& p)
	{
		// don't send out peers that we haven't connected to
		// (that have connected to us)
		if (!p.is_local()) return false;
		// don't send out peers that we haven't successfully connected to
		if (p.is_connecting()) return false;
		return true;
	}

	struct ut_pex_plugin: torrent_plugin
	{
		ut_pex_plugin(torrent& t): m_torrent(t), m_1_minute(55) {}

		virtual boost::shared_ptr<peer_plugin> new_connection(peer_connection* pc);

		std::vector<char>& get_ut_pex_msg()
		{
			return m_ut_pex_msg;
		}

		// the second tick of the torrent
		// each minute the new lists of "added" + "added.f" and "dropped"
		// are calculated here and the pex message is created
		// each peer connection will use this message
		// max_peer_entries limits the packet size
		virtual void tick()
		{
			if (++m_1_minute < 60) return;

			m_1_minute = 0;

			entry pex;
			std::string& pla = pex["added"].string();
			std::string& pld = pex["dropped"].string();
			std::string& plf = pex["added.f"].string();
			std::string& pla6 = pex["added6"].string();
			std::string& pld6 = pex["dropped6"].string();
			std::string& plf6 = pex["added6.f"].string();
			std::back_insert_iterator<std::string> pla_out(pla);
			std::back_insert_iterator<std::string> pld_out(pld);
			std::back_insert_iterator<std::string> plf_out(plf);
			std::back_insert_iterator<std::string> pla6_out(pla6);
			std::back_insert_iterator<std::string> pld6_out(pld6);
			std::back_insert_iterator<std::string> plf6_out(plf6);

			std::set<tcp::endpoint> dropped;
			m_old_peers.swap(dropped);

			int num_added = 0;
			for (torrent::peer_iterator i = m_torrent.begin()
				, end(m_torrent.end()); i != end; ++i)
			{
					peer_connection* peer = *i;
				if (!send_peer(*peer)) continue;

				tcp::endpoint const& remote = peer->remote();
				m_old_peers.insert(remote);

				std::set<tcp::endpoint>::iterator di = dropped.find(remote);
				if (di == dropped.end())
				{
					// don't write too big of a package
					if (num_added >= max_peer_entries) break;

					// only send proper bittorrent peers
					bt_peer_connection* p = dynamic_cast<bt_peer_connection*>(peer);
					if (!p) continue;

					// no supported flags to set yet
					// 0x01 - peer supports encryption
					// 0x02 - peer is a seed
					int flags = p->is_seed() ? 2 : 0;
#ifndef TORRENT_DISABLE_ENCRYPTION
					flags |= p->supports_encryption() ? 1 : 0;
#endif
					// i->first was added since the last time
					if (remote.address().is_v4())
					{
						detail::write_endpoint(remote, pla_out);
						detail::write_uint8(flags, plf_out);
					}
					else
					{
						detail::write_endpoint(remote, pla6_out);
						detail::write_uint8(flags, plf6_out);
					}
					++num_added;
				}
				else
				{
					// this was in the previous message
					// so, it wasn't dropped
					dropped.erase(di);
				}
			}

			for (std::set<tcp::endpoint>::const_iterator i = dropped.begin()
				, end(dropped.end()); i != end; ++i)
			{
				if (i->address().is_v4())
					detail::write_endpoint(*i, pld_out);
				else
					detail::write_endpoint(*i, pld6_out);
			}

			m_ut_pex_msg.clear();
			bencode(std::back_inserter(m_ut_pex_msg), pex);
		}

	private:
		torrent& m_torrent;

		std::set<tcp::endpoint> m_old_peers;
		int m_1_minute;
		std::vector<char> m_ut_pex_msg;
	};

	struct ut_pex_peer_plugin : peer_plugin
	{
		ut_pex_peer_plugin(torrent& t, peer_connection& pc, ut_pex_plugin& tp)
			: m_torrent(t)
			, m_pc(pc)
			, m_tp(tp)
			, m_1_minute(55)
			, m_message_index(0)
			, m_first_time(true)
		{}

		virtual void add_handshake(entry& h)
		{
			entry& messages = h["m"];
			messages[extension_name] = extension_index;
		}

		virtual bool on_extension_handshake(entry const& h)
		{
			m_message_index = 0;
			entry const* messages = h.find_key("m");
			if (!messages || messages->type() != entry::dictionary_t) return false;

			entry const* index = messages->find_key(extension_name);
			if (!index || index->type() != entry::int_t) return false;

			m_message_index = index->integer();
			return true;
		}

		virtual bool on_extended(int length, int msg, buffer::const_interval body)
		{
			if (msg != extension_index) return false;
			if (m_message_index == 0) return false;

			if (length > 500 * 1024)
				throw protocol_error("uT peer exchange message larger than 500 kB");

			if (body.left() < length) return true;

			entry pex_msg = bdecode(body.begin, body.end);

			entry const* p = pex_msg.find_key("added");
			entry const* pf = pex_msg.find_key("added.f");

			if (p != 0 && pf != 0 && p->type() == entry::string_t && pf->type() == entry::string_t)
			{
				std::string const& peers = p->string();
				std::string const& peer_flags = pf->string();

				int num_peers = peers.length() / 6;
				char const* in = peers.c_str();
				char const* fin = peer_flags.c_str();

				if (int(peer_flags.size()) != num_peers)
					return true;

				peer_id pid(0);
				policy& p = m_torrent.get_policy();
				for (int i = 0; i < num_peers; ++i)
				{
					tcp::endpoint adr = detail::read_v4_endpoint<tcp::endpoint>(in);
					char flags = detail::read_uint8(fin);
					p.peer_from_tracker(adr, pid, peer_info::pex, flags);
				}
			}

			entry const* p6 = pex_msg.find_key("added6");
			entry const* p6f = pex_msg.find_key("added6.f");
			if (p6 && p6f && p6->type() == entry::string_t && p6f->type() == entry::string_t)
			{
				std::string const& peers6 = p6->string();
				std::string const& peer6_flags = p6f->string();

				int num_peers = peers6.length() / 18;
				char const* in = peers6.c_str();
				char const* fin = peer6_flags.c_str();

				if (int(peer6_flags.size()) != num_peers)
					return true;

				peer_id pid(0);
				policy& p = m_torrent.get_policy();
				for (int i = 0; i < num_peers; ++i)
				{
					tcp::endpoint adr = detail::read_v6_endpoint<tcp::endpoint>(in);
					char flags = detail::read_uint8(fin);
					p.peer_from_tracker(adr, pid, peer_info::pex, flags);
				}
			}
			return true;
		}

		// the peers second tick
		// every minute we send a pex message
		virtual void tick()
		{
			if (!m_message_index) return;	// no handshake yet
			if (++m_1_minute <= 60) return;

			if (m_first_time)
			{
				send_ut_peer_list();
				m_first_time = false;
			}
			else
			{
				send_ut_peer_diff();
			}
			m_1_minute = 0;
		}

	private:

		void send_ut_peer_diff()
		{
			std::vector<char> const& pex_msg = m_tp.get_ut_pex_msg();

			buffer::interval i = m_pc.allocate_send_buffer(6 + pex_msg.size());

			detail::write_uint32(1 + 1 + pex_msg.size(), i.begin);
			detail::write_uint8(bt_peer_connection::msg_extended, i.begin);
			detail::write_uint8(m_message_index, i.begin);
			std::copy(pex_msg.begin(), pex_msg.end(), i.begin);
			i.begin += pex_msg.size();

			TORRENT_ASSERT(i.begin == i.end);
			m_pc.setup_send();
		}

		void send_ut_peer_list()
		{
			entry pex;
			// leave the dropped string empty
			pex["dropped"].string();
			std::string& pla = pex["added"].string();
			std::string& plf = pex["added.f"].string();
			pex["dropped6"].string();
			std::string& pla6 = pex["added6"].string();
			std::string& plf6 = pex["added6.f"].string();
			std::back_insert_iterator<std::string> pla_out(pla);
			std::back_insert_iterator<std::string> plf_out(plf);
			std::back_insert_iterator<std::string> pla6_out(pla6);
			std::back_insert_iterator<std::string> plf6_out(plf6);

			int num_added = 0;
			for (torrent::peer_iterator i = m_torrent.begin()
				, end(m_torrent.end()); i != end; ++i)
			{
				peer_connection* peer = *i;
				if (!send_peer(*peer)) continue;

				// don't write too big of a package
				if (num_added >= max_peer_entries) break;

				// only send proper bittorrent peers
				bt_peer_connection* p = dynamic_cast<bt_peer_connection*>(peer);
				if (!p) continue;

				// no supported flags to set yet
				// 0x01 - peer supports encryption
				// 0x02 - peer is a seed
				int flags = p->is_seed() ? 2 : 0;
#ifndef TORRENT_DISABLE_ENCRYPTION
				flags |= p->supports_encryption() ? 1 : 0;
#endif
				tcp::endpoint const& remote = peer->remote();
				// i->first was added since the last time
				if (remote.address().is_v4())
				{
					detail::write_endpoint(remote, pla_out);
					detail::write_uint8(flags, plf_out);
				}
				else
				{
					detail::write_endpoint(remote, pla6_out);
					detail::write_uint8(flags, plf6_out);
				}
				++num_added;
			}
			std::vector<char> pex_msg;
			bencode(std::back_inserter(pex_msg), pex);

			buffer::interval i = m_pc.allocate_send_buffer(6 + pex_msg.size());

			detail::write_uint32(1 + 1 + pex_msg.size(), i.begin);
			detail::write_uint8(bt_peer_connection::msg_extended, i.begin);
			detail::write_uint8(m_message_index, i.begin);
			std::copy(pex_msg.begin(), pex_msg.end(), i.begin);
			i.begin += pex_msg.size();

			TORRENT_ASSERT(i.begin == i.end);
			m_pc.setup_send();
		}

		torrent& m_torrent;
		peer_connection& m_pc;
		ut_pex_plugin& m_tp;
		int m_1_minute;
		int m_message_index;

		// this is initialized to true, and set to
		// false after the first pex message has been sent.
		// it is used to know if a diff message or a full
		// message should be sent.
		bool m_first_time;
	};

	boost::shared_ptr<peer_plugin> ut_pex_plugin::new_connection(peer_connection* pc)
	{
		bt_peer_connection* c = dynamic_cast<bt_peer_connection*>(pc);
		if (!c) return boost::shared_ptr<peer_plugin>();
		return boost::shared_ptr<peer_plugin>(new ut_pex_peer_plugin(m_torrent
			, *pc, *this));
	}
}}

namespace libtorrent
{

	boost::shared_ptr<torrent_plugin> create_ut_pex_plugin(torrent* t, void*)
	{
		if (t->torrent_file().priv())
		{
			return boost::shared_ptr<torrent_plugin>();
		}
		return boost::shared_ptr<torrent_plugin>(new ut_pex_plugin(*t));
	}

}

<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: anyperformance.cxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-08 01:53:39 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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
 *
 ************************************************************************/

#include <cstdlib>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
#include <iostream>
#include <iterator>

#include <com/sun/star/uno/Type.hxx>
#include <com/sun/star/uno/Any.hxx>
#include <com/sun/star/uno/Sequence.hxx>

using namespace ::std;
using namespace ::com::sun::star;

#define DBG_OUTPUT

typedef double testType;
// typedef sal_Int32 testType;

struct lcl_Data
{
    int m_nSeries;
    int m_nPoints;
    enum Type
    {
        TYPE_DOUBLE,
        TYPE_ANY
    } m_eType;

    lcl_Data() :
            m_nSeries( 10 ),
            m_nPoints( 10000 ),
            m_eType( TYPE_DOUBLE )
    {}

    lcl_Data( int nSeries, int nPoints, Type eType ) :
            m_nSeries( nSeries ),
            m_nPoints( nPoints ),
            m_eType( eType )
    {}
};

lcl_Data lcl_parseArgs( const vector< string > & aArgs )
{
    lcl_Data aResult;

    if( aArgs.size() > 1 )
    {
        aResult.m_eType = ( aArgs[ 1 ] == string( "any" ))
            ? lcl_Data::TYPE_ANY
            : lcl_Data::TYPE_DOUBLE;
    }
    else
    {
        cerr << "Usage: " << aArgs[ 0 ] << " double|any [series] [points-per-series]" << endl;
        exit( EXIT_FAILURE );
    }

    if( aArgs.size() > 2 )
    {
        stringstream aStrStream;
        aStrStream << aArgs[ 2 ];
        aStrStream >> aResult.m_nSeries;
    }
    if( aArgs.size() > 3 )
    {
        stringstream aStrStream;
        aStrStream << aArgs[ 3 ];
        aStrStream >> aResult.m_nPoints;
    }

    return aResult;
}

template< typename T >
    struct lcl_MakeAny : public unary_function< uno::Any, T >
    {
        uno::Any operator() ( const T & rVal )
        {
            return uno::makeAny( rVal );
        }
    };

// ========================================
//                   MAIN
// ========================================

int SAL_CALL main( int argc, char* argv[] )
{
    // parse arguments
    // arg0: number of series
    // arg1: number of points per series
    vector< string > aArgs( argc );
    copy( argv, argv + argc, aArgs.begin() );

    lcl_Data aParams( lcl_parseArgs( aArgs ));

    // create data
    vector< testType > aSeries( aParams.m_nPoints, 23 );
    vector< vector< testType > > aData( aParams.m_nSeries, aSeries );

    switch( aParams.m_eType )
    {
        case lcl_Data::TYPE_DOUBLE:
#ifdef DBG_OUTPUT
            cout << "Testing native type: " << endl;
#endif
            {
                uno::Sequence< uno::Sequence< testType > > aSeq;
                aSeq.realloc( aParams.m_nSeries );
                uno::Sequence< testType > * pArr = aSeq.getArray();

                for( vector< vector< testType > >::const_iterator aIter = aData.begin();
                     aIter != aData.end();
                     ++aIter, ++pArr )
                {
                    pArr->realloc( aParams.m_nPoints );
                    copy( (*aIter).begin(), (*aIter).end(),
                          pArr->getArray() );
                }
            }
            break;
        case lcl_Data::TYPE_ANY:
#ifdef DBG_OUTPUT
            cout << "Testing Anies" << endl;
#endif
            {
                uno::Sequence< uno::Sequence< uno::Any > > aSeq;
                aSeq.realloc( aParams.m_nSeries );
                uno::Sequence< uno::Any > * pArr = aSeq.getArray();

                for( vector< vector< testType > >::const_iterator aIter = aData.begin();
                     aIter != aData.end();
                     ++aIter, ++pArr )
                {
                    pArr->realloc( aParams.m_nPoints );
                    transform( (*aIter).begin(), (*aIter).end(),
                               pArr->getArray(), lcl_MakeAny< testType >() );
                }
            }
            break;
    }

#if 0
    // output data
    for( vector< vector< testType > >::const_iterator aIter = aData.begin();
         aIter != aData.end();
         ++aIter)
    {
        cout << "Series: * ";
        copy( (*aIter).begin(), (*aIter).end(),
              ostream_iterator< testType >( cout, " * " ));
        cout << endl;
    }
#endif

    return EXIT_SUCCESS;
}
<commit_msg>INTEGRATION: CWS pchfix02 (1.2.82); FILE MERGED 2006/09/01 17:19:03 kaib 1.2.82.1: #i68856# Added header markers and pch files<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: anyperformance.cxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: obo $ $Date: 2006-09-17 13:39:51 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_chart2.hxx"

#include <cstdlib>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
#include <iostream>
#include <iterator>

#include <com/sun/star/uno/Type.hxx>
#include <com/sun/star/uno/Any.hxx>
#include <com/sun/star/uno/Sequence.hxx>

using namespace ::std;
using namespace ::com::sun::star;

#define DBG_OUTPUT

typedef double testType;
// typedef sal_Int32 testType;

struct lcl_Data
{
    int m_nSeries;
    int m_nPoints;
    enum Type
    {
        TYPE_DOUBLE,
        TYPE_ANY
    } m_eType;

    lcl_Data() :
            m_nSeries( 10 ),
            m_nPoints( 10000 ),
            m_eType( TYPE_DOUBLE )
    {}

    lcl_Data( int nSeries, int nPoints, Type eType ) :
            m_nSeries( nSeries ),
            m_nPoints( nPoints ),
            m_eType( eType )
    {}
};

lcl_Data lcl_parseArgs( const vector< string > & aArgs )
{
    lcl_Data aResult;

    if( aArgs.size() > 1 )
    {
        aResult.m_eType = ( aArgs[ 1 ] == string( "any" ))
            ? lcl_Data::TYPE_ANY
            : lcl_Data::TYPE_DOUBLE;
    }
    else
    {
        cerr << "Usage: " << aArgs[ 0 ] << " double|any [series] [points-per-series]" << endl;
        exit( EXIT_FAILURE );
    }

    if( aArgs.size() > 2 )
    {
        stringstream aStrStream;
        aStrStream << aArgs[ 2 ];
        aStrStream >> aResult.m_nSeries;
    }
    if( aArgs.size() > 3 )
    {
        stringstream aStrStream;
        aStrStream << aArgs[ 3 ];
        aStrStream >> aResult.m_nPoints;
    }

    return aResult;
}

template< typename T >
    struct lcl_MakeAny : public unary_function< uno::Any, T >
    {
        uno::Any operator() ( const T & rVal )
        {
            return uno::makeAny( rVal );
        }
    };

// ========================================
//                   MAIN
// ========================================

int SAL_CALL main( int argc, char* argv[] )
{
    // parse arguments
    // arg0: number of series
    // arg1: number of points per series
    vector< string > aArgs( argc );
    copy( argv, argv + argc, aArgs.begin() );

    lcl_Data aParams( lcl_parseArgs( aArgs ));

    // create data
    vector< testType > aSeries( aParams.m_nPoints, 23 );
    vector< vector< testType > > aData( aParams.m_nSeries, aSeries );

    switch( aParams.m_eType )
    {
        case lcl_Data::TYPE_DOUBLE:
#ifdef DBG_OUTPUT
            cout << "Testing native type: " << endl;
#endif
            {
                uno::Sequence< uno::Sequence< testType > > aSeq;
                aSeq.realloc( aParams.m_nSeries );
                uno::Sequence< testType > * pArr = aSeq.getArray();

                for( vector< vector< testType > >::const_iterator aIter = aData.begin();
                     aIter != aData.end();
                     ++aIter, ++pArr )
                {
                    pArr->realloc( aParams.m_nPoints );
                    copy( (*aIter).begin(), (*aIter).end(),
                          pArr->getArray() );
                }
            }
            break;
        case lcl_Data::TYPE_ANY:
#ifdef DBG_OUTPUT
            cout << "Testing Anies" << endl;
#endif
            {
                uno::Sequence< uno::Sequence< uno::Any > > aSeq;
                aSeq.realloc( aParams.m_nSeries );
                uno::Sequence< uno::Any > * pArr = aSeq.getArray();

                for( vector< vector< testType > >::const_iterator aIter = aData.begin();
                     aIter != aData.end();
                     ++aIter, ++pArr )
                {
                    pArr->realloc( aParams.m_nPoints );
                    transform( (*aIter).begin(), (*aIter).end(),
                               pArr->getArray(), lcl_MakeAny< testType >() );
                }
            }
            break;
    }

#if 0
    // output data
    for( vector< vector< testType > >::const_iterator aIter = aData.begin();
         aIter != aData.end();
         ++aIter)
    {
        cout << "Series: * ";
        copy( (*aIter).begin(), (*aIter).end(),
              ostream_iterator< testType >( cout, " * " ));
        cout << endl;
    }
#endif

    return EXIT_SUCCESS;
}
<|endoftext|>