code:
/****************************************************************************
Copyright (c) 2010-2011 cocos2d-x.org
Copyright (c) 2009      Lam Pham

http://www.cocos2d-x.org

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 "CCTransitionRadial.h"
#include "CCDirector.h"
#include "CCRenderTexture.h"
#include "CCActionInstant.h"
#include "CCActionProgressTimer.h"
#include "CCPointExtension.h"

namespace   cocos2d {

//#import "CCDirector.h"
//#import "CCRadialTransition.h"
//#import "CCRenderTexture.h"
//#import "CCLayer.h"
//#import "CCInstantAction.h"
//#import "Support/CCPointExtension.h"

enum {
	kSceneRadial = 0xc001,
};

void CCTransitionRadialCCW::sceneOrder()
{
	m_bIsInSceneOnTop = false;
}

CCProgressTimerType CCTransitionRadialCCW::radialType()
{
	return kCCProgressTimerTypeRadialCCW;
}

void CCTransitionRadialCCW::onEnter()
{
	CCTransitionScene::onEnter();
	// create a transparent color layer
	// in which we are going to add our rendertextures
	CCSize size = CCDirector::sharedDirector()->getWinSize();

	// create the second render texture for outScene
	CCRenderTexture *outTexture = CCRenderTexture::renderTextureWithWidthAndHeight((int)size.width, (int)size.height);

	if (NULL == outTexture)
	{
		return;
	}

	outTexture->getSprite()->setAnchorPoint(ccp(0.5f,0.5f));
	outTexture->setPosition(ccp(size.width/2, size.height/2));
	outTexture->setAnchorPoint(ccp(0.5f,0.5f));

	// render outScene to its texturebuffer
	outTexture->clear(0,0,0,1);
	outTexture->begin();
	m_pOutScene->visit();
	outTexture->end();

	//	Since we've passed the outScene to the texture we don't need it.
	this->hideOutShowIn();

	//	We need the texture in RenderTexture.
	CCProgressTimer *outNode = CCProgressTimer::progressWithTexture(outTexture->getSprite()->getTexture());
	// but it's flipped upside down so we flip the sprite
	outNode->getSprite()->setFlipY(true);
	//	Return the radial type that we want to use
	outNode->setType(radialType());
	outNode->setPercentage(100.f);
	outNode->setPosition(ccp(size.width/2, size.height/2));
	outNode->setAnchorPoint(ccp(0.5f,0.5f));

	// create the blend action
	CCAction * layerAction = CCSequence::actions
	(
		CCProgressFromTo::actionWithDuration(m_fDuration, 100.0f, 0.0f),
		CCCallFunc::actionWithTarget(this, callfunc_selector(CCTransitionScene::finish)),
		NULL
	);
	// run the blend action
	outNode->runAction(layerAction);

	// add the layer (which contains our two rendertextures) to the scene
	this->addChild(outNode, 2, kSceneRadial);
}

// clean up on exit
void CCTransitionRadialCCW::onExit()
{
	// remove our layer and release all containing objects
	this->removeChildByTag(kSceneRadial, false);
	CCTransitionScene::onExit();
}

CCTransitionRadialCCW* CCTransitionRadialCCW::transitionWithDuration(ccTime t, CCScene* scene)
{
    CCTransitionRadialCCW* pScene = new CCTransitionRadialCCW();
    pScene->initWithDuration(t, scene);
    pScene->autorelease();

    return pScene;
}

CCProgressTimerType CCTransitionRadialCW::radialType()
{
	return kCCProgressTimerTypeRadialCW;
}

CCTransitionRadialCW* CCTransitionRadialCW::transitionWithDuration(ccTime t, CCScene* scene)
{
    CCTransitionRadialCW* pScene = new CCTransitionRadialCW();
    pScene->initWithDuration(t, scene);
    pScene->autorelease();

    return pScene;
}

}//namespace   cocos2d


code:
/****************************************************************************
Copyright (c) 2010-2011 cocos2d-x.org
Copyright (c) 2008-2010 Ricardo Quesada

http://www.cocos2d-x.org

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 "CCMenu.h"
#include "CCDirector.h"
#include "CCApplication.h"
#include "CCPointExtension.h"
#include "CCTouchDispatcher.h"
#include "CCTouch.h"
#include "CCStdC.h"

#include <vector>
#include <stdarg.h>

using namespace std;

namespace cocos2d{

	enum
	{
		kDefaultPadding =  5,
	};

	//
	//CCMenu
	//

        CCMenu* CCMenu::node()
        {
            return menuWithItem(NULL);
        }

	CCMenu * CCMenu::menuWithItems(CCMenuItem* item, ...)
	{
		va_list args;
		va_start(args,item);
		CCMenu *pRet = new CCMenu();
		if (pRet && pRet->initWithItems(item, args))
		{
			pRet->autorelease();
			va_end(args);
			return pRet;
		}
		va_end(args);
		CC_SAFE_DELETE(pRet)
		return NULL;
	}

	CCMenu* CCMenu::menuWithItem(CCMenuItem* item)
	{
		return menuWithItems(item, NULL);
	}

    bool CCMenu::init()
    {
        if (CCLayer::init())
        {
            this->m_bIsTouchEnabled = true;

            // menu in the center of the screen
            CCSize s = CCDirector::sharedDirector()->getWinSize();

            this->m_bIsRelativeAnchorPoint = false;
            setAnchorPoint(ccp(0.5f, 0.5f));
            this->setContentSize(s);

            // XXX: in v0.7, winSize should return the visible size
            // XXX: so the bar calculation should be done there
            CCRect r;
            CCApplication::sharedApplication().statusBarFrame(&r);
            ccDeviceOrientation orientation = CCDirector::sharedDirector()->getDeviceOrientation();
            if (orientation == CCDeviceOrientationLandscapeLeft || orientation == CCDeviceOrientationLandscapeRight)
            {
                s.height -= r.size.width;
            }
            else
            {
                s.height -= r.size.height;
            }
            setPosition(ccp(s.width/2, s.height/2));
            //	[self alignItemsVertically];
            m_pSelectedItem = NULL;
            m_eState = kCCMenuStateWaiting;
            return true;
        }
        return false;
    }

	bool CCMenu::initWithItems(CCMenuItem* item, va_list args)
	{
        if (init())
        {
            int z=0;

			if (item)
			{
				this->addChild(item, z);
				CCMenuItem *i = va_arg(args, CCMenuItem*);
				while (i)
				{
					z++;
					this->addChild(i, z);
					i = va_arg(args, CCMenuItem*);
				}
			}

			return true;
        }
        return false;
	}

	/*
	* override add:
	*/
	void CCMenu::addChild(CCNode * child, int zOrder)
	{
		CCLayer::addChild(child, zOrder);
	}

	void CCMenu::addChild(CCNode * child, int zOrder, int tag)
	{
		// we can not use RTTI, so we do not known the type of object
		/*CCAssert( dynamic_cast<CCMenuItem*>(child) != NULL, L"Menu only supports MenuItem objects as children");*/
		CCLayer::addChild(child, zOrder, tag);
	}

    void CCMenu::onExit()
    {
        if (m_eState == kCCMenuStateTrackingTouch)
        {
            m_pSelectedItem->unselected();
            m_eState = kCCMenuStateWaiting;
            m_pSelectedItem = NULL;
        }

        CCLayer::onExit();
    }

	//Menu - Events
	void CCMenu::registerWithTouchDispatcher()
	{
		CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this, kCCMenuTouchPriority, true);
	}

	bool CCMenu::ccTouchBegan(CCTouch* touch, CCEvent* event)
	{
        CC_UNUSED_PARAM(event);
		if (m_eState != kCCMenuStateWaiting || ! m_bIsVisible)
		{
			return false;
		}

		for (CCNode *c = this->m_pParent; c != NULL; c = c->getParent())
		{
			if (c->getIsVisible() == false)
			{
				return false;
			}
		}

		m_pSelectedItem = this->itemForTouch(touch);
		if (m_pSelectedItem)
		{
			m_eState = kCCMenuStateTrackingTouch;
			m_pSelectedItem->selected();
			return true;
		}
		return false;
	}

	void CCMenu::ccTouchEnded(CCTouch *touch, CCEvent* event)
	{
        CC_UNUSED_PARAM(touch);
        CC_UNUSED_PARAM(event);
		CCAssert(m_eState == kCCMenuStateTrackingTouch, "[Menu ccTouchEnded] -- invalid state");
		if (m_pSelectedItem)
		{
			m_pSelectedItem->unselected();
			m_pSelectedItem->activate();
		}
		m_eState = kCCMenuStateWaiting;
	}

	void CCMenu::ccTouchCancelled(CCTouch *touch, CCEvent* event)
	{
        CC_UNUSED_PARAM(touch);
        CC_UNUSED_PARAM(event);
		CCAssert(m_eState == kCCMenuStateTrackingTouch, "[Menu ccTouchCancelled] -- invalid state");
		if (m_pSelectedItem)
		{
			m_pSelectedItem->unselected();
		}
		m_eState = kCCMenuStateWaiting;
	}

	void CCMenu::ccTouchMoved(CCTouch* touch, CCEvent* event)
	{
        CC_UNUSED_PARAM(event);
		CCAssert(m_eState == kCCMenuStateTrackingTouch, "[Menu ccTouchMoved] -- invalid state");
		CCMenuItem *currentItem = this->itemForTouch(touch);
		if (currentItem != m_pSelectedItem)
		{
			if (m_pSelectedItem)
			{
				m_pSelectedItem->unselected();
			}
			m_pSelectedItem = currentItem;
			if (m_pSelectedItem)
			{
				m_pSelectedItem->selected();
			}
		}
	}

	//Menu - Alignment
	void CCMenu::alignItemsVertically()
	{
		this->alignItemsVerticallyWithPadding(kDefaultPadding);
	}

	void CCMenu::alignItemsVerticallyWithPadding(float padding)
	{
		float height = -padding;
		if (m_pChildren && m_pChildren->count() > 0)
		{
            CCObject* pObject = NULL;
            CCARRAY_FOREACH(m_pChildren, pObject)
            {
                CCNode* pChild = dynamic_cast<CCNode*>(pObject);
                if (pChild)
                {
                    height += pChild->getContentSize().height * pChild->getScaleY() + padding;
                }
            }
		}

		float y = height / 2.0f;
		if (m_pChildren && m_pChildren->count() > 0)
		{
            CCObject* pObject = NULL;
            CCARRAY_FOREACH(m_pChildren, pObject)
            {
                CCNode* pChild = dynamic_cast<CCNode*>(pObject);
                if (pChild)
                {
                    pChild->setPosition(ccp(0, y - pChild->getContentSize().height * pChild->getScaleY() / 2.0f));
                    y -= pChild->getContentSize().height * pChild->getScaleY() + padding;
                }
            }
		}
	}

	void CCMenu::alignItemsHorizontally(void)
	{
		this->alignItemsHorizontallyWithPadding(kDefaultPadding);
	}

	void CCMenu::alignItemsHorizontallyWithPadding(float padding)
	{

		float width = -padding;
		if (m_pChildren && m_pChildren->count() > 0)
		{
            CCObject* pObject = NULL;
            CCARRAY_FOREACH(m_pChildren, pObject)
            {
                CCNode* pChild = dynamic_cast<CCNode*>(pObject);
                if (pChild)
                {
                    width += pChild->getContentSize().width * pChild->getScaleX() + padding;
                }
            }
		}

		float x = -width / 2.0f;
		if (m_pChildren && m_pChildren->count() > 0)
		{
            CCObject* pObject = NULL;
            CCARRAY_FOREACH(m_pChildren, pObject)
            {
                CCNode* pChild = dynamic_cast<CCNode*>(pObject);
                if (pChild)
                {
                    pChild->setPosition(ccp(x + pChild->getContentSize().width * pChild->getScaleX() / 2.0f, 0));
     				x += pChild->getContentSize().width * pChild->getScaleX() + padding;
                }
            }
		}
	}

	void CCMenu::alignItemsInColumns(unsigned int columns, ...)
	{
		va_list args;
		va_start(args, columns);

		this->alignItemsInColumns(columns, args);

		va_end(args);
	}

	void CCMenu::alignItemsInColumns(unsigned int columns, va_list args)
	{
		vector<unsigned int> rows;
		while (columns)
		{
			rows.push_back(columns);
			columns = va_arg(args, unsigned int);
		}

		int height = -5;
		unsigned int row = 0;
		unsigned int rowHeight = 0;
		unsigned int columnsOccupied = 0;
		unsigned int rowColumns;

		if (m_pChildren && m_pChildren->count() > 0)
		{
            CCObject* pObject = NULL;
            CCARRAY_FOREACH(m_pChildren, pObject)
            {
                CCNode* pChild = dynamic_cast<CCNode*>(pObject);
                if (pChild)
                {
                    CCAssert(row < rows.size(), "");

				    rowColumns = rows[row];
				    // can not have zero columns on a row
				    CCAssert(rowColumns, "");

				    float tmp = pChild->getContentSize().height;
				    rowHeight = (unsigned int)((rowHeight >= tmp || isnan(tmp)) ? rowHeight : tmp);

				    ++columnsOccupied;
				    if (columnsOccupied >= rowColumns)
				    {
					    height += rowHeight + 5;

					    columnsOccupied = 0;
					    rowHeight = 0;
					    ++row;
				    }
                }
            }
		}

		// check if too many rows/columns for available menu items
		CCAssert(! columnsOccupied, "");

		CCSize winSize = CCDirector::sharedDirector()->getWinSize();

		row = 0;
		rowHeight = 0;
		rowColumns = 0;
		float w = 0.0;
		float x = 0.0;
		float y = (float)(height / 2);

		if (m_pChildren && m_pChildren->count() > 0)
		{
            CCObject* pObject = NULL;
            CCARRAY_FOREACH(m_pChildren, pObject)
            {
                CCNode* pChild = dynamic_cast<CCNode*>(pObject);
                if (pChild)
                {
				    if (rowColumns == 0)
				    {
					    rowColumns = rows[row];
					    w = winSize.width / (1 + rowColumns);
					    x = w;
				    }

				    float tmp = pChild->getContentSize().height;
				    rowHeight = (unsigned int)((rowHeight >= tmp || isnan(tmp)) ? rowHeight : tmp);

				    pChild->setPosition(ccp(x - winSize.width / 2,
					                       y - pChild->getContentSize().height / 2));

				    x += w;
				    ++columnsOccupied;

				    if (columnsOccupied >= rowColumns)
				    {
					    y -= rowHeight + 5;

					    columnsOccupied = 0;
					    rowColumns = 0;
					    rowHeight = 0;
					    ++row;
				    }
                }
            }
		}
	}

	void CCMenu::alignItemsInRows(unsigned int rows, ...)
	{
		va_list args;
		va_start(args, rows);

		this->alignItemsInRows(rows, args);

		va_end(args);
	}

	void CCMenu::alignItemsInRows(unsigned int rows, va_list args)
	{
		vector<unsigned int> columns;
		while (rows)
		{
			columns.push_back(rows);
			rows = va_arg(args, unsigned int);
		}

		vector<unsigned int> columnWidths;
		vector<unsigned int> columnHeights;

		int width = -10;
		int columnHeight = -5;
		unsigned int column = 0;
		unsigned int columnWidth = 0;
		unsigned int rowsOccupied = 0;
		unsigned int columnRows;

		if (m_pChildren && m_pChildren->count() > 0)
		{
            CCObject* pObject = NULL;
            CCARRAY_FOREACH(m_pChildren, pObject)
            {
                CCNode* pChild = dynamic_cast<CCNode*>(pObject);
                if (pChild)
                {
                    // check if too many menu items for the amount of rows/columns
				    CCAssert(column < columns.size(), "");

				    columnRows = columns[column];
				    // can't have zero rows on a column
				    CCAssert(columnRows, "");

				    // columnWidth = fmaxf(columnWidth, [item contentSize].width);
				    float tmp = pChild->getContentSize().width;
				    columnWidth = (unsigned int)((columnWidth >= tmp || isnan(tmp)) ? columnWidth : tmp);

				    columnHeight += (int)(pChild->getContentSize().height + 5);
				    ++rowsOccupied;

				    if (rowsOccupied >= columnRows)
				    {
					    columnWidths.push_back(columnWidth);
					    columnHeights.push_back(columnHeight);
					    width += columnWidth + 10;

					    rowsOccupied = 0;
					    columnWidth = 0;
					    columnHeight = -5;
					    ++column;
				    }
                }
            }
		}

		// check if too many rows/columns for available menu items.
		CCAssert(! rowsOccupied, "");

		CCSize winSize = CCDirector::sharedDirector()->getWinSize();

		column = 0;
		columnWidth = 0;
		columnRows = 0;
		float x = (float)(-width / 2);
		float y = 0.0;

        if (m_pChildren && m_pChildren->count() > 0)
		{
            CCObject* pObject = NULL;
            CCARRAY_FOREACH(m_pChildren, pObject)
            {
                CCNode* pChild = dynamic_cast<CCNode*>(pObject);
                if (pChild)
                {
                    if (columnRows == 0)
				    {
					    columnRows = columns[column];
					    y = (float) columnHeights[column];
				    }

				    // columnWidth = fmaxf(columnWidth, [item contentSize].width);
				    float tmp = pChild->getContentSize().width;
				    columnWidth = (unsigned int)((columnWidth >= tmp || isnan(tmp)) ? columnWidth : tmp);

				    pChild->setPosition(ccp(x + columnWidths[column] / 2,
					                       y - winSize.height / 2));

				    y -= pChild->getContentSize().height + 10;
				    ++rowsOccupied;

				    if (rowsOccupied >= columnRows)
				    {
					    x += columnWidth + 5;
					    rowsOccupied = 0;
					    columnRows = 0;
					    columnWidth = 0;
					    ++column;
				    }
                }
            }
		}
	}

	// Opacity Protocol

	/** Override synthesized setOpacity to recurse items */
	void CCMenu::setOpacity(GLubyte var)
	{
		m_cOpacity = var;

		if (m_pChildren && m_pChildren->count() > 0)
		{
            CCObject* pObject = NULL;
            CCARRAY_FOREACH(m_pChildren, pObject)
            {
                CCNode* pChild = dynamic_cast<CCNode*>(pObject);
                if (pChild)
                {
                    CCRGBAProtocol *pRGBAProtocol = dynamic_cast<CCRGBAProtocol*>(pChild);
				    if (pRGBAProtocol)
				    {
					    pRGBAProtocol->setOpacity(m_cOpacity);
				    }
                }
            }
		}
	}

	GLubyte CCMenu::getOpacity(void)
	{
		return m_cOpacity;
	}

	void CCMenu::setColor(const ccColor3B& var)
	{
		m_tColor = var;

		if (m_pChildren && m_pChildren->count() > 0)
		{
            CCObject* pObject = NULL;
            CCARRAY_FOREACH(m_pChildren, pObject)
            {
                CCNode* pChild = dynamic_cast<CCNode*>(pObject);
                if (pChild)
                {
                    CCRGBAProtocol *pRGBAProtocol = dynamic_cast<CCRGBAProtocol*>(pChild);
                    if (pRGBAProtocol)
                    {
                    	pRGBAProtocol->setColor(m_tColor);
                    }
                }
            }
		}
	}

	const ccColor3B& CCMenu::getColor(void)
	{
		return m_tColor;
	}

	CCMenuItem* CCMenu::itemForTouch(CCTouch *touch)
	{
		CCPoint touchLocation = touch->locationInView();
		touchLocation = CCDirector::sharedDirector()->convertToGL(touchLocation);

        if (m_pChildren && m_pChildren->count() > 0)
		{
            CCObject* pObject = NULL;
            CCARRAY_FOREACH(m_pChildren, pObject)
            {
                CCNode* pChild = dynamic_cast<CCNode*>(pObject);
                if (pChild && pChild->getIsVisible() && ((CCMenuItem*)pChild)->getIsEnabled())
                {
                    CCPoint local = pChild->convertToNodeSpace(touchLocation);
					CCRect r = ((CCMenuItem*)pChild)->rect();
					r.origin = CCPointZero;

					if (CCRect::CCRectContainsPoint(r, local))
					{
						return (CCMenuItem*)pChild;
					}
                }
            }

		}

		return NULL;
	}

}

