<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();
    }

    int TabbedArea::getNumberOfTabs() const
    {
        return mTabs.size();
    }

    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) const
    {
        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() const
    {
        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(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();
        mDimension = dim;
        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>Removed unnecessary variable<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();
    }

    int TabbedArea::getNumberOfTabs() const
    {
        return mTabs.size();
    }

    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) const
    {
        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() const
    {
        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(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();
        mDimension = dim;
        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)
    {
        Tab* tab = dynamic_cast<Tab*>(event.getSource());

        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>/*
Copyright (c) 2013 Stijn "tcpie" Hinterding (contact: contact at tcpie dot eu)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include "stdafx.h"

#include "..\WinCore\Function.h"
#include "..\WinCore\Thread.h"
#include "..\WinCore\Process.h"
#include "..\WinCore\MemoryRegion.h"
#include "..\WinCore\Hook.h"
#include "..\WinCore\NotifyHook.h"

#include <vector>
#include <iostream>
#include <string>

#pragma comment(lib, "..\\Release\\WinCore.lib")

using namespace tcpie::wincore;
using namespace std;

float __cdecl multiply(float a, int b)
{
	float ret = (float)b * a;
	float* ret_addr = &ret;
	return ret;
}

int __cdecl add(int a, float b, int* ret_buffer)
{
	int ret = a + (int)b;

	*ret_buffer = ret;

	return ret;
}

DetourRet __stdcall add_change_arg(DetourArgs* args)
{
	args->Arguments->at(0) = (void*)4;

	return DETOUR_ARGCHANGED;
}

DetourRet __stdcall add_change_ret(DetourArgs* args)
{
	args->CustomReturnValue = 500;

	return DETOUR_RETCHANGED;
}

DetourRet __stdcall add_block_fn(DetourArgs* args)
{
	args->CustomReturnValue = 600;

	return DETOUR_FNBLOCKED;
}

class detour_class : public tcpie::wincore::IDetourClass
{
public:
	virtual DetourRet DetourCallback(DetourArgs* Arguments) override
	{
		cout << "\tClass detour working? 1" << endl;

		return DETOUR_NOCHANGE;
	}
};

bool hook_test()
{
	cout << "Hook test:" << endl;
	vector<BYTE>* signature = new std::vector<BYTE>((BYTE*)add, (BYTE*)add_change_arg);
	vector<char>* mask = new std::vector<char>(signature->size(), 'x');

	Function* target = Function::FindFunction(signature,
		mask,
		CDECL_CALLCONV,			// Calling convention
		3,						// Number of arguments
		DWORD_SIZE);			// Return type

	cout << "\tFindFunction() succeeded? " << (bool)(target != NULL) << endl;

	Hook* cdecl_hook = Hook::CreateHook(target, new wstring(L"add"));

	cout << "\tCreateHook() succeeded? " << (bool)(cdecl_hook != NULL) << endl;

	Detour* cdecl_change_arg_detour = cdecl_hook->RegisterDetour(add_change_arg, DETOUR_PRE);

	cout << "\tRegisterDetour(fn) succeeded? " << (bool)(cdecl_change_arg_detour != NULL) << endl;

	Detour* cdecl_class_detour = cdecl_hook->RegisterDetour(new detour_class(), DETOUR_PRE);

	cout << "\tRegisterDetour(class) succeeded? " << (bool)(cdecl_class_detour != NULL) << endl;

	Detour* cdecl_change_ret_detour = cdecl_hook->RegisterDetour(add_change_ret, DETOUR_POST);

	Detour* cdecl_blockfn = cdecl_hook->RegisterDetour(add_block_fn, DETOUR_PRE);

	cdecl_blockfn->Disable();
	cdecl_hook->Enable();
	cdecl_change_ret_detour->Disable();

	int ret_buffer = 0;
	int ret = add(2, 5, &ret_buffer);

	cdecl_class_detour->Disable();

	cout << "\tChange arg succeeded? " << (bool)(ret == 9) << endl;
	cdecl_change_ret_detour->Enable();
	cdecl_change_arg_detour->Disable();

	ret = add(2, 5, &ret_buffer);

	cout << "\tChange ret succeeded? " << (bool)(ret == 500) << endl;
	cdecl_change_ret_detour->Disable();

	ret_buffer = 0;
	cdecl_blockfn->Enable();
	ret = add(2, 5, &ret_buffer);

	cout << "\tBlock function succeeded? " << (bool)(ret_buffer == 0 && ret == 600) << endl;

	cout << "Hook test done. " << endl;

	return true;
}

__declspec(noinline) int printLOL(int val)
{
	return val - 2;
}

void __stdcall notify_callback(void* fn_addr)
{
	cout << "\tNotify callback called? 1    (fn 0x" << hex << fn_addr << dec << ")" << endl;
}

class notify_callback_class : public tcpie::wincore::INotifyDetourClass
{
public:
	notify_callback_class()
	{

	}

	virtual void NotifyDetourCallback(void* HookedFunctionAddress) override
	{
		cout << "\tNotify class callback called? 1    (fn 0x" << hex << HookedFunctionAddress << dec << ")" << endl;
	}
};

bool notifyhook_test()
{
	cout << "NotifyHook test:" << endl;

	NotifyHook* n_hook = NotifyHook::CreateHook(&printLOL, new wstring(L"printLOL"));

	cout << "\tCreateHook succeeded? " << (bool)(n_hook != NULL) << endl;

	NotifyDetour* n_detour1 = n_hook->RegisterDetour(notify_callback);

	cout << "\tRegisterDetour(fn) succeeded? " << (bool)(n_detour1 != NULL) << endl;

	NotifyDetour* n_detour2 = n_hook->RegisterDetour(new notify_callback_class());

	cout << "\tRegisterDetour(class) succeeded? " << (bool)(n_detour2 != NULL) << endl;

	n_hook->Enable();

	int ret = printLOL(10);

	return true;
}

int _tmain(int argc, _TCHAR* argv[])
{
	hook_test();

	Function mult = Function(multiply,
									CDECL_CALLCONV,
									2,
									DWORD_SIZE);

	vector<void*> args = vector<void*>();
	float first = 3.0f;
	args.push_back((void*)5);
	args.push_back(*(void**)&first);

	DWORD ret = 0;
	DWORD* ret_addr = &ret;
	mult.Call(&args,
			  &ret);

	cout << hex << ret << dec << endl;

	notifyhook_test();

	MessageBoxA(NULL, "", "wait", MB_OK);

	return 0;

}

<commit_msg>Expanded the unittests to also test STDCALL and THISCALL functions. Known bug at this moment: Module's MemoryRegion gets corrupted by... something.<commit_after>/*
Copyright (c) 2013 Stijn "tcpie" Hinterding (contact: contact at tcpie dot eu)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include "stdafx.h"

#include "..\WinCore\Function.h"
#include "..\WinCore\Thread.h"
#include "..\WinCore\Process.h"
#include "..\WinCore\MemoryRegion.h"
#include "..\WinCore\Hook.h"
#include "..\WinCore\NotifyHook.h"

#include <vector>
#include <iostream>
#include <string>

#pragma comment(lib, "..\\Release\\WinCore.lib")

using namespace tcpie::wincore;
using namespace std;

float __cdecl multiply(float a, int b)
{
	float ret = (float)b * a;
	float* ret_addr = &ret;
	return ret;
}

class test_class
{
public:
	int add(int a, float b, int* ret_buffer)
	{
		int ret = a + (int)b;

		*ret_buffer = ret;

		return ret;
	}

	int add_end(int a, float b, int* ret_buffer)
	{
		return 0;
	}
};

int __cdecl add(int a, float b, int* ret_buffer)
{
	int ret = a + (int)b;

	*ret_buffer = ret;

	return ret;
}

void add_end() { }

int __stdcall add_stdcall(int a, float b, int* ret_buffer)
{
	int ret = a + (int)b;

	*ret_buffer = ret;

	return ret;
}

void add_stdcall_end() { }

DetourRet __stdcall add_change_arg(DetourArgs* args)
{
	args->Arguments->at(0) = (void*)4;

	return DETOUR_ARGCHANGED;
}

DetourRet __stdcall add_change_ret(DetourArgs* args)
{
	args->CustomReturnValue = 500;

	return DETOUR_RETCHANGED;
}

DetourRet __stdcall add_block_fn(DetourArgs* args)
{
	args->CustomReturnValue = 600;

	return DETOUR_FNBLOCKED;
}

class detour_class : public tcpie::wincore::IDetourClass
{
public:
	virtual DetourRet DetourCallback(DetourArgs* Arguments) override
	{
		cout << "\tClass detour working? 1" << endl;

		return DETOUR_NOCHANGE;
	}
};

typedef int (test_class::*test_memfnptr)(int, float, int*);

bool hook_test()
{
	cout << "Hook test:" << endl;

	for (int i = CDECL_CALLCONV; i < 3; i ++)
	{
		void* fn = NULL;
		void* fn_end = NULL;
		test_class instance;

		if (i == CDECL_CALLCONV)
		{
			cout << "\t--- CDECL" << endl;
			fn = (void*)&add;
			fn_end = (void*)&add_end;
		}
		else if (i == STDCALL_CALLCONV)
		{
			cout << "\t--- STDCALL" << endl;
			fn = (void*)&add_stdcall;
			fn_end = (void*)&add_stdcall_end;
		}
		else if (i == THISCALL_CALLCONV)
		{
			cout << "\t--- THISCALL" << endl;
			test_memfnptr fn_ptr = &test_class::add;
			test_memfnptr fn_ptr_end = &test_class::add_end;

			__asm
			{
				push eax;
				mov eax, fn_ptr;
				mov fn, eax;
				pop eax;
			}

			__asm
			{
				push eax;
				mov eax, fn_ptr_end;
				mov fn_end, eax;
				pop eax;
			}
		}

		vector<BYTE>* signature = new std::vector<BYTE>((BYTE*)fn, (BYTE*)fn_end);
		vector<char>* mask = new std::vector<char>(signature->size(), 'x');

		Function* target = Function::FindFunction(signature,
			mask,
			(CallingConvention)i,	// Calling convention
			3,						// Number of arguments
			DWORD_SIZE);			// Return type

		cout << "\tFindFunction() succeeded? " << (bool)(target != NULL) << endl;

		Hook* cdecl_hook = Hook::CreateHook(target, new wstring(L"add"));

		cout << "\tCreateHook() succeeded? " << (bool)(cdecl_hook != NULL) << endl;

		Detour* cdecl_change_arg_detour = cdecl_hook->RegisterDetour(add_change_arg, DETOUR_PRE);

		cout << "\tRegisterDetour(fn) succeeded? " << (bool)(cdecl_change_arg_detour != NULL) << endl;

		Detour* cdecl_class_detour = cdecl_hook->RegisterDetour(new detour_class(), DETOUR_PRE);

		cout << "\tRegisterDetour(class) succeeded? " << (bool)(cdecl_class_detour != NULL) << endl;

		Detour* cdecl_change_ret_detour = cdecl_hook->RegisterDetour(add_change_ret, DETOUR_POST);

		Detour* cdecl_blockfn = cdecl_hook->RegisterDetour(add_block_fn, DETOUR_PRE);

		cdecl_blockfn->Disable();
		cdecl_hook->Enable();
		cdecl_change_ret_detour->Disable();

		int ret_buffer = 0;
		int ret = 0;

		switch (i)
		{
		case CDECL_CALLCONV:
			ret = add(2, 5, &ret_buffer);
			break;

		case STDCALL_CALLCONV:
			ret = add_stdcall(2, 5, &ret_buffer);
			break;

		case THISCALL_CALLCONV:
			ret = instance.add(2, 5, &ret_buffer);
			break;
		}

		cdecl_class_detour->Disable();

		cout << "\tChange arg succeeded? " << (bool)(ret == 9) << endl;
		cdecl_change_ret_detour->Enable();
		cdecl_change_arg_detour->Disable();

		switch (i)
		{
		case CDECL_CALLCONV:
			ret = add(2, 5, &ret_buffer);
			break;

		case STDCALL_CALLCONV:
			ret = add_stdcall(2, 5, &ret_buffer);
			break;

		case THISCALL_CALLCONV:
			ret = instance.add(2, 5, &ret_buffer);
			break;
		}

		cout << "\tChange ret succeeded? " << (bool)(ret == 500) << endl;
		cdecl_change_ret_detour->Disable();

		ret_buffer = 0;
		cdecl_blockfn->Enable();

		switch (i)
		{
		case CDECL_CALLCONV:
			ret = add(2, 5, &ret_buffer);
			break;

		case STDCALL_CALLCONV:
			ret = add_stdcall(2, 5, &ret_buffer);
			break;

		case THISCALL_CALLCONV:
			ret = instance.add(2, 5, &ret_buffer);
			break;
		}

		cout << "\tBlock function succeeded? " << (bool)(ret_buffer == 0 && ret == 600) << endl;
	}

	cout << "Hook test done. " << endl;

	return true;
}

__declspec(noinline) int printLOL(int val)
{
	return val - 2;
}

void __stdcall notify_callback(void* fn_addr)
{
	cout << "\tNotify callback called? 1    (fn 0x" << hex << fn_addr << dec << ")" << endl;
}

class notify_callback_class : public tcpie::wincore::INotifyDetourClass
{
public:
	notify_callback_class()
	{

	}

	virtual void NotifyDetourCallback(void* HookedFunctionAddress) override
	{
		cout << "\tNotify class callback called? 1    (fn 0x" << hex << HookedFunctionAddress << dec << ")" << endl;
	}
};

bool notifyhook_test()
{
	cout << "NotifyHook test:" << endl;

	NotifyHook* n_hook = NotifyHook::CreateHook(&printLOL, new wstring(L"printLOL"));

	cout << "\tCreateHook succeeded? " << (bool)(n_hook != NULL) << endl;

	NotifyDetour* n_detour1 = n_hook->RegisterDetour(notify_callback);

	cout << "\tRegisterDetour(fn) succeeded? " << (bool)(n_detour1 != NULL) << endl;

	NotifyDetour* n_detour2 = n_hook->RegisterDetour(new notify_callback_class());

	cout << "\tRegisterDetour(class) succeeded? " << (bool)(n_detour2 != NULL) << endl;

	n_hook->Enable();

	int ret = printLOL(10);

	return true;
}

int _tmain(int argc, _TCHAR* argv[])
{
	hook_test();

	Function mult = Function(multiply,
									CDECL_CALLCONV,
									2,
									DWORD_SIZE);

	vector<void*> args = vector<void*>();
	float first = 3.0f;
	args.push_back((void*)5);
	args.push_back(*(void**)&first);

	DWORD ret = 0;
	DWORD* ret_addr = &ret;
	mult.Call(&args,
			  &ret);

	cout << hex << ret << dec << endl;

	notifyhook_test();

	MessageBoxA(NULL, "", "wait", MB_OK);

	return 0;

}

<|endoftext|>


<commit_before>#define NOT_CLOSED HIGH
#define HAVE_INPUT HIGH

int winter_prog_mode_1()
{
    if ( digitalRead(PIN_02) ==  NOT_CLOSED) // 检测0.2消防控制是否闭合
    {
      save_alert_msg("0.2消防控制没闭合");  // 存储报警信息
      Serial485.printf("0.2消防控制没闭合"); // 485输出
      shut_down_all(); // 输出全部停机
      return 1;
    }

    int val_PIN_11 = digitalRead(PIN_11);
    int val_PIN_14 = digitalRead(PIN_14);
    int val_PIN_21 = digitalRead(PIN_21);
    int val_PIN_22 = digitalRead(PIN_22);
    int val_PIN_23 = digitalRead(PIN_23);
    int val_PIN_24 = digitalRead(PIN_24);
    int val_PIN_25 = digitalRead(PIN_25);
    int val_PIN_26 = digitalRead(PIN_26);
    int val_PIN_27 = digitalRead(PIN_27);
    int val_PIN_30 = digitalRead(PIN_30);
    int val_PIN_31 = digitalRead(PIN_31);
    int val_PIN_32 = digitalRead(PIN_32);
    int val_PIN_33 = digitalRead(PIN_33);

    if (val_PIN_11==HAVE_INPUT || val_PIN_14==HAVE_INPUT || val_PIN_21==HAVE_INPUT || val_PIN_22==HAVE_INPUT || val_PIN_23==HAVE_INPUT || val_PIN_24==HAVE_INPUT || val_PIN_25==HAVE_INPUT || val_PIN_26==HAVE_INPUT || val_PIN_27==HAVE_INPUT || val_PIN_30==HAVE_INPUT || || val_PIN_31==HAVE_INPUT || || val_PIN_32==HAVE_INPUT || || val_PIN_33==HAVE_INPUT )
    {
      save_alert_msg("有报警信号输入");  // 存储报警信息
      Serial485.printf("有报警信号输入"); // 485输出
      return 0;
    }

    if ( digitalRead(PIN_34) == CLOSED ) // 检测3.4是否闭合
    {
        // 是
        if ( digitalRead(PIN_06) == CLOSED )
        {

        }
        else
        {

        }
    }
    else
    {
        // 否
    }

}
<commit_msg>Update winter_prog_mode_1.cpp<commit_after>#define NOT_CLOSED HIGH //没闭合
#define HAVE_INPUT HIGH  // 有输入
#define START_UP HIGH // 启动
#define SHUT_DOWN LOW // 停止
#define CUT_OFF LOW // 断开

int winter_prog_mode_1()
{
    if ( digitalRead(PIN_02) ==  NOT_CLOSED) // 检测0.2消防控制是否闭合
    {
      save_alert_msg("0.2消防控制没闭合");  // 存储报警信息
      Serial485.printf("0.2消防控制没闭合"); // 485输出
      shut_down_all(); // 输出全部停机
      return 1;
    }

    int val_PIN_i11 = digitalRead(PIN_i11);
    int val_PIN_i14 = digitalRead(PIN_i14);
    int val_PIN_i21 = digitalRead(PIN_i21);
    int val_PIN_i22 = digitalRead(PIN_i22);
    int val_PIN_i23 = digitalRead(PIN_i23);
    int val_PIN_i24 = digitalRead(PIN_i24);
    int val_PIN_i25 = digitalRead(PIN_i25);
    int val_PIN_i26 = digitalRead(PIN_i26);
    int val_PIN_i27 = digitalRead(PIN_i27);
    int val_PIN_i30 = digitalRead(PIN_i30);
    int val_PIN_i31 = digitalRead(PIN_i31);
    int val_PIN_i32 = digitalRead(PIN_i32);
    int val_PIN_i33 = digitalRead(PIN_i33);

    if (val_PIN_11==HAVE_INPUT || val_PIN_14==HAVE_INPUT || val_PIN_21==HAVE_INPUT || val_PIN_22==HAVE_INPUT || val_PIN_23==HAVE_INPUT || val_PIN_24==HAVE_INPUT || val_PIN_25==HAVE_INPUT || val_PIN_26==HAVE_INPUT || val_PIN_27==HAVE_INPUT || val_PIN_30==HAVE_INPUT || || val_PIN_31==HAVE_INPUT || || val_PIN_32==HAVE_INPUT || || val_PIN_33==HAVE_INPUT )
    {
      save_alert_msg("有报警信号输入");  // 存储报警信息
      Serial485.printf("有报警信号输入"); // 485输出
      return 0;
    }

    if ( digitalRead(PIN_i34) == CLOSED ) // 检测i3.4是否闭合
    {
        // i3.4闭合
        if ( digitalRead(PIN_06) == CLOSED )
        {
            delay(3000); // T06延时3秒，启动Q1.7
            digitalWrite(PIN_Q17, START_UP);
            if ( digitalRead(PIN_22) == CLOSED )
            {   //是
                digitalWrite(PIN_Q17, SHUT_DOWN); // 停Q1.7
                save_alert_msg("2.2闭合");  // 存储报警信息
                Serial485.printf("2.2闭合"); // 485输出
                return 0;
            }
            else
            {
                goto tag1;
            }

        }
        else // 0.6没闭合
        {
            delay(3000); // T06延时3s，启动Q2.0
            digitalWrite(PIN_Q20, START_UP);
            if (digitalRead(PIN_26) == CLOSED) // 检测2.6是否闭合
            { //2.6闭合
                /* code */
            }
            else //2.6没闭合
            {
                tag1:if( digitalRead(PIN_i10) ) // i1.0是否闭合
                { // i1.0 闭合
                    delay(3000); //T08延时3s，启动Q2.3
                    digitalWrite(PIN_Q23, START_UP);
                    if (digitalRead(PIN_i24) == CLOSED)//检测i2.4是否闭合
                    {//i2.4闭合
                        //停Q2.3，报警
                        digitalWrite(PIN_Q23, SHUT_DOWN);
                        save_alert_msg("i2.4闭合");  // 存储报警信息
                        Serial485.printf("i2.4闭合"); // 485输出
                        return 0;
                    }
                    else
                    {//i2.4没闭合
                        goto tag2;
                    }
                }
                else
                {// i1.0 没闭合
                    //T08延时3s，启动Q2.4
                    delay(3000);
                    digitalWrite(PIN_Q24, START_UP);
                    if ( digitalRead(PIN_i30)==CLOSED )//检测i3.0是否闭合
                    {//i3.0 闭合
                        // 停Q2.4 报警
                        digitalWrite(PIN_Q24, SHUT_DOWN);
                        save_alert_msg("i3.0闭合");  // 存储报警信息
                        Serial485.printf("i3.0闭合"); // 485输出
                        return 0;
                    }
                    else
                    {// i3.0没闭合
                        tag2: if (digital(PIN_34) == CUT_OFF) // 检测3.4是否断开
                        {// 3.4断开
                            if( digitalRead(PIN_i20) == CUT_OFF )//i2.0是否断开
                            {// i2.0断开
                                // 停Q1.7或者Q2.0 存信息
                                digitalWrite(PIN_Q17, SHUT_DOWN);
                                save_alert_msg("i2.0断开");  // 存储报警信息
                                Serial485.printf("i2.0断开"); // 485输出
                                return 0;
                            }
                            else
                            {// i2.0 没断开
                                goto tag3;
                            }
                        }
                        else
                        {//3.4没断开
                            goto tag3;
                        }
                    }

                }

            }

        }
    }
    else
    {// i3.4没闭合
        tag3:if( digitalRead(PIN_i01)==CLOSED )//i0.1是否闭合
        {// i0.1闭合

        }
        else
        {// i0.1没闭合
            if()//i1.7是否闭合
            {// i1.7闭合

            }
            else
            {// i1.7没闭合
                return 0;
            }

        }

    }

}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: sdmod.hxx,v $
 *
 *  $Revision: 1.17 $
 *
 *  last change: $Author: ka $ $Date: 2002-12-11 14:54:56 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 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
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _SDMOD_HXX
#define _SDMOD_HXX

