// Ryzom - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "stdpch.h"
#include "ctrl_base_button.h"
#include "interface_manager.h"
#include "game_share/xml_auto_ptr.h"
#include "../time_client.h"
#include "lua_ihm.h"
using namespace std;
using namespace NLMISC;
// ***************************************************************************
static const uint KEY_REPEAT_MIN = 100;
static const uint KEY_REPEAT_MAX = 750;
sint64 CCtrlBaseButton::_LastLeftClickDate = 0;
NLMISC::CRefPtr CCtrlBaseButton::_LastLeftClickButton;
// ***************************************************************************
CCtrlBaseButton::CCtrlBaseButton(const TCtorParam ¶m) : CCtrlBase(param), _Type(ToggleButton)
{
_Pushed = _Over = false;
_Frozen = false;
_FrozenHalfTone = true;
_OverWhenPushed = true;
_ColorOver = _ColorPushed = _ColorNormal = NLMISC::CRGBA(255,255,255,255);
_ModulateGlobalColorNormal= _ModulateGlobalColorPushed= _ModulateGlobalColorOver= true;
_LeftLongClickHandled = true;
_LeftDblClickHandled = false;
_ClickWhenPushed = false;
_RBRefBut = NULL;
_RBRef = NULL;
_AHOnOver = NULL;
_AHOnLeftClick = NULL;
_AHOnRightClick = NULL;
_AHOnClockTick = NULL;
_AHOnLeftLongClick = NULL;
_AHOnLeftDblClick = NULL;
}
// ***************************************************************************
bool CCtrlBaseButton::parse (xmlNodePtr cur,CInterfaceGroup * parentGroup)
{
CXMLAutoPtr prop;
//try to get props that can be inherited from groups
//if a property is not defined, try to find it in the parent group.
//if it is undefined, set it to zero
if (! CCtrlBase::parse(cur,parentGroup) )
return false;
_Over = false;
// *** try to get the NEEDED specific props
prop = xmlGetProp (cur, (xmlChar*)"button_type");
string type;
if (prop) type = (const char*) prop;
if (type.empty() || type == "toggle_button")
{
_Type = ToggleButton;
}
else if (type == "push_button")
{
_Type = PushButton;
}
else if (type == "radio_button")
{
_Type = RadioButton;
initRBRef();
if (_Pushed)
*_RBRef = this;
}
else
{
nlinfo(("cannot parse button type for button " + getId()).c_str());
}
prop= (char*) xmlGetProp (cur, (xmlChar*)"pushed");
_Pushed = false;
if (prop)
_Pushed = convertBool(prop);
prop= (char*) xmlGetProp (cur, (xmlChar*)"over_when_pushed");
_OverWhenPushed = true;
if (prop)
_OverWhenPushed = convertBool(prop);
prop= (char*) xmlGetProp (cur, (xmlChar*)"click_when_pushed");
_ClickWhenPushed = false;
if (prop)
_ClickWhenPushed = convertBool(prop);
// *** Read Colors
// get color normal
prop= (char*) xmlGetProp( cur, (xmlChar*)"color" );
_ColorNormal = CRGBA(255,255,255,255);
if (prop)
_ColorNormal = convertColor (prop);
// Get ColorPushed
prop= (char*) xmlGetProp( cur, (xmlChar*)"col_pushed" );
_ColorPushed = CRGBA(255,255,255,255);
if (prop)
_ColorPushed = convertColor(prop);
// Get ColorOver
prop= (char*) xmlGetProp( cur, (xmlChar*)"col_over" );
_ColorOver = CRGBA(255,255,255,255);
if (prop)
_ColorOver = convertColor(prop);
// Default: take "global_color" param interface_element option.
_ModulateGlobalColorNormal= _ModulateGlobalColorPushed= _ModulateGlobalColorOver= getModulateGlobalColor();
// Read special global_color for each state
prop = (char*) xmlGetProp( cur, (xmlChar*)"global_color_normal" );
if (prop) _ModulateGlobalColorNormal= convertBool(prop);
prop = (char*) xmlGetProp( cur, (xmlChar*)"global_color_pushed" );
if (prop) _ModulateGlobalColorPushed= convertBool(prop);
prop = (char*) xmlGetProp( cur, (xmlChar*)"global_color_over" );
if (prop) _ModulateGlobalColorOver= convertBool(prop);
// *** Read Action handlers
parseAH(cur, "onover", "params_over", _AHOnOver, _AHOverParams);
parseAH(cur, "onclick_l", "params_l", _AHOnLeftClick, _AHLeftClickParams);
parseAH(cur, "ondblclick_l", "params_dblclick_l", _AHOnLeftDblClick, _AHLeftDblClickParams);
parseAH(cur, "onclick_r", "params_r", _AHOnRightClick, _AHRightClickParams);
parseAH(cur, "onlongclick_l", "params_longclick_l", _AHOnLeftLongClick, _AHLeftLongClickParams);
parseAH(cur, "onclock_tick", "params_clock_tick", _AHOnClockTick, _AHClockTickParams);
// Context menu association
prop = (char*) xmlGetProp( cur, (xmlChar*)"menu_l" );
if (prop)
{
string tmp = (const char *) prop;
_ListMenuLeft = strlwr(tmp);
}
prop = (char*) xmlGetProp( cur, (xmlChar*)"menu_r" );
if (prop)
{
string tmp = (const char *) prop;
_ListMenuRight = strlwr(tmp);
}
// list menu on both clicks
prop = (char*) xmlGetProp( cur, (xmlChar*)"menu_b" );
if (prop)
{
string tmp = (const char *) prop;
setListMenuBoth(strlwr(tmp));
}
prop= (char*) xmlGetProp (cur, (xmlChar*)"frozen");
_Frozen = false;
if (prop)
_Frozen = convertBool(prop);
prop= (char*) xmlGetProp (cur, (xmlChar*)"frozen_half_tone");
_FrozenHalfTone = true;
if (prop)
_FrozenHalfTone = convertBool(prop);
return true;
}
// ***************************************************************************
void CCtrlBaseButton::setModulateGlobalColorAll(bool state)
{
setModulateGlobalColor(state);
setModulateGlobalColorNormal(state);
setModulateGlobalColorPushed(state);
setModulateGlobalColorOver(state);
}
// ***************************************************************************
bool CCtrlBaseButton::handleEvent (const CEventDescriptor& event)
{
if (CCtrlBase::handleEvent(event)) return true;
if (!_Active || _Frozen)
return false;
if (event.getType() == CEventDescriptor::mouse)
{
CInterfaceManager *pIM = CInterfaceManager::getInstance();
const CEventDescriptorMouse &eventDesc = (const CEventDescriptorMouse &)event;
if (eventDesc.getEventTypeExtended() == CEventDescriptorMouse::mouseleftup)
{
if (pIM->getCapturePointerLeft() != this)
return false;
_LeftLongClickHandled = true;
}
if (!((eventDesc.getX() >= _XReal) &&
(eventDesc.getX() < (_XReal + _WReal))&&
(eventDesc.getY() > _YReal) &&
(eventDesc.getY() <= (_YReal+ _HReal))))
return false;
if (eventDesc.getEventTypeExtended() == CEventDescriptorMouse::mouseleftdown)
{
if (_AHOnLeftDblClick)
{
if ((CCtrlBaseButton *) _LastLeftClickButton == this && (T1 - _LastLeftClickDate) < pIM->getUserDblClickDelay())
{
pIM->runActionHandler (_AHOnLeftDblClick, this, _AHLeftDblClickParams);
_LeftDblClickHandled = true;
_LastLeftClickButton = NULL;
return true;
}
}
if (_AHOnLeftLongClick != NULL)
{
_LeftLongClickHandled = false;
_LeftLongClickDate = T1;
}
_LeftDblClickHandled = false;
_LastLeftClickButton = NULL;
return true;
}
if (eventDesc.getEventTypeExtended() == CEventDescriptorMouse::mouseleftup)
{
if (pIM->getCapturePointerLeft() != this)
return false;
if (_LeftDblClickHandled) // no effect on mouse up after double click has been handled
{
_LeftDblClickHandled = false;
return true;
}
// Do not launch 2 times action handler if we are already pushed ! except if we want.
if (!_ClickWhenPushed)
{
if ((_Type == RadioButton) && _RBRef && (*_RBRef == this))
return true;
}
if (_Type == RadioButton)
{
_Pushed = true;
if(_RBRef) *_RBRef = this;
}
if (_Type == ToggleButton)
_Pushed = !_Pushed;
/*
// RunAction
if(_AHOnLeftClick != NULL)
{
//nlinfo("clicked on %s", _Id.c_str());
pIM->submitEvent ("button_click:"+getId());//TEMP
pIM->runActionHandler (_AHOnLeftClick, this, _AHLeftClickParams);
//pIM->submitEvent ("button_click:"+getId());
}
*/
runLeftClickAction();
if (pIM->getCapturePointerLeft() == NULL) return true; // event handler may release cpature from this object (if it is removed for example)
// Run Menu
if (!_ListMenuLeft.empty())
pIM->enableModalWindow (this, _ListMenuLeft);
if (_AHOnLeftDblClick != NULL)
{
_LastLeftClickDate = T1;
_LastLeftClickButton = this;
}
// Always return true on LeftClick.
return true;
}
if (eventDesc.getEventTypeExtended() == CEventDescriptorMouse::mouserightdown)
{
_LastLeftClickButton = NULL;
return true;
}
if (eventDesc.getEventTypeExtended() == CEventDescriptorMouse::mouserightup)
{
_LastLeftClickButton = NULL;
bool handled= false;
CInterfaceManager *pIM = CInterfaceManager::getInstance();
if (pIM->getCapturePointerRight() != this)
return false;
// RunAction
if(_AHOnRightClick != NULL)
{
handled= true;
pIM->runActionHandler (_AHOnRightClick, this, _AHRightClickParams);
}
if (pIM->getCapturePointerRight() == NULL) return true; // if this become NULL, this ctrl has been deleted
// Run Menu
if (!_ListMenuRight .empty())
{
handled= true;
pIM->enableModalWindow (this, _ListMenuRight);
}
// If not handled here, ret to parent
return handled;
}
}
else if (event.getType() == CEventDescriptor::system)
{
const CEventDescriptorSystem &systemEvent = (const CEventDescriptorSystem &) event;
if (systemEvent.getEventTypeExtended() == CEventDescriptorSystem::clocktick)
{
if (_AHOnClockTick != NULL)
{
CInterfaceManager *pIM = CInterfaceManager::getInstance();
pIM->runActionHandler(_AHOnClockTick, this, _AHClockTickParams);
}
CInterfaceManager *pIM = CInterfaceManager::getInstance();
if (pIM->getCapturePointerLeft() == this)
{
if (!_LeftLongClickHandled)
{
uint nVal = 50;
CCDBNodeLeaf *pNL = pIM->getDbProp("UI:SAVE:KEY_REPEAT_SPEED");
if (pNL != NULL)
nVal = pNL->getValue32();
uint repeatDelay = (uint)(KEY_REPEAT_MIN + (KEY_REPEAT_MAX-KEY_REPEAT_MIN) * (float)nVal / 100.0f);
if ((T1 - _LeftLongClickDate) > repeatDelay)
{
_LeftLongClickHandled = true;
pIM->runActionHandler(_AHOnLeftLongClick, this, _AHLeftLongClickParams);
}
}
}
}
}
return false;
}
// ***************************************************************************
void CCtrlBaseButton::initRBRef()
{
if (_RBRef != NULL) return;
nlassert(_Parent);
const vector &vCB = _Parent->getControls();
uint i = 0;
for (i = 0; i < vCB.size(); ++i)
{
CCtrlBaseButton *pBut = dynamic_cast(vCB[i]);
if (pBut && pBut->_Type == RadioButton)
{
_RBRef = &pBut->_RBRefBut;
break;
}
}
// If we are the first radio button of the group and not added
if (i == vCB.size())
_RBRef = &this->_RBRefBut;
}
// ***************************************************************************
void CCtrlBaseButton::initRBRefFromRadioButton(CCtrlBaseButton * pBut)
{
if(pBut && pBut->_Type == RadioButton)
{
_RBRef = &(pBut->_RBRefBut);
_RBRefBut=NULL;
}
}
// ***************************************************************************
void CCtrlBaseButton::setPushed (bool state)
{
_Pushed = state;
if (_Type == RadioButton && _RBRef)
{
if (state == true)
{
*_RBRef = this;
}
else
{
if (*_RBRef == this) // I have to be pushed to unpush me
*_RBRef = NULL; // After that : All radio buttons are NOT pushed
}
}
}
// ***************************************************************************
void CCtrlBaseButton::setFrozen (bool state)
{
_Frozen = state;
if (_Frozen)
{
_Pushed = false;
_Over = false;
}
}
// ***************************************************************************
void CCtrlBaseButton::setFrozenHalfTone(bool enabled)
{
_FrozenHalfTone = enabled;
}
// ***************************************************************************
void CCtrlBaseButton::unselect()
{
if (_Type == RadioButton)
{
if (_RBRef) *_RBRef = NULL;
}
}
// ***************************************************************************
void CCtrlBaseButton::updateOver(bool &lastOver)
{
CInterfaceManager *pIM= CInterfaceManager::getInstance();
if (!pIM->isMouseHandlingEnabled())
{
_Over = false;
return;
}
if (pIM->getCapturePointerLeft() != NULL)
{
if (pIM->getCapturePointerLeft() != this)
{
_Over = false;
}
return;
}
const vector &rVB = pIM->getCtrlsUnderPointer ();
if (!_Frozen)
{
uint32 i;
lastOver = _Over;
// show over if it is the last control that has the same father
CCtrlBase *candidate = NULL;
for (i = 0; i < rVB.size(); ++i)
{
if (rVB[i]->getParent() == this->getParent())
{
candidate = rVB[i];
}
}
_Over = (candidate == this);
}
else
_Over = false;
}
// ***************************************************************************
void CCtrlBaseButton::elementCaptured(CCtrlBase *capturedElement)
{
// if not me, then reset my '_Over'
if (capturedElement != this)
{
_Over = false;
}
}
// ***************************************************************************
void CCtrlBaseButton::runLeftClickAction()
{
if(_AHOnLeftClick != NULL)
{
CInterfaceManager *pIM = CInterfaceManager::getInstance();
//nlinfo("clicked on %s", _Id.c_str());
pIM->submitEvent ("button_click:"+getId());//TEMP
pIM->runActionHandler (_AHOnLeftClick, this, _AHLeftClickParams);
//pIM->submitEvent ("button_click:"+getId());
}
}
// ***************************************************************************
int CCtrlBaseButton::luaRunLeftClickAction(CLuaState &ls)
{
const char *funcName = "onLeftClick";
CLuaIHM::checkArgCount(ls, funcName, 0);
runLeftClickAction();
return 0;
}