license: mit


code:
/****************************************************************************
Copyright (c) 2010-2011 cocos2d-x.org
Copyright (c) 2008-2011 Ricardo Quesada
Copyright (c) 2011      Zynga Inc.

http://www.cocos2d-x.org

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 <cstring>
#include "CCMenuItem.h"
#include "CCPointExtension.h"
#include "CCActionInterval.h"
#include "CCSprite.h"
#include "CCLabelAtlas.h"
#include "CCLabelTTF.h"
#include "CCScriptSupport.h"
#include <stdarg.h>

namespace cocos2d{

    static unsigned int _fontSize = kCCItemSize;
    static std::string _fontName = "Marker Felt";
    static bool _fontNameRelease = false;

    const unsigned int	kCurrentItem = 0xc0c05001;
    const unsigned int	kZoomActionTag = 0xc0c05002;

    const unsigned int	kNormalTag = 0x1;
    const unsigned int	kSelectedTag = 0x2;
    const unsigned int	kDisableTag = 0x3;
    //
    // CCMenuItem
    //
    CCMenuItem * CCMenuItem::itemWithTarget(CCObject *rec, SEL_MenuHandler selector)
    {
        CCMenuItem *pRet = new CCMenuItem();
        pRet->initWithTarget(rec, selector);
        pRet->autorelease();
        return pRet;
    }
    bool CCMenuItem::initWithTarget(CCObject *rec, SEL_MenuHandler selector)
    {
        setAnchorPoint(ccp(0.5f, 0.5f));
        m_pListener = rec;
        m_pfnSelector = selector;
        m_bIsEnabled = true;
        m_bIsSelected = false;
        return true;
    }

    CCMenuItem::~CCMenuItem()
    {
        unregisterScriptHandler();
    }

    void CCMenuItem::selected()
    {
        m_bIsSelected = true;
    }

    void CCMenuItem::unselected()
    {
        m_bIsSelected = false;
    }

    void CCMenuItem::registerScriptHandler(int nHandler)
    {
        unregisterScriptHandler();
        m_nScriptHandler = nHandler;
        LUALOG("[LUA] Add CCMenuItem script handler: %d", m_nScriptHandler);
    }

    void CCMenuItem::unregisterScriptHandler(void)
    {
        if (m_nScriptHandler)
        {
            CCScriptEngineManager::sharedManager()->getScriptEngine()->removeLuaHandler(m_nScriptHandler);
            LUALOG("[LUA] Remove CCMenuItem script handler: %d", m_nScriptHandler);
            m_nScriptHandler = 0;
        }
    }

    void CCMenuItem::activate()
    {
        if (m_bIsEnabled)
        {
            if (m_pListener && m_pfnSelector)
            {
                (m_pListener->*m_pfnSelector)(this);
            }

            if (m_nScriptHandler)
            {
                CCScriptEngineManager::sharedManager()->getScriptEngine()->executeFunctionWithIntegerData(m_nScriptHandler, getTag());
            }
        }
    }

    void CCMenuItem::setIsEnabled(bool enabled)
    {
        m_bIsEnabled = enabled;
    }

    bool CCMenuItem::getIsEnabled()
    {
        return m_bIsEnabled;
    }

    CCRect CCMenuItem::rect()
    {
        return CCRectMake( m_tPosition.x - m_tContentSize.width * m_tAnchorPoint.x,
                          m_tPosition.y - m_tContentSize.height * m_tAnchorPoint.y,
                          m_tContentSize.width, m_tContentSize.height);
    }

    bool CCMenuItem::getIsSelected()
    {
        return m_bIsSelected;
    }

    void CCMenuItem::setTarget(CCObject *rec, SEL_MenuHandler selector)
    {
        m_pListener = rec;
        m_pfnSelector = selector;
    }

    //
    //CCMenuItemLabel
    //
    const ccColor3B& CCMenuItemLabel::getDisabledColor()
    {
        return m_tDisabledColor;
    }
    void CCMenuItemLabel::setDisabledColor(const ccColor3B& var)
    {
        m_tDisabledColor = var;
    }
    CCNode *CCMenuItemLabel::getLabel()
    {
        return m_pLabel;
    }
    void CCMenuItemLabel::setLabel(CCNode* var)
    {
        if (var)
        {
            addChild(var);
            var->setAnchorPoint(ccp(0, 0));
            setContentSize(var->getContentSize());
        }

        if (m_pLabel)
        {
            removeChild(m_pLabel, true);
        }

        m_pLabel = var;
    }
    CCMenuItemLabel * CCMenuItemLabel::itemWithLabel(CCNode*label, CCObject* target, SEL_MenuHandler selector)
    {
        CCMenuItemLabel *pRet = new CCMenuItemLabel();
        pRet->initWithLabel(label, target, selector);
        pRet->autorelease();
        return pRet;
    }
    CCMenuItemLabel* CCMenuItemLabel::itemWithLabel(CCNode *label)
    {
        CCMenuItemLabel *pRet = new CCMenuItemLabel();
        pRet->initWithLabel(label, NULL, NULL);
        pRet->autorelease();
        return pRet;
    }
    bool CCMenuItemLabel::initWithLabel(CCNode* label, CCObject* target, SEL_MenuHandler selector)
    {
        CCMenuItem::initWithTarget(target, selector);
        m_fOriginalScale = 1.0f;
        m_tColorBackup = ccWHITE;
        m_tDisabledColor = ccc3(126,126,126);
        this->setLabel(label);
        return true;
    }
    CCMenuItemLabel::~CCMenuItemLabel()
    {
    }
    void CCMenuItemLabel::setString(const char * label)
    {
        dynamic_cast<CCLabelProtocol*>(m_pLabel)->setString(label);
        this->setContentSize(m_pLabel->getContentSize());
    }
    void CCMenuItemLabel::activate()
    {
        if(m_bIsEnabled)
        {
            this->stopAllActions();
            this->setScale( m_fOriginalScale );
            CCMenuItem::activate();
        }
    }
    void CCMenuItemLabel::selected()
    {
        // subclass to change the default action
        if(m_bIsEnabled)
        {
            CCMenuItem::selected();

            CCAction *action = getActionByTag(kZoomActionTag);
            if (action)
            {
                this->stopAction(action);
            }
            else
            {
                m_fOriginalScale = this->getScale();
            }

            CCAction *zoomAction = CCScaleTo::actionWithDuration(0.1f, m_fOriginalScale * 1.2f);
            zoomAction->setTag(kZoomActionTag);
            this->runAction(zoomAction);
        }
    }
    void CCMenuItemLabel::unselected()
    {
        // subclass to change the default action
        if(m_bIsEnabled)
        {
            CCMenuItem::unselected();
            this->stopActionByTag(kZoomActionTag);
            CCAction *zoomAction = CCScaleTo::actionWithDuration(0.1f, m_fOriginalScale);
            zoomAction->setTag(kZoomActionTag);
            this->runAction(zoomAction);
        }
    }
    void CCMenuItemLabel::setIsEnabled(bool enabled)
    {
        if( m_bIsEnabled != enabled )
        {
            if(enabled == false)
            {
                m_tColorBackup = dynamic_cast<CCRGBAProtocol*>(m_pLabel)->getColor();
                dynamic_cast<CCRGBAProtocol*>(m_pLabel)->setColor(m_tDisabledColor);
            }
            else
            {
                dynamic_cast<CCRGBAProtocol*>(m_pLabel)->setColor(m_tColorBackup);
            }
        }
        CCMenuItem::setIsEnabled(enabled);
    }
    void CCMenuItemLabel::setOpacity(GLubyte opacity)
    {
        dynamic_cast<CCRGBAProtocol*>(m_pLabel)->setOpacity(opacity);
    }
    GLubyte CCMenuItemLabel::getOpacity()
    {
        return dynamic_cast<CCRGBAProtocol*>(m_pLabel)->getOpacity();
    }
    void CCMenuItemLabel::setColor(const ccColor3B& color)
    {
        dynamic_cast<CCRGBAProtocol*>(m_pLabel)->setColor(color);
    }
    const ccColor3B& CCMenuItemLabel::getColor()
    {
        return dynamic_cast<CCRGBAProtocol*>(m_pLabel)->getColor();
    }

    //
    //CCMenuItemAtlasFont
    //
    CCMenuItemAtlasFont * CCMenuItemAtlasFont::itemFromString(const char *value, const char *charMapFile, int itemWidth, int itemHeight, char startCharMap)
    {
        return CCMenuItemAtlasFont::itemFromString(value, charMapFile, itemWidth, itemHeight, startCharMap, NULL, NULL);
    }

    CCMenuItemAtlasFont * CCMenuItemAtlasFont::itemFromString(const char *value, const char *charMapFile, int itemWidth, int itemHeight, char startCharMap, CCObject* target, SEL_MenuHandler selector)
    {
        CCMenuItemAtlasFont *pRet = new CCMenuItemAtlasFont();
        pRet->initFromString(value, charMapFile, itemWidth, itemHeight, startCharMap, target, selector);
        pRet->autorelease();
        return pRet;
    }
    bool CCMenuItemAtlasFont::initFromString(const char *value, const char *charMapFile, int itemWidth, int itemHeight, char startCharMap, CCObject* target, SEL_MenuHandler selector)
    {
        CCAssert( value != NULL && strlen(value) != 0, "value length must be greater than 0");
        CCLabelAtlas *label = new CCLabelAtlas();
        label->initWithString(value, charMapFile, itemWidth, itemHeight, startCharMap);
        label->autorelease();
        if (CCMenuItemLabel::initWithLabel(label, target, selector))
        {
            // do something ?
        }
        return true;
    }
    //
    //CCMenuItemFont
    //
    void CCMenuItemFont::setFontSize(unsigned int s)
    {
        _fontSize = s;
    }
    unsigned int CCMenuItemFont::fontSize()
    {
        return _fontSize;
    }
    void CCMenuItemFont::setFontName(const char *name)
    {
        if( _fontNameRelease )
        {
            _fontName.clear();
        }
        _fontName = name;
        _fontNameRelease = true;
    }
    const char * CCMenuItemFont::fontName()
    {
        return _fontName.c_str();
    }
    CCMenuItemFont * CCMenuItemFont::itemFromString(const char *value, CCObject* target, SEL_MenuHandler selector)
    {
        CCMenuItemFont *pRet = new CCMenuItemFont();
        pRet->initFromString(value, target, selector);
        pRet->autorelease();
        return pRet;
    }
    CCMenuItemFont * CCMenuItemFont::itemFromString(const char *value)
    {
        CCMenuItemFont *pRet = new CCMenuItemFont();
        pRet->initFromString(value, NULL, NULL);
        pRet->autorelease();
        return pRet;
    }
    bool CCMenuItemFont::initFromString(const char *value, CCObject* target, SEL_MenuHandler selector)
    {
        CCAssert( value != NULL && strlen(value) != 0, "Value length must be greater than 0");

        m_strFontName = _fontName;
        m_uFontSize = _fontSize;

        CCLabelTTF *label = CCLabelTTF::labelWithString(value, m_strFontName.c_str(), (float)m_uFontSize);
        if (CCMenuItemLabel::initWithLabel(label, target, selector))
        {
            // do something ?
        }
        return true;
    }

    void CCMenuItemFont::recreateLabel()
    {
        CCLabelTTF *label = CCLabelTTF::labelWithString(dynamic_cast<CCLabelProtocol*>(m_pLabel)->getString(),
                                                        m_strFontName.c_str(), (float)m_uFontSize);
        this->setLabel(label);
    }

    void CCMenuItemFont::setFontSizeObj(unsigned int s)
    {
        m_uFontSize = s;
        recreateLabel();
    }

    unsigned int CCMenuItemFont::fontSizeObj()
    {
        return m_uFontSize;
    }

    void CCMenuItemFont::setFontNameObj(const char* name)
    {
        m_strFontName = name;
        recreateLabel();
    }

    const char* CCMenuItemFont::fontNameObj()
    {
        return m_strFontName.c_str();
    }

    //
    //CCMenuItemSprite
    //
    CCNode * CCMenuItemSprite::getNormalImage()
    {
        return m_pNormalImage;
    }
    void CCMenuItemSprite::setNormalImage(CCNode* var)
    {
        if (var)
        {
            addChild(var, 0, kNormalTag);
            var->setAnchorPoint(ccp(0, 0));
            var->setIsVisible(true);
        }

        if (m_pNormalImage)
        {
            removeChild(m_pNormalImage, true);
        }

        m_pNormalImage = var;
    }
    CCNode * CCMenuItemSprite::getSelectedImage()
    {
        return m_pSelectedImage;
    }
    void CCMenuItemSprite::setSelectedImage(CCNode* var)
    {
        if (var)
        {
            addChild(var, 0, kSelectedTag);
            var->setAnchorPoint(ccp(0, 0));
            var->setIsVisible(false);
        }

        if (m_pSelectedImage)
        {
            removeChild(m_pSelectedImage, true);
        }

        m_pSelectedImage = var;
    }
    CCNode * CCMenuItemSprite::getDisabledImage()
    {
        return m_pDisabledImage;
    }
    void CCMenuItemSprite::setDisabledImage(CCNode* var)
    {
        if (var)
        {
            addChild(var, 0, kDisableTag);
            var->setAnchorPoint(ccp(0, 0));
            var->setIsVisible(false);
        }

        if (m_pDisabledImage)
        {
            removeChild(m_pDisabledImage, true);
        }

        m_pDisabledImage = var;
    }
    //
    //CCMenuItemSprite - CCRGBAProtocol protocol
    //
    void CCMenuItemSprite::setOpacity(GLubyte opacity)
    {
        dynamic_cast<CCRGBAProtocol*>(m_pNormalImage)->setOpacity(opacity);

        if (m_pSelectedImage)
        {
            dynamic_cast<CCRGBAProtocol*>(m_pSelectedImage)->setOpacity(opacity);
        }

        if (m_pDisabledImage)
        {
            dynamic_cast<CCRGBAProtocol*>(m_pDisabledImage)->setOpacity(opacity);
        }
    }
    void CCMenuItemSprite::setColor(const ccColor3B& color)
    {
        dynamic_cast<CCRGBAProtocol*>(m_pNormalImage)->setColor(color);

        if (m_pSelectedImage)
        {
            dynamic_cast<CCRGBAProtocol*>(m_pSelectedImage)->setColor(color);
        }

        if (m_pDisabledImage)
        {
            dynamic_cast<CCRGBAProtocol*>(m_pDisabledImage)->setColor(color);
        }
    }
    GLubyte CCMenuItemSprite::getOpacity()
    {
        return dynamic_cast<CCRGBAProtocol*>(m_pNormalImage)->getOpacity();
    }
    const ccColor3B& CCMenuItemSprite::getColor()
    {
        return dynamic_cast<CCRGBAProtocol*>(m_pNormalImage)->getColor();
    }
    CCMenuItemSprite * CCMenuItemSprite::itemFromNormalSprite(CCNode* normalSprite, CCNode* selectedSprite, CCNode* disabledSprite)
    {
        return CCMenuItemSprite::itemFromNormalSprite(normalSprite, selectedSprite, disabledSprite, NULL, NULL);
    }
    CCMenuItemSprite * CCMenuItemSprite::itemFromNormalSprite(CCNode* normalSprite, CCNode* selectedSprite, CCObject* target, SEL_MenuHandler selector)
    {
        return CCMenuItemSprite::itemFromNormalSprite(normalSprite, selectedSprite, NULL, target, selector);
    }
    CCMenuItemSprite * CCMenuItemSprite::itemFromNormalSprite(CCNode *normalSprite, CCNode *selectedSprite, CCNode *disabledSprite, CCObject *target, SEL_MenuHandler selector)
    {
        CCMenuItemSprite *pRet = new CCMenuItemSprite();
        pRet->initFromNormalSprite(normalSprite, selectedSprite, disabledSprite, target, selector);
        pRet->autorelease();
        return pRet;
    }
    bool CCMenuItemSprite::initFromNormalSprite(CCNode* normalSprite, CCNode* selectedSprite, CCNode* disabledSprite, CCObject* target, SEL_MenuHandler selector)
    {
        CCAssert(normalSprite != NULL, "");
        CCMenuItem::initWithTarget(target, selector);
        setNormalImage(normalSprite);
        setSelectedImage(selectedSprite);
        setDisabledImage(disabledSprite);

        this->setContentSize(m_pNormalImage->getContentSize());
        return true;
    }

    /**
     @since v0.99.5
     */
    void CCMenuItemSprite::selected()
    {
        CCMenuItem::selected();

        if (m_pDisabledImage)
        {
            m_pDisabledImage->setIsVisible(false);
        }

        if (m_pSelectedImage)
        {
            m_pNormalImage->setIsVisible(false);
            m_pSelectedImage->setIsVisible(true);
        }
        else
        {
            m_pNormalImage->setIsVisible(true);
        }
    }

    void CCMenuItemSprite::unselected()
    {
        CCMenuItem::unselected();

        m_pNormalImage->setIsVisible(true);

        if (m_pSelectedImage)
        {
            m_pSelectedImage->setIsVisible(false);
        }

        if (m_pDisabledImage)
        {
            m_pDisabledImage->setIsVisible(false);
        }
    }

    void CCMenuItemSprite::setIsEnabled(bool bEnabled)
    {
        CCMenuItem::setIsEnabled(bEnabled);

        if (m_pSelectedImage)
        {
            m_pSelectedImage->setIsVisible(false);
        }

        if (bEnabled)
        {
            m_pNormalImage->setIsVisible(true);

            if (m_pDisabledImage)
            {
                m_pDisabledImage->setIsVisible(false);
            }
        }
        else
        {
            if (m_pDisabledImage)
            {
                m_pDisabledImage->setIsVisible(true);
                m_pNormalImage->setIsVisible(false);
            }
            else
            {
                m_pNormalImage->setIsVisible(true);
            }
        }
    }

    CCMenuItemImage * CCMenuItemImage::itemFromNormalImage(const char *normalImage, const char *selectedImage)
    {
        return CCMenuItemImage::itemFromNormalImage(normalImage, selectedImage, NULL, NULL, NULL);
    }
    CCMenuItemImage * CCMenuItemImage::itemFromNormalImage(const char *normalImage, const char *selectedImage, CCObject* target, SEL_MenuHandler selector)
    {
        return CCMenuItemImage::itemFromNormalImage(normalImage, selectedImage, NULL, target, selector);
    }
    CCMenuItemImage * CCMenuItemImage::itemFromNormalImage(const char *normalImage, const char *selectedImage, const char *disabledImage, CCObject* target, SEL_MenuHandler selector)
    {
        CCMenuItemImage *pRet = new CCMenuItemImage();
        if (pRet && pRet->initFromNormalImage(normalImage, selectedImage, disabledImage, target, selector))
        {
            pRet->autorelease();
            return pRet;
        }
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
    CCMenuItemImage * CCMenuItemImage::itemFromNormalImage(const char *normalImage, const char *selectedImage, const char *disabledImage)
    {
        CCMenuItemImage *pRet = new CCMenuItemImage();
        if (pRet && pRet->initFromNormalImage(normalImage, selectedImage, disabledImage, NULL, NULL))
        {
            pRet->autorelease();
            return pRet;
        }
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
    bool CCMenuItemImage::initFromNormalImage(const char *normalImage, const char *selectedImage, const char *disabledImage, CCObject* target, SEL_MenuHandler selector)
    {
        CCNode *normalSprite = CCSprite::spriteWithFile(normalImage);
        CCNode *selectedSprite = NULL;
        CCNode *disabledSprite = NULL;

        if (selectedImage)
        {
            selectedSprite = CCSprite::spriteWithFile(selectedImage);
        }

        if(disabledImage)
        {
            disabledSprite = CCSprite::spriteWithFile(disabledImage);
        }
        return initFromNormalSprite(normalSprite, selectedSprite, disabledSprite, target, selector);
    }
    //
    // MenuItemToggle
    //
    void CCMenuItemToggle::setSubItems(CCMutableArray<CCMenuItem*>* var)
    {
        CC_SAFE_RETAIN(var);
        CC_SAFE_RELEASE(m_pSubItems);
        m_pSubItems = var;
    }
    CCMutableArray<CCMenuItem*> *CCMenuItemToggle::getSubItems()
    {
        return m_pSubItems;
    }
    CCMenuItemToggle * CCMenuItemToggle::itemWithTarget(CCObject* target, SEL_MenuHandler selector, CCMenuItem* item, ...)
    {
        va_list args;
        va_start(args, item);
        CCMenuItemToggle *pRet = new CCMenuItemToggle();
        pRet->initWithTarget(target, selector, item, args);
        pRet->autorelease();
        va_end(args);
        return pRet;
    }
    bool CCMenuItemToggle::initWithTarget(CCObject* target, SEL_MenuHandler selector, CCMenuItem* item, va_list args)
    {
        CCMenuItem::initWithTarget(target, selector);
        this->m_pSubItems = new CCMutableArray<CCMenuItem*>();
        int z = 0;
        CCMenuItem *i = item;
        while(i)
        {
            z++;
            m_pSubItems->addObject(i);
            i = va_arg(args, CCMenuItem*);
        }
        m_uSelectedIndex = UINT_MAX;
        this->setSelectedIndex(0);
        return true;
    }

    CCMenuItemToggle* CCMenuItemToggle::itemWithItem(CCMenuItem *item)
    {
        CCMenuItemToggle *pRet = new CCMenuItemToggle();
        pRet->initWithItem(item);
        pRet->autorelease();
        return pRet;
    }

    bool CCMenuItemToggle::initWithItem(CCMenuItem *item)
    {
        CCMenuItem::initWithTarget(NULL, NULL);
        this->m_pSubItems = new CCMutableArray<CCMenuItem*>();
        m_pSubItems->addObject(item);
        m_uSelectedIndex = UINT_MAX;
        this->setSelectedIndex(0);
        return true;
    }

    void CCMenuItemToggle::addSubItem(CCMenuItem *item)
    {
        m_pSubItems->addObject(item);
    }

    CCMenuItemToggle::~CCMenuItemToggle()
    {
        CC_SAFE_RELEASE(m_pSubItems);
    }
    void CCMenuItemToggle::setSelectedIndex(unsigned int index)
    {
        if( index != m_uSelectedIndex )
        {
            m_uSelectedIndex = index;
            this->removeChildByTag(kCurrentItem, false);
            CCMenuItem *item = m_pSubItems->getObjectAtIndex(m_uSelectedIndex);
            this->addChild(item, 0, kCurrentItem);
            const CCSize& s = item->getContentSize();
            this->setContentSize(s);
            item->setPosition( ccp( s.width/2, s.height/2 ) );
        }
    }
    unsigned int CCMenuItemToggle::getSelectedIndex()
    {
        return m_uSelectedIndex;
    }
    void CCMenuItemToggle::selected()
    {
        CCMenuItem::selected();
        m_pSubItems->getObjectAtIndex(m_uSelectedIndex)->selected();
    }
    void CCMenuItemToggle::unselected()
    {
        CCMenuItem::unselected();
        m_pSubItems->getObjectAtIndex(m_uSelectedIndex)->unselected();
    }
    void CCMenuItemToggle::activate()
    {
        // update index
        if( m_bIsEnabled )
        {
            unsigned int newIndex = (m_uSelectedIndex + 1) % m_pSubItems->count();
            this->setSelectedIndex(newIndex);
        }
        CCMenuItem::activate();
    }
    void CCMenuItemToggle::setIsEnabled(bool enabled)
    {
        CCMenuItem::setIsEnabled(enabled);

        if(m_pSubItems && m_pSubItems->count() > 0)
        {
            CCMutableArray<CCMenuItem*>::CCMutableArrayIterator it;
            for( it = m_pSubItems->begin(); it != m_pSubItems->end(); ++it)
            {
                (*it)->setIsEnabled(enabled);
            }
        }
    }
    CCMenuItem * CCMenuItemToggle::selectedItem()
    {
        return m_pSubItems->getObjectAtIndex(m_uSelectedIndex);
    }
    //
    //CCMenuItemToggle - CCRGBAProtocol protocol
    //
    GLubyte CCMenuItemToggle::getOpacity()
    {
        return m_cOpacity;
    }
    void CCMenuItemToggle::setOpacity(GLubyte opacity)
    {
        m_cOpacity = opacity;
        if(m_pSubItems && m_pSubItems->count() > 0)
        {
            CCMutableArray<CCMenuItem*>::CCMutableArrayIterator it;
            for( it = m_pSubItems->begin(); it != m_pSubItems->end(); ++it)
            {
                dynamic_cast<CCRGBAProtocol*>(*it)->setOpacity(opacity);
            }
        }
    }
    const ccColor3B& CCMenuItemToggle::getColor()
    {
        return m_tColor;
    }
    void CCMenuItemToggle::setColor(const ccColor3B& color)
    {
        m_tColor = color;
        if(m_pSubItems && m_pSubItems->count() > 0)
        {
            CCMutableArray<CCMenuItem*>::CCMutableArrayIterator it;
            for( it = m_pSubItems->begin(); it != m_pSubItems->end(); ++it)
            {
                dynamic_cast<CCRGBAProtocol*>(*it)->setColor(color);
            }
        }
    }

} // namespace cocos2d


code:
/****************************************************************************
Copyright (c) 2010-2011 cocos2d-x.org
Copyright (c) 2008-2009 Jason Booth

http://www.cocos2d-x.org

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 "CCMotionStreak.h"
#include "CCPointExtension.h"
#include "CCRibbon.h"
namespace cocos2d {

/*
 * Motion Streak manages a Ribbon based on it's motion in absolute space.
 * You construct it with a fadeTime, minimum segment size, texture path, texture
 * length and color. The fadeTime controls how long it takes each vertex in
 * the streak to fade out, the minimum segment size it how many pixels the
 * streak will move before adding a new ribbon segement, and the texture
 * length is the how many pixels the texture is stretched across. The texture
 * is vertically aligned along the streak segemnts.
 */
//implementation CCMotionStreak

CCMotionStreak * CCMotionStreak::streakWithFade(float fade, float seg, const char *imagePath, float width, float length, const ccColor4B& color)
{
	CCMotionStreak *pRet = new CCMotionStreak();
	if(pRet && pRet->initWithFade(fade, seg, imagePath, width, length, color))
	{
		pRet->autorelease();
		return pRet;
	}
	CC_SAFE_DELETE(pRet)
	return NULL;
}

bool CCMotionStreak::initWithFade(float fade, float seg, const char *imagePath, float width, float length, const ccColor4B& color)
{
	m_fSegThreshold = seg;
	m_fWidth = width;
	m_tLastLocation = CCPointZero;
	m_pRibbon = CCRibbon::ribbonWithWidth(m_fWidth, imagePath, length, color, fade);
	this->addChild(m_pRibbon);

	// update ribbon position. Use schedule:interval and not scheduleUpdated. (cocos2d-iphone)issue #1075
	this->schedule(schedule_selector(CCMotionStreak::update), 0);
	return true;
}

void CCMotionStreak::update(ccTime delta)
{
	CCPoint location = this->convertToWorldSpace(CCPointZero);
	m_pRibbon->setPosition(ccp(-1*location.x, -1*location.y));
    float len = ccpLength(ccpSub(m_tLastLocation, location));
	if (len > m_fSegThreshold)
	{
		m_pRibbon->addPointAt(location, m_fWidth);
		m_tLastLocation = location;
	}
	m_pRibbon->update(delta);
}

//MotionStreak - CocosNodeTexture protocol

void CCMotionStreak::setTexture(CCTexture2D* texture)
{
	m_pRibbon->setTexture(texture);
}

CCTexture2D * CCMotionStreak::getTexture()
{
	return m_pRibbon->getTexture();
}

ccBlendFunc CCMotionStreak::getBlendFunc()
{
	return m_pRibbon->getBlendFunc();
}

void CCMotionStreak::setBlendFunc(ccBlendFunc blendFunc)
{
	m_pRibbon->setBlendFunc(blendFunc);
}

CCRibbon * CCMotionStreak::getRibbon()
{
	return m_pRibbon;
}

}// namespace cocos2d