#ifndef _SDDLL_HXX
#define _SD_DLL             // fuer SD_MOD()
#include "sddll.hxx"        // fuer SdModuleDummy
#endif
#ifndef _SD_GLOB_HXX
#include "glob.hxx"
#endif
#ifndef _PRESENTATION_HXX
#include "pres.hxx"
#endif
#ifndef _SVSTOR_HXX
#include <so3/svstor.hxx>
#endif
#ifndef _SFXLSTNER_HXX
#include <svtools/lstner.hxx>
#endif
#ifndef _COM_SUN_STAR_TEXT_WRITINGMODE_HPP_
#include <com/sun/star/text/WritingMode.hpp>
#endif

class SdOptions;
class BasicIDE;
class SvxSearchItem;
class SdAppLinkHdl; ;
class SvxErrorHandler;
class EditFieldInfo;
class SvFactory;
class SdTransferable;
class SdDrawDocShell;
class SvNumberFormatter;
class SfxErrorHandler;
class SdView;
class OutputDevice;
class SdPage;
class SdDrawDocument;
// ----------------------
// - SdOptionStreamMode -
// ----------------------

enum SdOptionStreamMode
{
    SD_OPTION_LOAD = 0,
    SD_OPTION_STORE = 1
};

/*************************************************************************
|*
|* This subclass of <SfxModule> (which is a subclass of <SfxShell>) is
|* linked to the DLL. One instance of this class exists while the DLL is
|* loaded.
|*
|* SdModule is like to be compared with the <SfxApplication>-subclass.
|*
|* Remember: Don`t export this class! It uses DLL-internal symbols.
|*
\************************************************************************/

class SdModule : public SdModuleDummy, public SfxListener
{
protected:

    SdOptions*              pImpressOptions;
    SdOptions*              pDrawOptions;
    SvxSearchItem*          pSearchItem;
    SvNumberFormatter*      pNumberFormatter;
    SvStorageRef            xOptionStorage;
    BOOL                    bAutoSave;
    BOOL                    bWaterCan;
    SfxErrorHandler*        mpErrorHdl;
    OutputDevice*           mpRefDevice;

    virtual void            Notify( SfxBroadcaster& rBC, const SfxHint& rHint );

public:

                            TYPEINFO();
                            SFX_DECL_INTERFACE(SD_IF_SDAPP);
                            DECL_LINK( CalcFieldValueHdl, EditFieldInfo* );

                            SdModule(SvFactory* pDrawObjFact, SvFactory* pGraphicObjFact);
    virtual                 ~SdModule();

    SdTransferable*         pTransferClip;
    SdTransferable*         pTransferDrag;
    SdTransferable*         pTransferSelection;

    void                    Execute(SfxRequest& rReq);
    void                    GetState(SfxItemSet&);

    virtual SfxModule*      Load();
    virtual void            Free();

    virtual void            FillStatusBar(StatusBar& rBar);

    SdOptions*              GetSdOptions(DocumentType eDocType);
    SvStorageStreamRef      GetOptionStream( const String& rOptionName, SdOptionStreamMode eMode );