code:
/****************************************************************************
Copyright (c) 2010-2011 cocos2d-x.org
Copyright (c) 2010      Lam Pham

http://www.cocos2d-x.org

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 "CCProgressTimer.h"

#include "ccMacros.h"
#include "CCTextureCache.h"
#include "CCPointExtension.h"

#include <float.h>

namespace cocos2d {

#define kProgressTextureCoordsCount 4
const char kProgressTextureCoords = 0x1e;

CCProgressTimer* CCProgressTimer::progressWithFile(const char *pszFileName)
{
	CCProgressTimer *pProgressTimer = new CCProgressTimer();
	if (pProgressTimer->initWithFile(pszFileName))
	{
		pProgressTimer->autorelease();
	}
	else
	{
        delete pProgressTimer;
		pProgressTimer = NULL;
	}

	return pProgressTimer;
}

bool CCProgressTimer::initWithFile(const char *pszFileName)
{
	return this->initWithTexture(CCTextureCache::sharedTextureCache()->addImage(pszFileName));
}

CCProgressTimer* CCProgressTimer::progressWithTexture(CCTexture2D *pTexture)
{
	CCProgressTimer *pProgressTimer = new CCProgressTimer();
	if (pProgressTimer->initWithTexture(pTexture))
	{
		pProgressTimer->autorelease();
	}
	else
	{
        delete pProgressTimer;
		pProgressTimer = NULL;
	}

	return pProgressTimer;
}

bool CCProgressTimer::initWithTexture(CCTexture2D *pTexture)
{
	m_pSprite = CCSprite::spriteWithTexture(pTexture);
	CC_SAFE_RETAIN(m_pSprite);
	m_fPercentage = 0.f;
	m_pVertexData = NULL;
    m_nVertexDataCount = 0;
	setAnchorPoint(ccp(0.5f, 0.5f));
	setContentSize(m_pSprite->getContentSize());
    m_eType = kCCProgressTimerTypeRadialCCW;

	return true;
}

CCProgressTimer::~CCProgressTimer(void)
{
	CC_SAFE_DELETE_ARRAY(m_pVertexData);
	CC_SAFE_RELEASE(m_pSprite);
}

void CCProgressTimer::setPercentage(float fPercentage)
{
	if (m_fPercentage != fPercentage)
	{
		m_fPercentage = clampf(fPercentage, 0, 100);
		updateProgress();
	}
}

void CCProgressTimer::setSprite(CCSprite *pSprite)
{
	if (m_pSprite != pSprite)
	{
		CC_SAFE_RETAIN(pSprite);
		CC_SAFE_RELEASE(m_pSprite);
		m_pSprite = pSprite;
        setContentSize(m_pSprite->getContentSize());

		//	Everytime we set a new sprite, we free the current vertex data
		if (m_pVertexData)
		{
			delete[] m_pVertexData;
			m_pVertexData = NULL;
			m_nVertexDataCount = 0;
		}
	}
}

void CCProgressTimer::setType(CCProgressTimerType type)
{
	if (type != m_eType)
	{
		//	release all previous information
		if (m_pVertexData)
		{
			delete[] m_pVertexData;
			m_pVertexData = NULL;
			m_nVertexDataCount = 0;
		}

		m_eType = type;
	}
}

// Interval

///
//	@returns the vertex position from the texture coordinate
///
ccVertex2F CCProgressTimer::vertexFromTexCoord(const CCPoint& texCoord)
{
    CCPoint tmp;
    ccVertex2F ret;

    CCTexture2D *pTexture = m_pSprite->getTexture();
    if (pTexture)
    {
        float fXMax = MAX(m_pSprite->getQuad().br.texCoords.u, m_pSprite->getQuad().bl.texCoords.u);
        float fXMin = MIN(m_pSprite->getQuad().br.texCoords.u, m_pSprite->getQuad().bl.texCoords.u);
        float fYMax = MAX(m_pSprite->getQuad().tl.texCoords.v, m_pSprite->getQuad().bl.texCoords.v);
        float fYMin = MIN(m_pSprite->getQuad().tl.texCoords.v, m_pSprite->getQuad().bl.texCoords.v);
        CCPoint tMax = ccp(fXMax, fYMax);
        CCPoint tMin = ccp(fXMin, fYMin);

        CCSize texSize = CCSizeMake(m_pSprite->getQuad().br.vertices.x - m_pSprite->getQuad().bl.vertices.x,
                                    m_pSprite->getQuad().tl.vertices.y - m_pSprite->getQuad().bl.vertices.y);
        tmp = ccp(texSize.width * (texCoord.x - tMin.x) / (tMax.x - tMin.x),
                    texSize.height * (1 - (texCoord.y - tMin.y) / (tMax.y - tMin.y)));
	}
	else
	{
	    tmp = CCPointZero;
	}

    ret.x = tmp.x;
    ret.y = tmp.y;
    return ret;
}

void CCProgressTimer::updateColor(void)
{
	GLubyte op = m_pSprite->getOpacity();
	ccColor3B c3b = m_pSprite->getColor();

	ccColor4B color = {c3b.r, c3b.g, c3b.b, op};
	if (m_pSprite->getTexture()->getHasPremultipliedAlpha())
	{
		color.r *= op / 255;
		color.g *= op / 255;
		color.b *= op / 255;
	}

	if (m_pVertexData)
	{
		for (int i = 0; i < m_nVertexDataCount; ++i)
		{
			m_pVertexData[i].colors = color;
		}
	}
}

void CCProgressTimer::updateProgress(void)
{
	switch (m_eType)
	{
	case kCCProgressTimerTypeRadialCW:
	case kCCProgressTimerTypeRadialCCW:
        updateRadial();
		break;
	case kCCProgressTimerTypeHorizontalBarLR:
	case kCCProgressTimerTypeHorizontalBarRL:
	case kCCProgressTimerTypeVerticalBarBT:
	case kCCProgressTimerTypeVerticalBarTB:
		updateBar();
		break;
	default:
		break;
	}
}

///
//	Update does the work of mapping the texture onto the triangles
//	It now doesn't occur the cost of free/alloc data every update cycle.
//	It also only changes the percentage point but no other points if they have not
//	been modified.
//
//	It now deals with flipped texture. If you run into this problem, just use the
//	sprite property and enable the methods flipX, flipY.
///
void CCProgressTimer::updateRadial(void)
{
	//	Texture Max is the actual max coordinates to deal with non-power of 2 textures
    float fXMax = MAX(m_pSprite->getQuad().br.texCoords.u, m_pSprite->getQuad().bl.texCoords.u);
    float fXMin = MIN(m_pSprite->getQuad().br.texCoords.u, m_pSprite->getQuad().bl.texCoords.u);
    float fYMax = MAX(m_pSprite->getQuad().tl.texCoords.v, m_pSprite->getQuad().bl.texCoords.v);
    float fYMin = MIN(m_pSprite->getQuad().tl.texCoords.v, m_pSprite->getQuad().bl.texCoords.v);
    CCPoint tMax = ccp(fXMax, fYMax);
    CCPoint tMin = ccp(fXMin, fYMin);

	//	Grab the midpoint
	CCPoint midpoint = ccpAdd(tMin, ccpCompMult(m_tAnchorPoint, ccpSub(tMax, tMin)));

	float alpha = m_fPercentage / 100.f;

	//	Otherwise we can get the angle from the alpha
	float angle = 2.f * ((float)M_PI) * (m_eType == kCCProgressTimerTypeRadialCW ? alpha : 1.f - alpha);

	//	We find the vector to do a hit detection based on the percentage
	//	We know the first vector is the one @ 12 o'clock (top,mid) so we rotate
	//	from that by the progress angle around the midpoint pivot
	CCPoint topMid = ccp(midpoint.x, tMin.y);
	CCPoint percentagePt = ccpRotateByAngle(topMid, midpoint, angle);

	int index = 0;
	CCPoint hit = CCPointZero;

	if (alpha == 0.f)
	{
		//	More efficient since we don't always need to check intersection
		//	If the alpha is zero then the hit point is top mid and the index is 0.
		hit = topMid;
		index = 0;
	} else
	if (alpha == 1.f)
	{
		//	More efficient since we don't always need to check intersection
		//	If the alpha is one then the hit point is top mid and the index is 4.
		hit = topMid;
		index = 4;
	}
	else
	{
		//	We run a for loop checking the edges of the texture to find the
		//	intersection point
		//	We loop through five points since the top is split in half

		float min_t = FLT_MAX;

		for (int i = 0; i <= kProgressTextureCoordsCount; ++i)
		{
			int pIndex = (i + (kProgressTextureCoordsCount - 1)) % kProgressTextureCoordsCount;

			CCPoint edgePtA = ccpAdd(tMin, ccpCompMult(boundaryTexCoord(i % kProgressTextureCoordsCount), ccpSub(tMax, tMin)));
			CCPoint edgePtB = ccpAdd(tMin, ccpCompMult(boundaryTexCoord(pIndex), ccpSub(tMax, tMin)));

			//	Remember that the top edge is split in half for the 12 o'clock position
			//	Let's deal with that here by finding the correct endpoints
			if (i == 0)
			{
				edgePtB = ccpLerp(edgePtA, edgePtB, 0.5f);
			} else
			if (i == 4)
			{
				edgePtA = ccpLerp(edgePtA, edgePtB, 0.5f);
			}

			//	s and t are returned by ccpLineIntersect
			float s = 0;
			float t = 0;
			if (ccpLineIntersect(edgePtA, edgePtB, midpoint, percentagePt, &s, &t))
			{
				//	Since our hit test is on rays we have to deal with the top edge
				//	being in split in half so we have to test as a segment
				if (i == 0 || i == 4)
				{
					//	s represents the point between edgePtA--edgePtB
					if (!(0.f <= s && s <= 1.f))
					{
						continue;
					}
				}

				//	As long as our t isn't negative we are at least finding a
				//	correct hitpoint from midpoint to percentagePt.
				if (t >= 0.f)
				{
					//	Because the percentage line and all the texture edges are
					//	rays we should only account for the shortest intersection
					if (t < min_t)
					{
						min_t = t;
						index = i;
					}
				}
			}

		}

        //	Now that we have the minimum magnitude we can use that to find our intersection
		hit = ccpAdd(midpoint, ccpMult(ccpSub(percentagePt, midpoint),min_t));
	}

    //	The size of the vertex data is the index from the hitpoint
	//	the 3 is for the midpoint, 12 o'clock point and hitpoint position.

	bool sameIndexCount = true;
	if (m_nVertexDataCount != index + 3)
	{
		sameIndexCount = false;
		if (m_pVertexData)
		{
			delete[] m_pVertexData;
			m_pVertexData = NULL;
			m_nVertexDataCount = 0;
		}
	}

	if (! m_pVertexData)
	{
		m_nVertexDataCount = index + 3;
		m_pVertexData = new ccV2F_C4B_T2F[m_nVertexDataCount];
		CCAssert(m_pVertexData, "");

		updateColor();
	}

	if (! sameIndexCount)
	{
		//	First we populate the array with the midpoint, then all
		//	vertices/texcoords/colors of the 12 'o clock start and edges and the hitpoint
		m_pVertexData[0].texCoords = tex2(midpoint.x, midpoint.y);
		m_pVertexData[0].vertices = vertexFromTexCoord(midpoint);

		m_pVertexData[1].texCoords = tex2(midpoint.x, tMin.y);
		m_pVertexData[1].vertices = vertexFromTexCoord(ccp(midpoint.x, tMin.y));

		for (int i = 0; i < index; ++i)
		{
			CCPoint texCoords = ccpAdd(tMin, ccpCompMult(boundaryTexCoord(i), ccpSub(tMax, tMin)));

			m_pVertexData[i+2].texCoords = tex2(texCoords.x, texCoords.y);
			m_pVertexData[i+2].vertices = vertexFromTexCoord(texCoords);
		}

		//	Flip the texture coordinates if set
		if (m_pSprite->isFlipX() || m_pSprite->isFlipY())
		{
			for (int i = 0; i < m_nVertexDataCount - 1; ++i)
			{
				if (m_pSprite->isFlipX())
				{
					m_pVertexData[i].texCoords.u = tMin.x + tMax.x - m_pVertexData[i].texCoords.u;
				}

				if (m_pSprite->isFlipY())
				{
					m_pVertexData[i].texCoords.v = tMin.y + tMax.y - m_pVertexData[i].texCoords.v;
				}
			}
		}
	}

	//	hitpoint will go last
	m_pVertexData[m_nVertexDataCount - 1].texCoords = tex2(hit.x, hit.y);
	m_pVertexData[m_nVertexDataCount - 1].vertices = vertexFromTexCoord(hit);

	if (m_pSprite->isFlipX() || m_pSprite->isFlipY())
	{
		if (m_pSprite->isFlipX())
		{
			m_pVertexData[m_nVertexDataCount - 1].texCoords.u = tMin.x + tMax.x - m_pVertexData[m_nVertexDataCount - 1].texCoords.u;
		}

        if (m_pSprite->isFlipY())
		{
			m_pVertexData[m_nVertexDataCount - 1].texCoords.v = tMin.y + tMax.y - m_pVertexData[m_nVertexDataCount - 1].texCoords.v;
		}
	}
}

///
//	Update does the work of mapping the texture onto the triangles for the bar
//	It now doesn't occur the cost of free/alloc data every update cycle.
//	It also only changes the percentage point but no other points if they have not
//	been modified.
//
//	It now deals with flipped texture. If you run into this problem, just use the
//	sprite property and enable the methods flipX, flipY.
///
void CCProgressTimer::updateBar(void)
{
	float alpha = m_fPercentage / 100.f;

    float fXMax = MAX(m_pSprite->getQuad().br.texCoords.u, m_pSprite->getQuad().bl.texCoords.u);
    float fXMin = MIN(m_pSprite->getQuad().br.texCoords.u, m_pSprite->getQuad().bl.texCoords.u);
    float fYMax = MAX(m_pSprite->getQuad().tl.texCoords.v, m_pSprite->getQuad().bl.texCoords.v);
    float fYMin = MIN(m_pSprite->getQuad().tl.texCoords.v, m_pSprite->getQuad().bl.texCoords.v);
    CCPoint tMax = ccp(fXMax, fYMax);
    CCPoint tMin = ccp(fXMin, fYMin);

	unsigned char vIndexes[2] = {0, 0};
	unsigned char index = 0;

	//	We know vertex data is always equal to the 4 corners
	//	If we don't have vertex data then we create it here and populate
	//	the side of the bar vertices that won't ever change.
	if (! m_pVertexData)
	{
		m_nVertexDataCount = kProgressTextureCoordsCount;
		m_pVertexData = new ccV2F_C4B_T2F[m_nVertexDataCount];
		CCAssert(m_pVertexData, "");

		if (m_eType == kCCProgressTimerTypeHorizontalBarLR)
		{
			m_pVertexData[vIndexes[0] = 0].texCoords = tex2(tMin.x, tMin.y);
			m_pVertexData[vIndexes[1] = 1].texCoords = tex2(tMin.x, tMax.y);
		} else
		if (m_eType == kCCProgressTimerTypeHorizontalBarRL)
		{
			m_pVertexData[vIndexes[0] = 2].texCoords = tex2(tMax.x, tMax.y);
			m_pVertexData[vIndexes[1] = 3].texCoords = tex2(tMax.x, tMin.y);
		} else
		if (m_eType == kCCProgressTimerTypeVerticalBarBT)
		{
			m_pVertexData[vIndexes[0] = 1].texCoords = tex2(tMin.x, tMax.y);
			m_pVertexData[vIndexes[1] = 3].texCoords = tex2(tMax.x, tMax.y);
		} else
		if (m_eType == kCCProgressTimerTypeVerticalBarTB)
		{
			m_pVertexData[vIndexes[0] = 0].texCoords = tex2(tMin.x, tMin.y);
			m_pVertexData[vIndexes[1] = 2].texCoords = tex2(tMax.x, tMin.y);
		}

		index = vIndexes[0];
		m_pVertexData[index].vertices = vertexFromTexCoord(ccp(m_pVertexData[index].texCoords.u,
			                                                   m_pVertexData[index].texCoords.v));

		index = vIndexes[1];
		m_pVertexData[index].vertices = vertexFromTexCoord(ccp(m_pVertexData[index].texCoords.u,
			                                                   m_pVertexData[index].texCoords.v));

		if (m_pSprite->isFlipY() || m_pSprite->isFlipX())
		{
			if (m_pSprite->isFlipX())
			{
				index = vIndexes[0];
				m_pVertexData[index].texCoords.u = tMin.x + tMax.x - m_pVertexData[index].texCoords.u;
				index = vIndexes[1];
				m_pVertexData[index].texCoords.u = tMin.x + tMax.x - m_pVertexData[index].texCoords.u;
			}

			if (m_pSprite->isFlipY())
			{
				index = vIndexes[0];
				m_pVertexData[index].texCoords.v = tMin.y + tMax.y - m_pVertexData[index].texCoords.v;
				index = vIndexes[1];
				m_pVertexData[index].texCoords.v = tMin.y + tMax.y - m_pVertexData[index].texCoords.v;
			}
		}

		updateColor();
	}

	if(m_eType == kCCProgressTimerTypeHorizontalBarLR)
	{
		m_pVertexData[vIndexes[0] = 3].texCoords = tex2(tMin.x + (tMax.x - tMin.x) *alpha, tMax.y);
		m_pVertexData[vIndexes[1] = 2].texCoords = tex2(tMin.x + (tMax.x - tMin.x) *alpha, tMin.y);
	} else
	if (m_eType == kCCProgressTimerTypeHorizontalBarRL)
	{
		m_pVertexData[vIndexes[0] = 1].texCoords = tex2(tMin.x + (tMax.x - tMin.x) * (1.f - alpha), tMin.y);
		m_pVertexData[vIndexes[1] = 0].texCoords = tex2(tMin.x + (tMax.x - tMin.x) * (1.f - alpha), tMax.y);
	} else
	if (m_eType == kCCProgressTimerTypeVerticalBarBT)
	{
		m_pVertexData[vIndexes[0] = 0].texCoords = tex2(tMin.x, tMin.y + (tMax.y - tMin.y) * (1.f - alpha));
		m_pVertexData[vIndexes[1] = 2].texCoords = tex2(tMax.x, tMin.y + (tMax.y - tMin.y) * (1.f - alpha));
	} else
	if (m_eType == kCCProgressTimerTypeVerticalBarTB)
	{
		m_pVertexData[vIndexes[0] = 1].texCoords = tex2(tMin.x, tMin.y + (tMax.y - tMin.y) * alpha);
		m_pVertexData[vIndexes[1] = 3].texCoords = tex2(tMax.x, tMin.y + (tMax.y - tMin.y) * alpha);
	}

	index = vIndexes[0];
	m_pVertexData[index].vertices = vertexFromTexCoord(ccp(m_pVertexData[index].texCoords.u,
		                                                   m_pVertexData[index].texCoords.v));
	index = vIndexes[1];
	m_pVertexData[index].vertices = vertexFromTexCoord(ccp(m_pVertexData[index].texCoords.u,
		                                                   m_pVertexData[index].texCoords.v));

	if (m_pSprite->isFlipY() || m_pSprite->isFlipX())
	{
		if (m_pSprite->isFlipX())
		{
			index = vIndexes[0];
			m_pVertexData[index].texCoords.u = tMin.x + tMax.x - m_pVertexData[index].texCoords.u;
			index = vIndexes[1];
			m_pVertexData[index].texCoords.u = tMin.x + tMax.x - m_pVertexData[index].texCoords.u;
		}

		if (m_pSprite->isFlipY())
		{
			index = vIndexes[0];
			m_pVertexData[index].texCoords.v = tMin.y + tMax.y - m_pVertexData[index].texCoords.v;
			index = vIndexes[1];
			m_pVertexData[index].texCoords.v = tMin.y + tMax.y - m_pVertexData[index].texCoords.v;
		}
	}
}

CCPoint CCProgressTimer::boundaryTexCoord(char index)
{
	if (index < kProgressTextureCoordsCount)
	{
		switch (m_eType)
		{
		case kCCProgressTimerTypeRadialCW:
			return ccp( (float)((kProgressTextureCoords>>((index<<1)+1))&1), (float)((kProgressTextureCoords>>(index<<1))&1) );
		case kCCProgressTimerTypeRadialCCW:
			return ccp( (float)((kProgressTextureCoords>>(7-(index<<1)))&1), (float)((kProgressTextureCoords>>(7-((index<<1)+1)))&1));
		default:
			break;
		}
	}

	return CCPointZero;
}

void CCProgressTimer::draw(void)
{
	CCNode::draw();

	if(! m_pVertexData)
	{
		return;
	}

	if(! m_pSprite)
	{
		return;
	}

	ccBlendFunc bf = m_pSprite->getBlendFunc();
    bool newBlend = (bf.src != CC_BLEND_SRC || bf.dst != CC_BLEND_DST) ? true : false;
	if (newBlend)
	{
		glBlendFunc(bf.src, bf.dst);
	}

	///	========================================================================
	//	Replaced [texture_ drawAtPoint:CCPointZero] with my own vertexData
	//	Everything above me and below me is copied from CCTextureNode's draw
	glBindTexture(GL_TEXTURE_2D, m_pSprite->getTexture()->getName());
	glVertexPointer(2, GL_FLOAT, sizeof(ccV2F_C4B_T2F), &m_pVertexData[0].vertices);
	glTexCoordPointer(2, GL_FLOAT, sizeof(ccV2F_C4B_T2F), &m_pVertexData[0].texCoords);
	glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ccV2F_C4B_T2F), &m_pVertexData[0].colors);

	if(m_eType == kCCProgressTimerTypeRadialCCW || m_eType == kCCProgressTimerTypeRadialCW)
	{
		glDrawArrays(GL_TRIANGLE_FAN, 0, m_nVertexDataCount);
	} else
	if (	m_eType == kCCProgressTimerTypeHorizontalBarLR ||
			m_eType == kCCProgressTimerTypeHorizontalBarRL ||
			m_eType == kCCProgressTimerTypeVerticalBarBT ||
			m_eType == kCCProgressTimerTypeVerticalBarTB)
	{
		glDrawArrays(GL_TRIANGLE_STRIP, 0, m_nVertexDataCount);
	}
	//glDrawElements(GL_TRIANGLES, indicesCount_, GL_UNSIGNED_BYTE, indices_);
	///	========================================================================

	if( newBlend )
    {
		glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
    }
}

} // namespace cocos2d


code:
/****************************************************************************
Copyright (c) 2010-2011 cocos2d-x.org
Copyright (c) 2009      Jason Booth

http://www.cocos2d-x.org

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 "CCConfiguration.h"
#include "CCRenderTexture.h"
#include "CCDirector.h"
#include "platform/platform.h"
#include "CCImage.h"
#include "support/ccUtils.h"
#include "CCTextureCache.h"
#include "CCFileUtils.h"
#include "CCGL.h"

namespace cocos2d {

// implementation CCRenderTexture
CCRenderTexture::CCRenderTexture()
: m_pSprite(NULL)
, m_uFBO(0)
, m_nOldFBO(0)
, m_pTexture(0)
, m_pUITextureImage(NULL)
, m_ePixelFormat(kCCTexture2DPixelFormat_RGBA8888)
{
}

CCRenderTexture::~CCRenderTexture()
{
    removeAllChildrenWithCleanup(true);
    ccglDeleteFramebuffers(1, &m_uFBO);

	CC_SAFE_DELETE(m_pUITextureImage);
}

CCSprite * CCRenderTexture::getSprite()
{
	return m_pSprite;
}
void CCRenderTexture::setSprite(CCSprite* var)
{
	m_pSprite = var;
}

CCRenderTexture * CCRenderTexture::renderTextureWithWidthAndHeight(int w, int h, CCTexture2DPixelFormat eFormat)
{
    CCRenderTexture *pRet = new CCRenderTexture();

    if(pRet && pRet->initWithWidthAndHeight(w, h, eFormat))
    {
        pRet->autorelease();
        return pRet;
    }
    CC_SAFE_DELETE(pRet);
    return NULL;
}

CCRenderTexture * CCRenderTexture::renderTextureWithWidthAndHeight(int w, int h)
{
	CCRenderTexture *pRet = new CCRenderTexture();

    if(pRet && pRet->initWithWidthAndHeight(w, h, kCCTexture2DPixelFormat_RGBA8888))
	{
		pRet->autorelease();
		return pRet;
	}
	CC_SAFE_DELETE(pRet)
	return NULL;
}

bool CCRenderTexture::initWithWidthAndHeight(int w, int h, CCTexture2DPixelFormat eFormat)
{
	// If the gles version is lower than GLES_VER_1_0,
	// some extended gles functions can't be implemented, so return false directly.
	if (CCConfiguration::sharedConfiguration()->getGlesVersion() <= GLES_VER_1_0)
	{
		return false;
	}

    bool bRet = false;
    do
    {
        w *= (int)CC_CONTENT_SCALE_FACTOR();
        h *= (int)CC_CONTENT_SCALE_FACTOR();

        glGetIntegerv(CC_GL_FRAMEBUFFER_BINDING, &m_nOldFBO);

        // textures must be power of two squared
        unsigned int powW = ccNextPOT(w);
        unsigned int powH = ccNextPOT(h);

        void *data = malloc((int)(powW * powH * 4));
        CC_BREAK_IF(! data);

        memset(data, 0, (int)(powW * powH * 4));
        m_ePixelFormat = eFormat;

        m_pTexture = new CCTexture2D();
        CC_BREAK_IF(! m_pTexture);

        m_pTexture->initWithData(data, (CCTexture2DPixelFormat)m_ePixelFormat, powW, powH, CCSizeMake((float)w, (float)h));
        free( data );

        // generate FBO
        ccglGenFramebuffers(1, &m_uFBO);
        ccglBindFramebuffer(CC_GL_FRAMEBUFFER, m_uFBO);

        // associate texture with FBO
        ccglFramebufferTexture2D(CC_GL_FRAMEBUFFER, CC_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_pTexture->getName(), 0);

        // check if it worked (probably worth doing :) )
        GLuint status = ccglCheckFramebufferStatus(CC_GL_FRAMEBUFFER);
        if (status != CC_GL_FRAMEBUFFER_COMPLETE)
        {
            CCAssert(0, "Render Texture : Could not attach texture to framebuffer");
            CC_SAFE_DELETE(m_pTexture);
            break;
        }

        m_pTexture->setAliasTexParameters();

        m_pSprite = CCSprite::spriteWithTexture(m_pTexture);

        m_pTexture->release();
        m_pSprite->setScaleY(-1);
        this->addChild(m_pSprite);

        ccBlendFunc tBlendFunc = {GL_ONE, GL_ONE_MINUS_SRC_ALPHA };
        m_pSprite->setBlendFunc(tBlendFunc);

        ccglBindFramebuffer(CC_GL_FRAMEBUFFER, m_nOldFBO);
        bRet = true;
    } while (0);
    return bRet;

}

void CCRenderTexture::begin()
{
	// Save the current matrix
	glPushMatrix();

	const CCSize& texSize = m_pTexture->getContentSizeInPixels();

	// Calculate the adjustment ratios based on the old and new projections
	CCSize size = CCDirector::sharedDirector()->getDisplaySizeInPixels();
	float widthRatio = size.width / texSize.width;
	float heightRatio = size.height / texSize.height;

	// Adjust the orthographic propjection and viewport
	ccglOrtho((float)-1.0 / widthRatio,  (float)1.0 / widthRatio, (float)-1.0 / heightRatio, (float)1.0 / heightRatio, -1,1);
    glViewport(0, 0, (GLsizei)texSize.width, (GLsizei)texSize.height);
//     CCDirector::sharedDirector()->getOpenGLView()->setViewPortInPoints(0, 0, texSize.width, texSize.height);

	glGetIntegerv(CC_GL_FRAMEBUFFER_BINDING, &m_nOldFBO);
	ccglBindFramebuffer(CC_GL_FRAMEBUFFER, m_uFBO);//Will direct drawing to the frame buffer created above

	// Issue #1145
	// There is no need to enable the default GL states here
	// but since CCRenderTexture is mostly used outside the "render" loop
	// these states needs to be enabled.
	// Since this bug was discovered in API-freeze (very close of 1.0 release)
	// This bug won't be fixed to prevent incompatibilities with code.
	//
	// If you understand the above mentioned message, then you can comment the following line
	// and enable the gl states manually, in case you need them.

	CC_ENABLE_DEFAULT_GL_STATES();
}

void CCRenderTexture::beginWithClear(float r, float g, float b, float a)
{
    this->begin();

	// save clear color
	GLfloat	clearColor[4];
	glGetFloatv(GL_COLOR_CLEAR_VALUE,clearColor);

	glClearColor(r, g, b, a);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// restore clear color
	glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
}

void CCRenderTexture::end(bool bIsTOCacheTexture)
{
	ccglBindFramebuffer(CC_GL_FRAMEBUFFER, m_nOldFBO);
	// Restore the original matrix and viewport
	glPopMatrix();
	CCSize size = CCDirector::sharedDirector()->getDisplaySizeInPixels();
	//	glViewport(0, 0, (GLsizei)size.width, (GLsizei)size.height);
	CCDirector::sharedDirector()->getOpenGLView()->setViewPortInPoints(0, 0, size.width, size.height);

#if CC_ENABLE_CACHE_TEXTTURE_DATA
	if (bIsTOCacheTexture)
	{
		CC_SAFE_DELETE(m_pUITextureImage);

		// to get the rendered texture data
		const CCSize& s = m_pTexture->getContentSizeInPixels();
		int tx = (int)s.width;
		int ty = (int)s.height;
		m_pUITextureImage = new CCImage;
		if (true == getUIImageFromBuffer(m_pUITextureImage, 0, 0, tx, ty))
		{
			VolatileTexture::addDataTexture(m_pTexture, m_pUITextureImage->getData(), kTexture2DPixelFormat_RGBA8888, s);
		}
		else
		{
			CCLOG("Cache rendertexture failed!");
		}
	}
#endif
}

void CCRenderTexture::clear(float r, float g, float b, float a)
{
	this->beginWithClear(r, g, b, a);
	this->end();
}

bool CCRenderTexture::saveBuffer(const char *szFilePath, int x, int y, int nWidth, int nHeight)
{
	bool bRet = false;

	CCImage *pImage = new CCImage();
	if (pImage != NULL && getUIImageFromBuffer(pImage, x, y, nWidth, nHeight))
	{
		bRet = pImage->saveToFile(szFilePath);
	}

	CC_SAFE_DELETE(pImage);
	return bRet;
}
bool CCRenderTexture::saveBuffer(int format, const char *fileName, int x, int y, int nWidth, int nHeight)
{
	bool bRet = false;
	CCAssert(format == kCCImageFormatJPG || format == kCCImageFormatPNG,
			 "the image can only be saved as JPG or PNG format");

	CCImage *pImage = new CCImage();
	if (pImage != NULL && getUIImageFromBuffer(pImage, x, y, nWidth, nHeight))
	{
		std::string fullpath = CCFileUtils::getWriteablePath() + fileName;

		bRet = pImage->saveToFile(fullpath.c_str());
	}

	CC_SAFE_DELETE(pImage);

	return bRet;
}

/* get buffer as UIImage */
bool CCRenderTexture::getUIImageFromBuffer(CCImage *pImage, int x, int y, int nWidth, int nHeight)
{
	if (NULL == pImage || NULL == m_pTexture)
	{
		return false;
	}

	const CCSize& s = m_pTexture->getContentSizeInPixels();
	int tx = (int)s.width;
	int ty = (int)s.height;

	if (x < 0 || x >= tx || y < 0 || y >= ty)
	{
		return false;
	}

	if (nWidth < 0
		|| nHeight < 0
		|| (0 == nWidth && 0 != nHeight)
		|| (0 == nHeight && 0 != nWidth))
	{
		return false;
	}

	// to get the image size to save
	//		if the saving image domain exeeds the buffer texture domain,
	//		it should be cut
	int nSavedBufferWidth = nWidth;
	int nSavedBufferHeight = nHeight;
	if (0 == nWidth)
	{
		nSavedBufferWidth = tx;
	}
	if (0 == nHeight)
	{
		nSavedBufferHeight = ty;
	}
	nSavedBufferWidth = x + nSavedBufferWidth > tx ? (tx - x): nSavedBufferWidth;
	nSavedBufferHeight = y + nSavedBufferHeight > ty ? (ty - y): nSavedBufferHeight;

	GLubyte *pBuffer = NULL;
	GLubyte *pTempData = NULL;
	bool bRet = false;

	do
	{
		CCAssert(m_ePixelFormat == kCCTexture2DPixelFormat_RGBA8888, "only RGBA8888 can be saved as image");

		CC_BREAK_IF(! (pBuffer = new GLubyte[nSavedBufferWidth * nSavedBufferHeight * 4]));

		// On some machines, like Samsung i9000, Motorola Defy,
		// the dimension need to be a power of 2
		int nReadBufferWidth = 0;
		int nReadBufferHeight = 0;
		int nMaxTextureSize = 0;
		glGetIntegerv(GL_MAX_TEXTURE_SIZE, &nMaxTextureSize);

		nReadBufferWidth = ccNextPOT(tx);
		nReadBufferHeight = ccNextPOT(ty);

		CC_BREAK_IF(0 == nReadBufferWidth || 0 == nReadBufferHeight);
		CC_BREAK_IF(nReadBufferWidth > nMaxTextureSize || nReadBufferHeight > nMaxTextureSize);

		CC_BREAK_IF(! (pTempData = new GLubyte[nReadBufferWidth * nReadBufferHeight * 4]));

		this->begin();
		glPixelStorei(GL_PACK_ALIGNMENT, 1);
		glReadPixels(0,0,nReadBufferWidth,nReadBufferHeight,GL_RGBA,GL_UNSIGNED_BYTE, pTempData);
		this->end(false);

		// to get the actual texture data
		// #640 the image read from rendertexture is upseted
		for (int i = 0; i < nSavedBufferHeight; ++i)
		{
			memcpy(&pBuffer[i * nSavedBufferWidth * 4],
				&pTempData[(y + nSavedBufferHeight - i - 1) * nReadBufferWidth * 4 + x * 4],
				nSavedBufferWidth * 4);
		}

		bRet = pImage->initWithImageData(pBuffer, nSavedBufferWidth * nSavedBufferHeight * 4, CCImage::kFmtRawData, nSavedBufferWidth, nSavedBufferHeight, 8);
	} while (0);

	CC_SAFE_DELETE_ARRAY(pBuffer);
	CC_SAFE_DELETE_ARRAY(pTempData);

	return bRet;
}