    BOOL                    GetWaterCan() const { return bWaterCan; }
    void                    SetWaterCan( BOOL bWC ) { bWaterCan = bWC; }

    SvxSearchItem*          GetSearchItem() { return (pSearchItem); }
    void                    SetSearchItem(SvxSearchItem* pItem) { pSearchItem = pItem; }

    OutputDevice*           GetRefDevice( SdDrawDocShell& rDocShell );
    sal_Bool                IsPrinterRefDevice() const;

    SvNumberFormatter*      GetNumberFormatter();

    ::com::sun::star::text::WritingMode GetDefaultWritingMode() const;

    //virtuelle Methoden fuer den Optionendialog
    virtual SfxItemSet*  CreateItemSet( USHORT nId );
    virtual void         ApplyItemSet( USHORT nId, const SfxItemSet& rSet );
    virtual SfxTabPage*  CreateTabPage( USHORT nId, Window* pParent, const SfxItemSet& rSet );

private:

    /** Create a new summary page.  When the document has been created in
        the kiosk mode with automatical transitions then this method adds
        this kind of transition to the new summary page.
        @param pViewFrame
            The view frame that is used to execute the slot for creating the
            summary page.
        @param pDocument
            The document which will contain the summary page and from which
            the information about the default transition is retrieved.
    */
    void AddSummaryPage (SfxViewFrame* pViewFrame, SdDrawDocument* pDocument);
};

#ifndef SD_MOD
#define SD_MOD() ( *(SdModule**) GetAppData(SHL_DRAW) )
#endif

#endif                                 // _SDMOD_HXX

<commit_msg>INTEGRATION: CWS draw9 (1.17.84); FILE MERGED 2003/04/04 12:57:22 af 1.17.84.1: #108104# Modified access to virtual ref device.<commit_after>/*************************************************************************
 *
 *  $RCSfile: sdmod.hxx,v $
 *
 *  $Revision: 1.18 $
 *
 *  last change: $Author: rt $ $Date: 2003-04-24 14:35:42 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 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
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _SDMOD_HXX
#define _SDMOD_HXX

#ifndef _SDDLL_HXX
#define _SD_DLL             // fuer SD_MOD()
#include "sddll.hxx"        // fuer SdModuleDummy
#endif
#ifndef _SD_GLOB_HXX
#include "glob.hxx"
#endif
#ifndef _PRESENTATION_HXX
#include "pres.hxx"
#endif
#ifndef _SVSTOR_HXX
#include <so3/svstor.hxx>
#endif
#ifndef _SFXLSTNER_HXX
#include <svtools/lstner.hxx>
#endif
#ifndef _COM_SUN_STAR_TEXT_WRITINGMODE_HPP_
#include <com/sun/star/text/WritingMode.hpp>
#endif

class SdOptions;
class BasicIDE;
class SvxSearchItem;
class SdAppLinkHdl; ;
class SvxErrorHandler;
class EditFieldInfo;
class SvFactory;
class SdTransferable;
class SdDrawDocShell;
class SvNumberFormatter;
class SfxErrorHandler;
class SdView;
class OutputDevice;
class SdPage;
class SdDrawDocument;
// ----------------------
// - SdOptionStreamMode -
// ----------------------

enum SdOptionStreamMode
{
    SD_OPTION_LOAD = 0,
    SD_OPTION_STORE = 1
};

/*************************************************************************
|*
|* This subclass of <SfxModule> (which is a subclass of <SfxShell>) is
|* linked to the DLL. One instance of this class exists while the DLL is
|* loaded.
|*
|* SdModule is like to be compared with the <SfxApplication>-subclass.
|*
|* Remember: Don`t export this class! It uses DLL-internal symbols.
|*
\************************************************************************/

class SdModule : public SdModuleDummy, public SfxListener
{
protected:

    SdOptions*              pImpressOptions;
    SdOptions*              pDrawOptions;
    SvxSearchItem*          pSearchItem;
    SvNumberFormatter*      pNumberFormatter;
    SvStorageRef            xOptionStorage;
    BOOL                    bAutoSave;
    BOOL                    bWaterCan;
    SfxErrorHandler*        mpErrorHdl;
    /** This device is used for printer independent layout.  It is virtual
        in the sense that it does not represent a printer.  The pointer may
        be NULL when the virtual device could not be created.
    */
    OutputDevice*           mpVirtualRefDevice;

    virtual void            Notify( SfxBroadcaster& rBC, const SfxHint& rHint );

public:

                            TYPEINFO();
                            SFX_DECL_INTERFACE(SD_IF_SDAPP);
                            DECL_LINK( CalcFieldValueHdl, EditFieldInfo* );

                            SdModule(SvFactory* pDrawObjFact, SvFactory* pGraphicObjFact);
    virtual                 ~SdModule();

    SdTransferable*         pTransferClip;
    SdTransferable*         pTransferDrag;
    SdTransferable*         pTransferSelection;

    void                    Execute(SfxRequest& rReq);
    void                    GetState(SfxItemSet&);

    virtual SfxModule*      Load();
    virtual void            Free();

    virtual void            FillStatusBar(StatusBar& rBar);

    SdOptions*              GetSdOptions(DocumentType eDocType);
    SvStorageStreamRef      GetOptionStream( const String& rOptionName, SdOptionStreamMode eMode );

    BOOL                    GetWaterCan() const { return bWaterCan; }
    void                    SetWaterCan( BOOL bWC ) { bWaterCan = bWC; }

    SvxSearchItem*          GetSearchItem() { return (pSearchItem); }
    void                    SetSearchItem(SvxSearchItem* pItem) { pSearchItem = pItem; }

    /** Return the virtual device that can be used for printer independent
        layout.
        @return
            The returned pointer is NULL when the device could not be
            created when this modules was instantiated.
    */
    OutputDevice* GetVirtualRefDevice (void);

    /** Deprecated alias to <member>GetVirtualRefDevice</member>.
        @param rDocShell
            Unused dummy parameter.
    */
    OutputDevice* GetRefDevice (SdDrawDocShell& rDocShell);

    SvNumberFormatter*      GetNumberFormatter();

    ::com::sun::star::text::WritingMode GetDefaultWritingMode() const;

    //virtuelle Methoden fuer den Optionendialog
    virtual SfxItemSet*  CreateItemSet( USHORT nId );
    virtual void         ApplyItemSet( USHORT nId, const SfxItemSet& rSet );
    virtual SfxTabPage*  CreateTabPage( USHORT nId, Window* pParent, const SfxItemSet& rSet );

private:

    /** Create a new summary page.  When the document has been created in
        the kiosk mode with automatical transitions then this method adds
        this kind of transition to the new summary page.
        @param pViewFrame
            The view frame that is used to execute the slot for creating the
            summary page.
        @param pDocument
            The document which will contain the summary page and from which
            the information about the default transition is retrieved.
    */
    void AddSummaryPage (SfxViewFrame* pViewFrame, SdDrawDocument* pDocument);
};

#ifndef SD_MOD
#define SD_MOD() ( *(SdModule**) GetAppData(SHL_DRAW) )
#endif

#endif                                 // _SDMOD_HXX

<|endoftext|>


<commit_before>// Best-first Utility-Guided Search Yes! From:
// "Best-first Utility-Guided Search," Wheeler Ruml and Minh B. Do,
// Proceedings of the Twentieth International Joint Conference on
// Artificial Intelligence (IJCAI-07), 2007

#include "../search/search.hpp"
#include "../structs/binheap.hpp"
#include "../utils/pool.hpp"

template <class D> struct Bugsy : public SearchAlgorithm<D> {
	typedef typename D::State State;
	typedef typename D::PackedState PackedState;
	typedef typename D::Cost Cost;
	typedef typename D::Oper Oper;

	struct Node : SearchNode<D> {
		typename D::Cost f, h, d;
		double u, t;

		static bool pred(Node *a, Node *b) {
			if (a->u != b->u)
				return a->u > b->u;
			if (a->t != b->t)
				return a->t < b->t;
			if (a->f != b->f)
				return a->f < b->f;
			return a->g > b->g;
		}
	};

	Bugsy(int argc, const char *argv[]) :
			SearchAlgorithm<D>(argc, argv),
			timeper(0.0), nresort(0), pertick(20), nexp(0), state(WaitTick),
			closed(30000001) {
		wf = wt = -1;
		for (int i = 0; i < argc; i++) {
			if (i < argc - 1 && strcmp(argv[i], "-wf") == 0)
				wf = strtod(argv[++i], NULL);
			else if (i < argc - 1 && strcmp(argv[i], "-wt") == 0)
				wt = strtod(argv[++i], NULL);
		}

		if (wf < 0)
			fatal("Must specify non-negative f-weight using -wf");
		if (wt < 0)
			fatal("Must specify non-negative t-weight using -wt");

		nodes = new Pool<Node>();
	}

	~Bugsy(void) {
		delete nodes;
	}

	enum { WaitTick, ExpandSome, WaitExpand };

	void search(D &d, typename D::State &s0) {
		this->start();
		closed.init(d);
		Node *n0 = init(d, s0);
		closed.add(n0);
		open.push(n0);

		lasttick = walltime();
		while (!open.empty() && !SearchAlgorithm<D>::limit()) {
			updatetime();

			Node* n = *open.pop();
			State buf, &state = d.unpack(buf, n->packed);
			if (d.isgoal(state)) {
				SearchAlgorithm<D>::res.goal(d, n);
				break;
			}
			expand(d, n, state);
		}

		this->finish();
	}

	virtual void reset(void) {
		SearchAlgorithm<D>::reset();
		open.clear();
		closed.clear();
		delete nodes;
		timeper = 0.0;
		state = WaitTick;
		pertick = 20;
		nexp = 0;
		nresort = 0;
		nodes = new Pool<Node>();
	}

	virtual void output(FILE *out) {
		SearchAlgorithm<D>::output(out);
		closed.prstats(stdout, "closed ");
		dfpair(stdout, "open list type", "%s", "binary heap");
		dfpair(stdout, "node size", "%u", sizeof(Node));
		dfpair(stdout, "cost weight", "%g", wf);
		dfpair(stdout, "time weight", "%g", wt);
		dfpair(stdout, "final time per expand", "%g", timeper);
		dfpair(stdout, "number of resorts", "%lu", nresort);
	}

private:

	void expand(D &d, Node *n, State &state) {
		SearchAlgorithm<D>::res.expd++;

		for (unsigned int i = 0; i < d.nops(state); i++) {
			Oper op = d.nthop(state, i);
			if (op == n->pop)
				continue;
			SearchAlgorithm<D>::res.gend++;
			considerkid(d, n, state, op);
		}
	}

	void considerkid(D &d, Node *parent, State &state, Oper op) {
		Node *kid = nodes->construct();
		typename D::Transition tr(d, state, op);
		kid->g = parent->g + tr.cost;
		d.pack(kid->packed, tr.state);

		unsigned long hash = kid->packed.hash();
		Node *dup = static_cast<Node*>(closed.find(kid->packed, hash));
		if (dup) {
			SearchAlgorithm<D>::res.dups++;
			if (kid->g >= dup->g) {
				nodes->destruct(kid);
				return;
			}
			SearchAlgorithm<D>::res.reopnd++;
			dup->f = dup->f - dup->g + kid->g;
			dup->update(kid->g, parent, op, tr.revop);
			computeutil(dup);
			open.pushupdate(dup, dup->openind);
			nodes->destruct(kid);
		} else {
			kid->d = d.d(tr.state);
			kid->h = d.h(tr.state);
			kid->f = kid->g + kid->h;
			kid->update(kid->g, parent, op, tr.revop);
			computeutil(kid);
			closed.add(kid, hash);
			open.push(kid);
		}
	}

	Node *init(D &d, State &s0) {
		Node *n0 = nodes->construct();
		d.pack(n0->packed, s0);
		n0->g = Cost(0);
		n0->h = n0->f = d.h(s0);
		n0->d = d.d(s0);
		computeutil(n0);
		n0->op = n0->pop = D::Nop;
		n0->parent = NULL;
		return n0;
	}

	void computeutil(Node *n) {
		n->t = timeper * n->d;
		n->u = -(wf * n->f + wt * n->t);
	}

	void updatetime(void) {
		double now;
		nexp++;

		switch (state) {
		case WaitTick:
			now = walltime();
			if (now <= lasttick)
				break;
			starttime = now;
			state = ExpandSome;
			break;

		case ExpandSome:
			if (nexp < pertick)
				break;
			lasttick = walltime();
			state = WaitExpand;
			break;

		case WaitExpand:
			now = walltime();
			if (now <= lasttick)
				break;
			updateopen();
			timeper = (now - starttime) / nexp;
			// 1.8 * nexp from Wheeler's bugsy_old.ml
			pertick = nexp * 9 / 5;
			nexp = 0;
			starttime = now;
			state = ExpandSome;
			break;

		default:
			fatal("Unknown update time state: %d\n", state);
		}
	}

	void updateopen(void) {
		nresort++;
		for (int i = 0; i < open.size(); i++)
			computeutil(open.at(i));
		open.reinit();
	}

	double wf, wt;

	// for nodes-per-second estimation
	double timeper;
	unsigned long nresort, pertick, nexp;
	double starttime, lasttick;
	int state;

	BinHeap<Node, Node*> open;
 	ClosedList<SearchNode<D>, SearchNode<D>, D> closed;
	Pool<Node> *nodes;
};
<commit_msg>search: online 'global' correction for BUGSY. There is a bug in how solution costs are computed for any non-optimal algorithm that does inconsistent tree updating.   We will need to change the domains to compute their own solution cost by traversing the returned solution path.  This is probably good as it will also be a great time to validate the solutions.<commit_after>// Best-first Utility-Guided Search Yes! From:
// "Best-first Utility-Guided Search," Wheeler Ruml and Minh B. Do,
// Proceedings of the Twentieth International Joint Conference on
// Artificial Intelligence (IJCAI-07), 2007