CCData * CCRenderTexture::getUIImageAsDataFromBuffer(int format)
{
    CC_UNUSED_PARAM(format);
    CCData *  pData     = NULL;
//@ todo CCRenderTexture::getUIImageAsDataFromBuffer

// #include "Availability.h"
// #include "UIKit.h"

//     GLubyte * pBuffer   = NULL;
//     GLubyte * pPixels   = NULL;
//     do
//     {
//         CC_BREAK_IF(! m_pTexture);
//
//         CCAssert(m_ePixelFormat == kCCTexture2DPixelFormat_RGBA8888, "only RGBA8888 can be saved as image");
//
//         const CCSize& s = m_pTexture->getContentSizeInPixels();
//         int tx = s.width;
//         int ty = s.height;
//
//         int bitsPerComponent = 8;
//         int bitsPerPixel = 32;
//
//         int bytesPerRow = (bitsPerPixel / 8) * tx;
//         int myDataLength = bytesPerRow * ty;
//
//         CC_BREAK_IF(! (pBuffer = new GLubyte[tx * ty * 4]));
//         CC_BREAK_IF(! (pPixels = new GLubyte[tx * ty * 4]));
//
//         this->begin();
//         glReadPixels(0,0,tx,ty,GL_RGBA,GL_UNSIGNED_BYTE, pBuffer);
//         this->end();
//
//         int x,y;
//
//         for(y = 0; y <ty; y++) {
//             for(x = 0; x <tx * 4; x++) {
//                 pPixels[((ty - 1 - y) * tx * 4 + x)] = pBuffer[(y * 4 * tx + x)];
//             }
//         }
//
//         if (format == kCCImageFormatRawData)
//         {
//             pData = CCData::dataWithBytesNoCopy(pPixels, myDataLength);
//             break;
//         }

        //@ todo impliment save to jpg or png
        /*
        CGImageCreate(size_t width, size_t height,
        size_t bitsPerComponent, size_t bitsPerPixel, size_t bytesPerRow,
        CGColorSpaceRef space, CGBitmapInfo bitmapInfo, CGDataProviderRef provider,
        const CGFloat decode[], bool shouldInterpolate,
        CGColorRenderingIntent intent)
        */
        // make data provider with data.
//         CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault;
//         CGDataProviderRef provider		= CGDataProviderCreateWithData(NULL, pixels, myDataLength, NULL);
//         CGColorSpaceRef colorSpaceRef	= CGColorSpaceCreateDeviceRGB();
//         CGImageRef iref					= CGImageCreate(tx, ty,
//             bitsPerComponent, bitsPerPixel, bytesPerRow,
//             colorSpaceRef, bitmapInfo, provider,
//             NULL, false,
//             kCGRenderingIntentDefault);
//
//         UIImage* image					= [[UIImage alloc] initWithCGImage:iref];
//
//         CGImageRelease(iref);
//         CGColorSpaceRelease(colorSpaceRef);
//         CGDataProviderRelease(provider);
//
//
//
//         if (format == kCCImageFormatPNG)
//             data = UIImagePNGRepresentation(image);
//         else
//             data = UIImageJPEGRepresentation(image, 1.0f);
//
//         [image release];
//     } while (0);
//
//     CC_SAFE_DELETE_ARRAY(pBuffer);
//     CC_SAFE_DELETE_ARRAY(pPixels);
	return pData;
}

} // namespace cocos2d


code:
/****************************************************************************
Copyright (c) 2010-2011 cocos2d-x.org
Copyright (c) 2008-2009 Jason Booth

http://www.cocos2d-x.org

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 "CCRibbon.h"
#include "CCTextureCache.h"
#include "CCPointExtension.h"
#include "CCDirector.h"

namespace cocos2d {

/*
* A ribbon is a dynamically generated list of polygons drawn as a single or series
* of triangle strips. The primary use of Ribbon is as the drawing class of Motion Streak,
* but it is quite useful on it's own. When manually drawing a ribbon, you can call addPointAt
* and pass in the parameters for the next location in the ribbon. The system will automatically
* generate new polygons, texture them accourding to your texture width, etc, etc.
*
* Ribbon data is stored in a RibbonSegment class. This class statically allocates enough verticies and
* texture coordinates for 50 locations (100 verts or 48 triangles). The ribbon class will allocate
* new segments when they are needed, and reuse old ones if available. The idea is to avoid constantly
* allocating new memory and prefer a more static method. However, since there is no way to determine
* the maximum size of some ribbons (motion streaks), a truely static allocation is not possible.
*
*/

//
// Ribbon
//
CCRibbon * CCRibbon::ribbonWithWidth(float w, const char *path, float length, const ccColor4B& color, float fade)
{
	CCRibbon *pRet = new CCRibbon();
	if(pRet && pRet->initWithWidth(w, path, length, color, fade))
	{
		pRet->autorelease();
		return pRet;
	}
	CC_SAFE_DELETE(pRet)
	return NULL;
}