#include "../search/search.hpp"
#include "../structs/binheap.hpp"
#include "../utils/pool.hpp"

template <class D> struct Bugsy : public SearchAlgorithm<D> {
	typedef typename D::State State;
	typedef typename D::PackedState PackedState;
	typedef typename D::Cost Cost;
	typedef typename D::Oper Oper;

	struct Node : SearchNode<D> {
		typename D::Cost f, h, d;
		double u, t;

		static bool pred(Node *a, Node *b) {
			if (a->u != b->u)
				return a->u > b->u;
			if (a->t != b->t)
				return a->t < b->t;
			if (a->f != b->f)
				return a->f < b->f;
			return a->g > b->g;
		}
	};

	Bugsy(int argc, const char *argv[]) :
			SearchAlgorithm<D>(argc, argv),
			correct(false), herror(0), derror(0), navg(0),
			timeper(0.0), nresort(0), pertick(20), nexp(0), state(WaitTick),
			closed(30000001) {
		wf = wt = -1;
		for (int i = 0; i < argc; i++) {
			if (i < argc - 1 && strcmp(argv[i], "-wf") == 0)
				wf = strtod(argv[++i], NULL);
			else if (i < argc - 1 && strcmp(argv[i], "-wt") == 0)
				wt = strtod(argv[++i], NULL);
			else if (strcmp(argv[i], "-correct") == 0)
				correct = true;
		}

		if (wf < 0)
			fatal("Must specify non-negative f-weight using -wf");
		if (wt < 0)
			fatal("Must specify non-negative t-weight using -wt");

		nodes = new Pool<Node>();
	}

	~Bugsy(void) {
		delete nodes;
	}

	enum { WaitTick, ExpandSome, WaitExpand };

	void search(D &d, typename D::State &s0) {
		this->start();
		closed.init(d);
		Node *n0 = init(d, s0);
		closed.add(n0);
		open.push(n0);

		lasttick = walltime();
		while (!open.empty() && !SearchAlgorithm<D>::limit()) {
			updatetime();

			Node* n = *open.pop();
			State buf, &state = d.unpack(buf, n->packed);
			if (d.isgoal(state)) {
				SearchAlgorithm<D>::res.goal(d, n);
				break;
			}
			expand(d, n, state);
		}

		this->finish();
	}

	virtual void reset(void) {
		SearchAlgorithm<D>::reset();
		open.clear();
		closed.clear();
		delete nodes;
		timeper = 0.0;
		state = WaitTick;
		pertick = 20;
		nexp = 0;
		nresort = 0;
		nodes = new Pool<Node>();
	}

	virtual void output(FILE *out) {
		SearchAlgorithm<D>::output(out);
		closed.prstats(stdout, "closed ");
		dfpair(stdout, "open list type", "%s", "binary heap");
		dfpair(stdout, "node size", "%u", sizeof(Node));
		dfpair(stdout, "cost weight", "%g", wf);
		dfpair(stdout, "time weight", "%g", wt);
		dfpair(stdout, "final time per expand", "%g", timeper);
		dfpair(stdout, "number of resorts", "%lu", nresort);
		dfpair(stdout, "corrected h and d", "%s", correct ? "yes" : "no");
		if (correct) {
			dfpair(stdout, "final h error", "%g", herror);
			dfpair(stdout, "final d error", "%g", derror);
		}
	}

private:

	void expand(D &d, Node *n, State &state) {
		SearchAlgorithm<D>::res.expd++;
		double hbest = -1, hcost = -1, dbest = -1;

		for (unsigned int i = 0; i < d.nops(state); i++) {
			Oper op = d.nthop(state, i);
			if (op == n->pop)
				continue;
			SearchAlgorithm<D>::res.gend++;
			Node *k = considerkid(d, n, state, op);

			if (!correct || !k)
				continue;

			if (hbest < 0 || k->h < hbest) {
				hbest = k->h;
				hcost = k->g - n->g;
			}
			if (dbest < 0 || k->d < dbest)
				dbest = k->d;
		}

		if (!correct || hbest < 0)
			return;

		navg++;
		double herr = hbest + hcost - n->h;
		herror = herror + (herr - herror)/navg;
		double derr = dbest + 1 - n->d;
		derror = derror + (derr - derror)/navg;
	}

	Node *considerkid(D &d, Node *parent, State &state, Oper op) {
		Node *kid = nodes->construct();
		typename D::Transition tr(d, state, op);
		kid->g = parent->g + tr.cost;
		d.pack(kid->packed, tr.state);

		unsigned long hash = kid->packed.hash();
		Node *dup = static_cast<Node*>(closed.find(kid->packed, hash));
		if (dup) {
			SearchAlgorithm<D>::res.dups++;
			if (kid->g >= dup->g) {
				nodes->destruct(kid);
				return NULL;
			}
			SearchAlgorithm<D>::res.reopnd++;
			dup->f = dup->f - dup->g + kid->g;
			dup->update(kid->g, parent, op, tr.revop);
			computeutil(dup);
			open.pushupdate(dup, dup->openind);
			nodes->destruct(kid);
			return dup;
		}
		kid->d = d.d(tr.state);
		kid->h = d.h(tr.state);
		kid->f = kid->g + kid->h;
		kid->update(kid->g, parent, op, tr.revop);
		computeutil(kid);
		closed.add(kid, hash);
		open.push(kid);
		return kid;
	}

	Node *init(D &d, State &s0) {
		Node *n0 = nodes->construct();
		d.pack(n0->packed, s0);
		n0->g = Cost(0);
		n0->h = n0->f = d.h(s0);
		n0->d = d.d(s0);
		computeutil(n0);
		n0->op = n0->pop = D::Nop;
		n0->parent = NULL;
		return n0;
	}

	void computeutil(Node *n) {
		n->t = timeper * n->d;
		n->u = -(wf * n->f + wt * n->t);
	}

	void updatetime(void) {
		double now;
		nexp++;

		switch (state) {
		case WaitTick:
			now = walltime();
			if (now <= lasttick)
				break;
			starttime = now;
			state = ExpandSome;
			break;

		case ExpandSome:
			if (nexp < pertick)
				break;
			lasttick = walltime();
			state = WaitExpand;
			break;

		case WaitExpand:
			now = walltime();
			if (now <= lasttick)
				break;
			updateopen();
			timeper = (now - starttime) / nexp;
			// 1.8 * nexp from Wheeler's bugsy_old.ml
			pertick = nexp * 9 / 5;
			nexp = 0;
			starttime = now;
			state = ExpandSome;
			break;

		default:
			fatal("Unknown update time state: %d\n", state);
		}
	}

	void updateopen(void) {
		nresort++;
		for (int i = 0; i < open.size(); i++)
			computeutil(open.at(i));
		open.reinit();
	}

	double wf, wt;

	// heuristic updating
	bool correct;
	double herror, derror;
	unsigned long navg;

	// for nodes-per-second estimation
	double timeper;
	unsigned long nresort, pertick, nexp;
	double starttime, lasttick;
	int state;

	BinHeap<Node, Node*> open;
 	ClosedList<SearchNode<D>, SearchNode<D>, D> closed;
	Pool<Node> *nodes;
};
<|endoftext|>