bool CCRibbon::initWithWidth(float w, const char *path, float length, const ccColor4B& color, float fade)
{
	m_pSegments = new CCMutableArray<CCRibbonSegment*>();
	m_pDeletedSegments = new CCMutableArray<CCRibbonSegment*>();

	/* 1 initial segment */
	CCRibbonSegment* seg = new CCRibbonSegment();
	seg->init();
	m_pSegments->addObject(seg);
	seg->release();

	m_fTextureLength = length;

	m_tColor = color;
	m_fFadeTime = fade;
	m_tLastLocation = CCPointZero;
	m_fLastWidth = w/2;
	m_fTexVPos = 0.0f;

	m_fCurTime = 0;
	m_bPastFirstPoint = false;

	/* XXX:
	Ribbon, by default uses this blend function, which might not be correct
	if you are using premultiplied alpha images,
	but 99% you might want to use this blending function regarding of the texture
	*/
	m_tBlendFunc.src = GL_SRC_ALPHA;
	m_tBlendFunc.dst = GL_ONE_MINUS_SRC_ALPHA;

	m_pTexture = CCTextureCache::sharedTextureCache()->addImage(path);
	CC_SAFE_RETAIN(m_pTexture);

	/* default texture parameter */
	ccTexParams params = { GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT };
	m_pTexture->setTexParameters(&params);
	return true;
}

CCRibbon::~CCRibbon()
{
    CC_SAFE_RELEASE(m_pSegments);
    CC_SAFE_RELEASE(m_pDeletedSegments);
    CC_SAFE_RELEASE(m_pTexture);
}

CCPoint CCRibbon::rotatePoint(const CCPoint& vec, float rotation)
{
	CCPoint ret;
	ret.x = (vec.x * cosf(rotation)) - (vec.y * sinf(rotation));
	ret.y = (vec.x * sinf(rotation)) + (vec.y * cosf(rotation));
	return ret;
}

void CCRibbon::update(ccTime delta)
{
	m_fCurTime += delta;
	m_fDelta = delta;
}

float CCRibbon::sideOfLine(const CCPoint& p, const CCPoint& l1, const CCPoint& l2)
{
	CCPoint vp = ccpPerp(ccpSub(l1, l2));
	CCPoint vx = ccpSub(p, l1);
	return ccpDot(vx, vp);
}

// adds a new segment to the ribbon
void CCRibbon::addPointAt(CCPoint location, float width)
{
    location.x *= CC_CONTENT_SCALE_FACTOR();
    location.y *= CC_CONTENT_SCALE_FACTOR();

    width = width * 0.5f;
	// if this is the first point added, cache it and return
	if (!m_bPastFirstPoint)
	{
		m_fLastWidth = width;
		m_tLastLocation = location;
		m_bPastFirstPoint = true;
		return;
	}

	CCPoint sub = ccpSub(m_tLastLocation, location);
	float r = ccpToAngle(sub) + (float)M_PI_2;
	CCPoint p1 = ccpAdd(this->rotatePoint(ccp(-width, 0), r), location);
	CCPoint p2 = ccpAdd(this->rotatePoint(ccp(+width, 0), r), location);
	float len = sqrtf(powf(m_tLastLocation.x - location.x, 2) + powf(m_tLastLocation.y - location.y, 2));
	float tend = m_fTexVPos + len/m_fTextureLength;
	CCRibbonSegment* seg;
	// grab last segment
	seg = m_pSegments->getLastObject();
	// lets kill old segments
	if (m_pSegments && m_pSegments->count()>0)
	{
		CCMutableArray<CCRibbonSegment*>::CCMutableArrayIterator it;
		for (it = m_pSegments->begin(); it != m_pSegments->end(); ++it)
		{
			if (*it != seg && (*it)->m_bFinished)
			{
				m_pDeletedSegments->addObject(*it);
			}
		}
	}

	m_pSegments->removeObjectsInArray(m_pDeletedSegments);
	// is the segment full?
	if (seg->m_uEnd >= 50)
	{
		m_pSegments->removeObjectsInArray(m_pDeletedSegments);
	}
	// grab last segment and append to it if it's not full
	seg = m_pSegments->getLastObject();
	// is the segment full?
	if (seg->m_uEnd >= 50)
	{
		CCRibbonSegment* newSeg;
		// grab it from the cache if we can
		if (m_pDeletedSegments->count() > 0)
		{
			newSeg = m_pDeletedSegments->getObjectAtIndex(0);
			newSeg->retain();							// will be released later
			m_pDeletedSegments->removeObject(newSeg);
			newSeg->reset();
		}
		else
		{
			newSeg = new CCRibbonSegment(); // will be released later
			newSeg->init();
		}

		newSeg->m_pCreationTime[0] = seg->m_pCreationTime[seg->m_uEnd- 1];
		int v = (seg->m_uEnd-1)*6;
		int c = (seg->m_uEnd-1)*4;
		newSeg->m_pVerts[0] = seg->m_pVerts[v];
		newSeg->m_pVerts[1] = seg->m_pVerts[v+1];
		newSeg->m_pVerts[2] = seg->m_pVerts[v+2];
		newSeg->m_pVerts[3] = seg->m_pVerts[v+3];
		newSeg->m_pVerts[4] = seg->m_pVerts[v+4];
		newSeg->m_pVerts[5] = seg->m_pVerts[v+5];

		newSeg->m_pCoords[0] = seg->m_pCoords[c];
		newSeg->m_pCoords[1] = seg->m_pCoords[c+1];
		newSeg->m_pCoords[2] = seg->m_pCoords[c+2];
		newSeg->m_pCoords[3] = seg->m_pCoords[c+3];
		newSeg->m_uEnd++;
		seg = newSeg;
		m_pSegments->addObject(seg);
		newSeg->release();// it was retained before
	}
	if (seg->m_uEnd == 0)
	{
		// first edge has to get rotation from the first real polygon
		CCPoint lp1 = ccpAdd(this->rotatePoint(ccp(-m_fLastWidth, 0), r), m_tLastLocation);
		CCPoint lp2 = ccpAdd(this->rotatePoint(ccp(+m_fLastWidth, 0), r), m_tLastLocation);
		seg->m_pCreationTime[0] = m_fCurTime - m_fDelta;
		seg->m_pVerts[0] = lp1.x;
		seg->m_pVerts[1] = lp1.y;
		seg->m_pVerts[2] = 0.0f;
		seg->m_pVerts[3] = lp2.x;
		seg->m_pVerts[4] = lp2.y;
		seg->m_pVerts[5] = 0.0f;
		seg->m_pCoords[0] = 0.0f;
		seg->m_pCoords[1] = m_fTexVPos;
		seg->m_pCoords[2] = 1.0f;
		seg->m_pCoords[3] = m_fTexVPos;
		seg->m_uEnd++;
	}

	int v = seg->m_uEnd*6;
	int c = seg->m_uEnd*4;
	// add new vertex
	seg->m_pCreationTime[seg->m_uEnd] = m_fCurTime;
	seg->m_pVerts[v] = p1.x;
	seg->m_pVerts[v+1] = p1.y;
	seg->m_pVerts[v+2] = 0.0f;
	seg->m_pVerts[v+3] = p2.x;
	seg->m_pVerts[v+4] = p2.y;
	seg->m_pVerts[v+5] = 0.0f;

	seg->m_pCoords[c] = 0.0f;
	seg->m_pCoords[c+1] = tend;
	seg->m_pCoords[c+2] = 1.0f;
	seg->m_pCoords[c+3] = tend;

	m_fTexVPos = tend;
	m_tLastLocation = location;
	m_tLastPoint1 = p1;
	m_tLastPoint2 = p2;
	m_fLastWidth = width;
	seg->m_uEnd++;
}