<commit_before>//   Copyright 2016-2019 Jean-Francois Poilpret
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

/*
 * Special check for LinkedList container (kind of unit tests).
 * Wiring:
 * - Arduino UNO
 *   - Standard USB to console
 */

#include <fastarduino/linked_list.h>
#include <fastarduino/uart.h>
#include <fastarduino/streams.h>
#include <fastarduino/iomanip.h>

#ifdef ARDUINO_UNO
static const board::USART USART = board::USART::USART0;
// Define vectors we need in the example
REGISTER_UATX_ISR(0)
#else
#error "Current target is not yet supported!"
#endif

// Buffers for UART
static const uint8_t OUTPUT_BUFFER_SIZE = 64;
static char output_buffer[OUTPUT_BUFFER_SIZE];

static const uint8_t BUFFER_SIZE = 10;

using namespace streams;
using namespace containers;

// Generic assertion
//===================
template<typename T1, typename T2> void assert(ostream& out, const char* var, T1 expected, T2 actual)
{
	out << "    Comparing " << var;
	if (expected == actual)
		out << " OK: " << expected << endl;
	else
		out << " KO exp=" << expected << " act=" << actual << endl;
}

// Traveral class running assertions against expected content
//============================================================
template<typename ITEM> class TraversalAssert
{
public:
	TraversalAssert(ostream& out)
	: out{out}, index{}, expected{}, size{} {}

	template<uint8_t SIZE>
	TraversalAssert(ostream& out, ITEM (&expected)[SIZE])
	: out{out}, index{}, expected{expected}, size{SIZE} {}

	bool operator()(ITEM& item)
	{
		if (index < size)
		{
			char buf[] = "item[x]";
			buf[5] = '0' + index;
			assert(out, buf, expected[index], item);
		}
		else
		{
			out << "    KO -> Too many items in list!" << endl;
		}
		++index;
		return false;
	}

private:
	ostream& out;
	uint8_t index;
	ITEM* expected;
	uint8_t size;
};

// Link items classes
//====================
class Link1 : public Link<Link1>
{
public:
	Link1(uint32_t value) : value{value} {}

	const uint32_t value;
};

class Link2
{
public:
	Link2(uint32_t value) : value{value} {}

	const uint32_t value;
};

// Define the 3 types of links used on this test
//===============================================

// Link items obtained by subclassing Link
using LINK1 = Link1;
// Link items obtained by wrapping in LinkWrapper
using LINK2 = LinkWrapper<Link2>;
// Simple chars as wrapped links
using LINK3 = LinkWrapper<char, char, char>;

// Operators required for assertions
//===================================
ostream& operator<<(ostream& out, const LINK1& item)
{
	return out << item.value;
}
ostream& operator<<(ostream& out, const LINK2& item)
{
	return out << item.item().value;
}
ostream& operator<<(ostream& out, const LINK3& item)
{
	return out << item.item();
}
bool operator==(const LINK1& item1, const LINK1& item2)
{
	return item1.value == item2.value;
}
bool operator==(const LINK2& item1, const LINK2& item2)
{
	return item1.item().value == item2.item().value;
}
bool operator==(const LINK3& item1, const LINK3& item2)
{
	return item1.item() == item2.item();
}

// Variables used in tests
//=========================
static LINK1 links1[] = {123456UL, 0UL, 123UL, 456UL, 654321UL};
static LINK2 links2[] = {LINK2(123456UL), LINK2(0UL), LINK2(123UL), LINK2(456UL), LINK2(654321UL)};
static LINK3 links3[] = {'a', 'b', 'c', 'd', 'e'};

// List-checking functions
//=========================
//TODO check remove first and remove last
template<typename LINK> void check_link_list(ostream& out, LINK links[5])
{
	LinkedList<LINK> list;
	list.traverse(TraversalAssert<LINK>{out});

	out << "after insert() #1" << endl;
	list.insert(links[0]);
	LINK expected1[] = {links[0]};
	list.traverse(TraversalAssert<LINK>{out, expected1});

	out << "after insert() #2" << endl;
	list.insert(links[1]);
	LINK expected2[] = {links[1], links[0]};
	list.traverse(TraversalAssert<LINK>{out, expected2});

	out << "after insert() #3,4,5" << endl;
	list.insert(links[2]);
	list.insert(links[3]);
	list.insert(links[4]);
	LINK expected3[] = {links[4], links[3], links[2], links[1], links[0]};
	list.traverse(TraversalAssert<LINK>{out, expected3});

	out << "after remove() #3" << endl;
	list.remove(links[2]);
	LINK expected4[] = {links[4], links[3], links[1], links[0]};
	list.traverse(TraversalAssert<LINK>{out, expected4});

	out << "after remove() #3 second time" << endl;
	list.remove(links[2]);
	list.traverse(TraversalAssert<LINK>{out, expected4});

	out << "after remove() #1" << endl;
	list.remove(links[0]);
	LINK expected5[] = {links[4], links[3], links[1]};
	list.traverse(TraversalAssert<LINK>{out, expected5});

	out << "after remove() #5" << endl;
	list.remove(links[4]);
	LINK expected6[] = {links[3], links[1]};
	list.traverse(TraversalAssert<LINK>{out, expected6});

	out << "after remove all" << endl;
	list.remove(links[1]);
	list.remove(links[3]);
	list.traverse(TraversalAssert<LINK>{out});

	out << endl;
}