void CCRibbon::draw()
{
	CCNode::draw();

	if (m_pSegments->count() > 0)
	{
		// Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
		// Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY
		// Unneeded states: GL_COLOR_ARRAY
		glDisableClientState(GL_COLOR_ARRAY);

		glBindTexture(GL_TEXTURE_2D, m_pTexture->getName());

        bool newBlend = ( m_tBlendFunc.src != CC_BLEND_SRC || m_tBlendFunc.dst != CC_BLEND_DST ) ? true : false;
        if( newBlend )
        {
            glBlendFunc( m_tBlendFunc.src, m_tBlendFunc.dst );
        }

		if(m_pSegments && m_pSegments->count() > 0)
		{
			CCRibbonSegment* seg;
			CCMutableArray<CCRibbonSegment*>::CCMutableArrayIterator it;
			for( it = m_pSegments->begin(); it != m_pSegments->end(); it++)
			{
				seg = (CCRibbonSegment*)*it;
				seg->draw(m_fCurTime, m_fFadeTime, m_tColor);
			}
		}

		if( newBlend )
		{
			glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
		}

		// restore default GL state
		glEnableClientState( GL_COLOR_ARRAY );
	}
}

// Ribbon - CocosNodeTexture protocol
void CCRibbon::setTexture(CCTexture2D* var)
{
	CC_SAFE_RETAIN(var);
	CC_SAFE_RELEASE(m_pTexture);
	m_pTexture = var;
	this->setContentSize(m_pTexture->getContentSizeInPixels());
	/* XXX Don't update blending function in Ribbons */
}

CCTexture2D *CCRibbon::getTexture()
{
	return m_pTexture;
}

void CCRibbon::setTextureLength(float var)
{
	m_fTextureLength = var;
}

float CCRibbon::getTextureLength()
{
	return m_fTextureLength;
}
void CCRibbon::setBlendFunc(ccBlendFunc var)
{
	m_tBlendFunc = var;
}

ccBlendFunc CCRibbon::getBlendFunc()
{
	return m_tBlendFunc;
}

void CCRibbon::setColor(const ccColor4B& var)
{
	m_tColor = var;
}
const ccColor4B& CCRibbon::getColor()
{
	return m_tColor;
}

//
//RibbonSegment
//
bool CCRibbonSegment::init()
{
	this->reset();
	return true;
}

char * CCRibbonSegment::description()
{
	char *ret = new char[100] ;
	sprintf(ret, "<CCRibbonSegment | end = %u, begin = %u>", m_uEnd, m_uBegin);
	return ret;
}

CCRibbonSegment::~CCRibbonSegment()
{
	CCLOGINFO("cocos2d: deallocing.");
}

void CCRibbonSegment::reset()
{
	m_uEnd = 0;
	m_uBegin = 0;
	m_bFinished = false;
}

void CCRibbonSegment::draw(float curTime, float fadeTime, const ccColor4B& color)
{
	GLubyte r = color.r;
	GLubyte g = color.g;
	GLubyte b = color.b;
	GLubyte a = color.a;

	if (m_uBegin < 50)
	{
		// the motion streak class will call update and cause time to change, thus, if curTime_ != 0
		// we have to generate alpha for the ribbon each frame.
		if (curTime == 0)
		{
			// no alpha over time, so just set the color
            // glColor4ub isn't implement on some android devices
			// glColor4ub(r,g,b,a);
            glColor4f(((GLfloat)r) / 255, ((GLfloat)g) / 255, ((GLfloat)b) / 255, ((GLfloat)a) / 255);
		}
		else
		{
			// generate alpha/color for each point
			glEnableClientState(GL_COLOR_ARRAY);
			unsigned int i = m_uBegin;
			for (; i < m_uEnd; ++i)
			{
				int idx = i*8;
				m_pColors[idx] = r;
				m_pColors[idx+1] = g;
				m_pColors[idx+2] = b;
				m_pColors[idx+4] = r;
				m_pColors[idx+5] = g;
				m_pColors[idx+6] = b;
				float alive = ((curTime - m_pCreationTime[i]) / fadeTime);
				if (alive > 1)
				{
					m_uBegin++;
					m_pColors[idx+3] = 0;
					m_pColors[idx+7] = 0;
				}
				else
				{
					m_pColors[idx+3] = (GLubyte)(255.f - (alive * 255.f));
					m_pColors[idx+7] = m_pColors[idx+3];
				}
			}
			glColorPointer(4, GL_UNSIGNED_BYTE, 0, &m_pColors[m_uBegin*8]);
		}
		glVertexPointer(3, GL_FLOAT, 0, &m_pVerts[m_uBegin*6]);
		glTexCoordPointer(2, GL_FLOAT, 0, &m_pCoords[m_uBegin*4]);
		glDrawArrays(GL_TRIANGLE_STRIP, 0, (m_uEnd - m_uBegin) * 2);
	}
	else
	{
		m_bFinished = true;
	}
}

}// namespace cocos2d