void check_link1(ostream& out)
{
	// Create a list of Link1 and operate on it
	out << "list of Link<uint32_t>" << endl;
	check_link_list(out, links1);
}

void check_link2(ostream& out)
{
	// Create a list of Link1 and operate on it
	out << "list of LinkWrapper<Link1>" << endl;
	check_link_list(out, links2);
}

void check_link3(ostream& out)
{
	// Create a list of Link1 and operate on it
	out << "list of LinkWrapper<char>" << endl;
	check_link_list(out, links3);
}

int main()
{
	board::init();
	// Enable interrupts at startup time
	sei();

	// Start UART
	serial::hard::UATX<USART> uart{output_buffer};
	uart.begin(115200);
	ostream out = uart.out();

	check_link1(out);
	check_link2(out);
	check_link3(out);

	return 0;
}
<commit_msg>Remove obsolete comment.<commit_after>//   Copyright 2016-2019 Jean-Francois Poilpret
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

/*
 * Special check for LinkedList container (kind of unit tests).
 * Wiring:
 * - Arduino UNO
 *   - Standard USB to console
 */

#include <fastarduino/linked_list.h>
#include <fastarduino/uart.h>
#include <fastarduino/streams.h>
#include <fastarduino/iomanip.h>

#ifdef ARDUINO_UNO
static const board::USART USART = board::USART::USART0;
// Define vectors we need in the example
REGISTER_UATX_ISR(0)
#else
#error "Current target is not yet supported!"
#endif

// Buffers for UART
static const uint8_t OUTPUT_BUFFER_SIZE = 64;
static char output_buffer[OUTPUT_BUFFER_SIZE];

static const uint8_t BUFFER_SIZE = 10;

using namespace streams;
using namespace containers;

// Generic assertion
//===================
template<typename T1, typename T2> void assert(ostream& out, const char* var, T1 expected, T2 actual)
{
	out << "    Comparing " << var;
	if (expected == actual)
		out << " OK: " << expected << endl;
	else
		out << " KO exp=" << expected << " act=" << actual << endl;
}

// Traveral class running assertions against expected content
//============================================================
template<typename ITEM> class TraversalAssert
{
public:
	TraversalAssert(ostream& out)
	: out{out}, index{}, expected{}, size{} {}

	template<uint8_t SIZE>
	TraversalAssert(ostream& out, ITEM (&expected)[SIZE])
	: out{out}, index{}, expected{expected}, size{SIZE} {}

	bool operator()(ITEM& item)
	{
		if (index < size)
		{
			char buf[] = "item[x]";
			buf[5] = '0' + index;
			assert(out, buf, expected[index], item);
		}
		else
		{
			out << "    KO -> Too many items in list!" << endl;
		}
		++index;
		return false;
	}

private:
	ostream& out;
	uint8_t index;
	ITEM* expected;
	uint8_t size;
};

// Link items classes
//====================
class Link1 : public Link<Link1>
{
public:
	Link1(uint32_t value) : value{value} {}

	const uint32_t value;
};

class Link2
{
public:
	Link2(uint32_t value) : value{value} {}

	const uint32_t value;
};

// Define the 3 types of links used on this test
//===============================================

// Link items obtained by subclassing Link
using LINK1 = Link1;
// Link items obtained by wrapping in LinkWrapper
using LINK2 = LinkWrapper<Link2>;
// Simple chars as wrapped links
using LINK3 = LinkWrapper<char, char, char>;

// Operators required for assertions
//===================================
ostream& operator<<(ostream& out, const LINK1& item)
{
	return out << item.value;
}
ostream& operator<<(ostream& out, const LINK2& item)
{
	return out << item.item().value;
}
ostream& operator<<(ostream& out, const LINK3& item)
{
	return out << item.item();
}
bool operator==(const LINK1& item1, const LINK1& item2)
{
	return item1.value == item2.value;
}
bool operator==(const LINK2& item1, const LINK2& item2)
{
	return item1.item().value == item2.item().value;
}
bool operator==(const LINK3& item1, const LINK3& item2)
{
	return item1.item() == item2.item();
}

// Variables used in tests
//=========================
static LINK1 links1[] = {123456UL, 0UL, 123UL, 456UL, 654321UL};
static LINK2 links2[] = {LINK2(123456UL), LINK2(0UL), LINK2(123UL), LINK2(456UL), LINK2(654321UL)};
static LINK3 links3[] = {'a', 'b', 'c', 'd', 'e'};

// List-checking functions
//=========================
template<typename LINK> void check_link_list(ostream& out, LINK links[5])
{
	LinkedList<LINK> list;
	list.traverse(TraversalAssert<LINK>{out});

	out << "after insert() #1" << endl;
	list.insert(links[0]);
	LINK expected1[] = {links[0]};
	list.traverse(TraversalAssert<LINK>{out, expected1});

	out << "after insert() #2" << endl;
	list.insert(links[1]);
	LINK expected2[] = {links[1], links[0]};
	list.traverse(TraversalAssert<LINK>{out, expected2});

	out << "after insert() #3,4,5" << endl;
	list.insert(links[2]);
	list.insert(links[3]);
	list.insert(links[4]);
	LINK expected3[] = {links[4], links[3], links[2], links[1], links[0]};
	list.traverse(TraversalAssert<LINK>{out, expected3});

	out << "after remove() #3" << endl;
	list.remove(links[2]);
	LINK expected4[] = {links[4], links[3], links[1], links[0]};
	list.traverse(TraversalAssert<LINK>{out, expected4});

	out << "after remove() #3 second time" << endl;
	list.remove(links[2]);
	list.traverse(TraversalAssert<LINK>{out, expected4});

	out << "after remove() #1" << endl;
	list.remove(links[0]);
	LINK expected5[] = {links[4], links[3], links[1]};
	list.traverse(TraversalAssert<LINK>{out, expected5});

	out << "after remove() #5" << endl;
	list.remove(links[4]);
	LINK expected6[] = {links[3], links[1]};
	list.traverse(TraversalAssert<LINK>{out, expected6});

	out << "after remove all" << endl;
	list.remove(links[1]);
	list.remove(links[3]);
	list.traverse(TraversalAssert<LINK>{out});

	out << endl;
}

void check_link1(ostream& out)
{
	// Create a list of Link1 and operate on it
	out << "list of Link<uint32_t>" << endl;
	check_link_list(out, links1);
}

void check_link2(ostream& out)
{
	// Create a list of Link1 and operate on it
	out << "list of LinkWrapper<Link1>" << endl;
	check_link_list(out, links2);
}

void check_link3(ostream& out)
{
	// Create a list of Link1 and operate on it
	out << "list of LinkWrapper<char>" << endl;
	check_link_list(out, links3);
}

int main()
{
	board::init();
	// Enable interrupts at startup time
	sei();

	// Start UART
	serial::hard::UATX<USART> uart{output_buffer};
	uart.begin(115200);
	ostream out = uart.out();

	check_link1(out);
	check_link2(out);
	check_link3(out);

	return 0;
}
<|endoftext|>