Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / gui / lua_ihm.cpp
blob8d4d64b889275376ff907e2319189a64e1c74fe0
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2019 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2019-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "stdpch.h"
23 #include "nel/gui/lua_helper.h"
24 #include "nel/gui/interface_group.h"
26 #include <algorithm>
28 // to get rid of you_must_not_use_assert___use_nl_assert___read_debug_h_file messages
29 #include <cassert>
30 #ifdef assert
31 #undef assert
32 #endif
34 #ifdef NL_OS_WINDOWS
35 #include <Windows.h>
36 #endif
38 // Warning: cannot use namespace std, when using luabind
39 #ifdef NL_OS_WINDOWS
40 # ifndef NL_EXTENDED_FOR_SCOPE
41 # undef for
42 # endif
43 #endif
45 #ifdef NL_DEBUG
46 # define assert(x) nlassert(x)
47 #else
48 # define assert(x)
49 #endif
51 // Always use unique_ptr with ValyriaTear/luabind on Ubuntu 20,
52 // since the setting is not stored in build_information.hpp
53 #ifndef LUABIND_USE_CXX11
54 #define LUABIND_USE_CXX11
55 #endif
57 #include <luabind/luabind.hpp>
58 // in luabind > 0.6, LUABIND_MAX_ARITY is set to 10
59 #if LUABIND_MAX_ARITY == 10
60 # include <luabind/operator.hpp>
61 // only luabind > 0.7 have version.hpp (file checked with build system)
62 # ifdef HAVE_LUABIND_VERSION
63 # include <luabind/version.hpp>
64 # endif
65 # ifndef LUABIND_VERSION
66 // luabind 0.7 doesn't define LUABIND_VERSION
67 # define LUABIND_VERSION 700
68 # endif
69 // luabind 0.6 doesn't define LUABIND_VERSION but LUABIND_MAX_ARITY is set to 5
70 #elif LUABIND_MAX_ARITY == 5
71 # define LUABIND_VERSION 600
72 #else
73 # pragma error("luabind version not recognized")
74 #endif
77 #include "nel/gui/lua_ihm.h"
78 #include "nel/gui/reflect.h"
79 #include "nel/misc/algo.h"
80 #include "nel/misc/file.h"
81 #include "nel/misc/i18n.h"
82 #include "nel/misc/time_nl.h"
83 #include "nel/misc/path.h"
84 #include "nel/misc/sstring.h"
85 #include "nel/misc/command.h"
86 #include "nel/gui/lua_object.h"
87 #include "nel/misc/polygon.h"
88 #include "nel/gui/lua_manager.h"
89 #include "nel/gui/widget_manager.h"
90 #include "nel/gui/action_handler.h"
91 #include "nel/gui/view_renderer.h"
92 #include "nel/gui/interface_expr.h"
93 #include "nel/misc/debug.h"
95 // ***************************************************************************
97 IMPORTANT NOTE: we do this heavy double registration in this file because we DON'T want
98 to include luabind.hpp in every file.
99 Compilation is VERY SLOW
101 // ***************************************************************************
103 using namespace NLMISC;
105 #ifdef DEBUG_NEW
106 #define new DEBUG_NEW
107 #endif
109 #ifdef RYZOM_LUA_UCSTRING
110 // declare ostream << operator for ucstring -> registration of ucstring iin luabind will build a 'tostring' function from it
111 std::ostream &operator<<(std::ostream &str, const ucstring &value)
113 return str << value.toString();
115 #endif
117 namespace NLGUI
120 struct CMiscFunctions
122 static std::string fileLookup(const std::string &fileName)
124 return NLMISC::CPath::lookup(fileName, false);
126 static void shellExecute(const std::string &operation, const std::string &fileName, const std::string &parameters)
128 #if !FINAL_VERSION
129 #ifdef NL_OS_WINDOWS
130 ShellExecuteW(NULL, nlUtf8ToWide(operation), nlUtf8ToWide(fileName), nlUtf8ToWide(parameters), NULL, SW_SHOWDEFAULT);
131 #endif
132 #endif
137 // ***************************************************************************
138 bool CLuaIHM::pop(CLuaState &ls, NLMISC::CRGBA &dest)
140 //H_AUTO(Lua_CLuaIHM_pop)
143 if (ls.isNil(-1)) return false;
144 #if LUABIND_VERSION > 600
145 luabind::object obj(luabind::from_stack(ls.getStatePointer(), -1));
146 ls.pop();
147 #else
148 luabind::object obj(ls.getStatePointer());
149 obj.set();
150 #endif
151 dest = luabind::object_cast<NLMISC::CRGBA>(obj);
153 catch(const luabind::cast_failed &)
155 return false;
157 return true;
160 // ***************************************************************************
161 bool CLuaIHM::pop(CLuaState &ls,NLMISC::CVector2f &dest)
163 //H_AUTO(Lua_CLuaIHM_pop)
166 if (ls.isNil(-1)) return false;
167 #if LUABIND_VERSION > 600
168 luabind::object obj(luabind::from_stack(ls.getStatePointer(), -1));
169 ls.pop();
170 #else
171 luabind::object obj(ls.getStatePointer());
172 obj.set();
173 #endif
174 dest = luabind::object_cast<NLMISC::CVector2f>(obj);
176 catch(const luabind::cast_failed &)
178 return false;
180 return true;
183 #ifdef RYZOM_LUA_UCSTRING
184 // ***************************************************************************
185 bool CLuaIHM::pop(CLuaState &ls, ucstring &dest)
187 //H_AUTO(Lua_CLuaIHM_pop)
190 if (ls.isNil(-1)) return false;
191 #if LUABIND_VERSION > 600
192 luabind::object obj(luabind::from_stack(ls.getStatePointer(), -1));
193 ls.pop();
194 #else
195 luabind::object obj(ls.getStatePointer());
196 obj.set();
197 #endif
198 dest = luabind::object_cast<ucstring>(obj);
200 catch(const luabind::cast_failed &)
202 return false;
204 return true;
207 // ***************************************************************************
208 bool CLuaIHM::isUCStringOnStack(CLuaState &ls, sint index)
210 //H_AUTO(Lua_CLuaIHM_isUCStringOnStack)
211 ls.pushValue(index);
212 ucstring dummy;
213 return pop(ls, dummy);
216 // ***************************************************************************
217 bool CLuaIHM::getUCStringOnStack(CLuaState &ls, sint index, ucstring &dest)
219 //H_AUTO(Lua_CLuaIHM_getUCStringOnStack)
220 ls.pushValue(index);
221 return pop(ls, dest);
225 // ***************************************************************************
226 void CLuaIHM::push(CLuaState &ls, const ucstring &value)
228 //H_AUTO(Lua_CLuaIHM_push)
229 #if defined(LUABIND_STACK_HPP_INCLUDED)
230 luabind::push(ls.getStatePointer(), value);
231 #elif (LUABIND_VERSION > 600)
232 luabind::detail::push(ls.getStatePointer(), value);
233 #else
234 luabind::object obj(ls.getStatePointer(), value);
235 obj.pushvalue();
236 #endif
238 #endif
240 // ***************************************************************************
241 // ***************************************************************************
242 // CInterface To LUA Registry
243 // ***************************************************************************
244 // ***************************************************************************
247 CLuaState * ELuaIHMException::getLuaState()
249 return CLuaManager::getInstance().getLuaState();
254 // ***************************************************************************
255 #define LUA_REGISTER_BASIC(_type_) \
256 luabind::detail::yes_t is_user_defined(luabind::detail::by_value<_type_>); \
257 _type_ convert_lua_to_cpp(lua_State* L, luabind::detail::by_value<_type_>, int index) \
259 return (_type_)lua_tointeger(L, index); \
261 int match_lua_to_cpp(lua_State* L, luabind::detail::by_value<_type_>, int index) \
263 return lua_isnumber(L, index) ? 0:-1; \
265 void convert_cpp_to_lua(lua_State* L, const _type_& v) \
267 lua_pushinteger(L, (lua_Integer)v); \
270 // Basic LUA types
271 namespace luabind
273 namespace converters
275 LUA_REGISTER_BASIC(sint8)
276 LUA_REGISTER_BASIC(uint8)
277 LUA_REGISTER_BASIC(sint16)
278 LUA_REGISTER_BASIC(uint16)
279 LUA_REGISTER_BASIC(sint32)
280 LUA_REGISTER_BASIC(uint32)
284 namespace NLGUI
286 static CLuaString lstr_Env("Env");
287 static CLuaString lstr_isNil("isNil");
289 // ***************************************************************************
290 int CLuaIHM::luaUIIndex(CLuaState &ls)
292 //H_AUTO(Lua_CLuaIHM_luaUIIndex)
293 nlassert(ls.getTop()==2);
294 // get the userdata and key
295 CReflectableLuaRef *pRefElm = (CReflectableLuaRef *) ls.toUserData(1);
297 const char *propName = ls.toString(2);
298 CReflectableRefPtrTarget *pRPT= (CReflectableRefPtrTarget*)(pRefElm->Ptr);
299 // ** try to get the Env Table (interface group only)
300 if(propName==lstr_isNil)
302 ls.push(pRPT==NULL);
303 return 1;
306 // Check the object is not NULL or freed
307 if(pRPT==NULL)
309 return 0;
312 // ** try to get the Env Table (interface group only)
313 if(propName==lstr_Env)
315 // Env can be bound to a CInterfaceGroup only
316 CInterfaceGroup *group= dynamic_cast<CInterfaceGroup*>(pRPT);
317 if(group==NULL)
319 ls.pushNil();
320 return 1;
322 else
324 group->pushLUAEnvTable();
325 return 1;
329 // ** try to get the property
330 const CReflectedProperty *prop = pRefElm->getProp(propName);
331 if (prop)
333 CLuaIHM::luaValueFromReflectedProperty(ls, *pRPT, *prop);
334 return 1;
337 // ** try to get a UI relative
338 CInterfaceElement *uiRelative= getUIRelative(dynamic_cast<CInterfaceElement *>(pRPT), propName);
339 if(uiRelative)
341 // push the UI onto the stack
342 pushUIOnStack(ls, uiRelative);
343 return 1;
347 // Fail to find any Attributes or elements
348 // Yoyo: don't write any message or warning because this may be a feature (if user want to test that something exit in the ui)
349 ls.pushNil();
350 return 1;
353 // ***************************************************************************
354 int CLuaIHM::luaUINewIndex(CLuaState &ls)
356 //H_AUTO(Lua_CLuaIHM_luaUINewIndex)
357 nlassert(ls.getTop()==3);
358 // get the userdata and key
359 CReflectableLuaRef *pRefElm = (CReflectableLuaRef *) ls.toUserData(1);
360 nlassert(pRefElm);
361 CReflectableRefPtrTarget *pRPT= (CReflectableRefPtrTarget*)(pRefElm->Ptr);
362 // Check the UI is not NULL or freed
363 if(pRPT == NULL)
365 return 0;
368 const char *propName = ls.toString(2);
369 // ** try to set the Env Table (interface group only)
370 if(propName == lstr_Env)
372 CInterfaceElement *pIE = dynamic_cast<CInterfaceElement *>(pRPT);
373 std::string name ;
374 if (pIE)
376 name = pIE->getId();
378 else
380 name = "<reflectable element>";
382 // Exception!!! not allowed
383 throw ELuaIHMException("You cannot change the Env Table of '%s'", name.c_str());
387 // ** try to set the property
388 const CReflectedProperty *prop = pRefElm->getProp(propName);
389 if (prop)
391 CLuaIHM::luaValueToReflectedProperty(ls, 3, *pRPT, *prop);
392 return 0;
395 CInterfaceElement *pIE = dynamic_cast<CInterfaceElement *>(pRPT);
396 // ** try to get another UI (child or parent)
397 CInterfaceElement *uiRelative= getUIRelative(pIE, propName);
398 if(uiRelative)
400 // Exception!!! not allowed
401 throw ELuaIHMException("You cannot write into the UI '%s' of '%s'", propName, pIE->getId().c_str());
404 // ** Prop Not Found
405 throw ELuaIHMException("Property '%s' not found in '%s' of type %s", propName, pIE ? pIE->getId().c_str() : "<reflectable element>", typeid(*pRPT).name());
407 // Fail to find any Attributes or elements
408 return 0;
411 // ***************************************************************************
412 int CLuaIHM::luaUIEq(CLuaState &ls)
414 //H_AUTO(Lua_CLuaIHM_luaUIEq)
415 nlassert(ls.getTop() == 2);
416 // read lhs & rhs
417 // get the userdata and key
418 CReflectableLuaRef *lhs = (CReflectableLuaRef *) ls.toUserData(1);
419 CReflectableLuaRef *rhs = (CReflectableLuaRef *) ls.toUserData(2);
420 nlassert(lhs);
421 nlassert(rhs);
422 ls.push(lhs->Ptr == rhs->Ptr);
423 return 1;
427 // ***************************************************************************
428 int CLuaIHM::luaUIDtor(CLuaState &ls)
430 //H_AUTO(Lua_CLuaIHM_luaUIDtor)
431 nlassert(ls.getTop()==1);
432 // get the userdata
433 CReflectableLuaRef *pRefElm = (CReflectableLuaRef *) ls.toUserData(1);
434 nlassert(pRefElm);
436 // call dtor
437 pRefElm->~CReflectableLuaRef();
439 return 0;
442 // ***************************************************************************
443 int CLuaIHM::luaUINext(CLuaState &ls)
445 //H_AUTO(Lua_CLuaIHM_luaUINext)
446 // Code below allow enumeration of properties of a reflectable object
447 // From lua standpoint, the object is seen as a table with (key, value) pairs
448 // If object is a CInterfaceGroup, iteration is also done on sons (groups, controls & view).
450 if (ls.getTop() != 2)
452 CLuaIHM::fails(ls, "__next metamethod require 2 arguments (table & key)");
454 CLuaIHM::check(ls, CLuaIHM::isReflectableOnStack(ls, 1), "__next : require ui element as first arg");
455 CReflectableRefPtrTarget *reflectedObject = CLuaIHM::getReflectableOnStack(ls, 1);
456 // To traverse all properties / field of the object, we must be able to determine the next key from a previous key
457 // (keys are ordered)
458 // We use the 'TValueType' enum to know which kind of property we are traversing, and an index in this group of properties
459 // The key which uniquely identify an element / property in the reflectable object
460 struct CKey
462 enum TValueType
464 VTGroup = 0, // children groups (If the object is a CInterfaceGroup)
465 VTView, // children views (If the object is a CInterfaceView)
466 VTCtrl, // children controls (If the object is a CInterfaceCtrl)
467 VTProp // List of exported proeprties (For all relfectable objects)
469 TValueType ValueType;
470 sint Index;
471 const CClassInfo *ClassInfo; // if ValueType is "VTProp" -> give the class for which property are currently enumerated
473 static int tostring(CLuaState &ls) // '__print' metamathod
475 CLuaIHM::checkArgCount(ls, "reflected object metatable:__print", 1);
476 CKey key;
477 key.pop(ls);
478 switch(key.ValueType)
480 case VTGroup: ls.push(toString("_Group %d", key.Index)); break;
481 case VTView: ls.push(toString("_View %d", key.Index)); break;
482 case VTCtrl: ls.push(toString("_Ctrl %d", key.Index)); break;
483 case VTProp: ls.push(key.ClassInfo->Properties[key.Index].Name); break;
485 return 1;
487 // push the key on the lua stack
488 void push(CLuaState &ls)
490 void *ud = ls.newUserData(sizeof(*this));
491 *(CKey *) ud = *this;
492 getMetaTable(ls).push();
493 ls.setMetaTable(-2);
495 // pop the key from the lua stack
496 void pop(CLuaState &ls)
498 CLuaStackChecker lsc(&ls, -1);
499 if (!ls.isUserData(-1))
501 CLuaIHM::fails(ls, "Can't pop object, not a user data");
503 // check that metatable is good (it is share between all keys)
504 ls.getMetaTable(-1);
505 getMetaTable(ls).push();
506 if (!ls.rawEqual(-1, -2))
508 CLuaIHM::fails(ls, "Bad metatable for reflectable object key");
510 ls.pop(2);
511 // retrieve key
512 *this = *(CKey *) ls.toUserData(-1);
513 ls.pop();
515 // get the metatable for a CKey
516 CLuaObject &getMetaTable(CLuaState &ls)
518 static CLuaObject metatable;
519 if (!metatable.isValid())
521 // first build
522 CLuaStackChecker lsc(&ls);
523 ls.newTable();
524 ls.push("__tostring");
525 ls.push(CKey::tostring);
526 ls.setTable(-3);
527 metatable.pop(ls);
529 return metatable;
532 // Pop the current key to continue enumeration
533 CKey key;
534 if (ls.isNil(2))
536 // no key -> start of table
537 key.ValueType = CKey::VTGroup;
538 key.Index = -1;
540 else
542 key.pop(ls);
545 CInterfaceGroup *group = dynamic_cast<CInterfaceGroup *>(reflectedObject);
546 bool enumerate = true;
547 while (enumerate)
549 switch(key.ValueType)
551 case CKey::VTGroup:
552 if (!group || (key.Index + 1) == (sint) group->getGroups().size())
554 key.Index = -1;
555 key.ValueType = CKey::VTView; // continue enumeration with views
557 else
559 ++ key.Index;
560 key.push(ls);
561 pushUIOnStack(ls, group->getGroups()[key.Index]);
562 return 2;
564 break;
565 case CKey::VTView:
566 if (!group || (key.Index + 1) == (sint) group->getViews().size())
568 key.Index = -1;
569 key.ValueType = CKey::VTCtrl; // continue enumeration with controls
571 else
573 ++ key.Index;
574 key.push(ls);
575 pushUIOnStack(ls, group->getViews()[key.Index]);
576 return 2;
578 break;
579 case CKey::VTCtrl:
580 if (!group || (key.Index + 1) == (sint) group->getControls().size())
582 key.Index = -1;
583 key.ValueType = CKey::VTProp; // continue enumeration with properties
584 key.ClassInfo = reflectedObject->getClassInfo();
586 else
588 ++ key.Index;
589 key.push(ls);
590 pushUIOnStack(ls, group->getControls()[key.Index]);
591 return 2;
593 break;
594 case CKey::VTProp:
595 if (!key.ClassInfo)
597 enumerate = false;
598 break;
600 if ((sint) key.ClassInfo->Properties.size() == (key.Index + 1))
602 key.ClassInfo = key.ClassInfo->ParentClass; // continue enumeration in parent class
603 key.Index = -1;
605 else
607 ++ key.Index;
608 key.push(ls);
609 CLuaIHM::luaValueFromReflectedProperty(ls, *reflectedObject, key.ClassInfo->Properties[key.Index]);
610 return 2;
612 break;
613 default:
614 nlassert(0);
615 break;
618 ls.pushNil();
619 return 0;
622 // ***************************************************************************
623 void CLuaIHM::pushUIOnStack(CLuaState &ls, CInterfaceElement *pIE)
625 //H_AUTO(Lua_CLuaIHM_pushUIOnStack)
626 CLuaIHM::pushReflectableOnStack(ls, pIE);
629 // ***************************************************************************
630 bool CLuaIHM::isUIOnStack(CLuaState &ls, sint index)
632 //H_AUTO(Lua_CLuaIHM_isUIOnStack)
633 return getUIOnStack(ls, index) != NULL;
636 // ***************************************************************************
637 CInterfaceElement *CLuaIHM::getUIOnStack(CLuaState &ls, sint index)
639 //H_AUTO(Lua_CLuaIHM_getUIOnStack)
640 return dynamic_cast<CInterfaceElement *>(CLuaIHM::getReflectableOnStack(ls, index));
643 // ***************************************************************************
644 void CLuaIHM::checkArgTypeUIElement(CLuaState &ls, const char *funcName, uint index)
646 //H_AUTO(Lua_CLuaIHM_checkArgTypeUIElement)
647 nlassert(index > 0);
648 if (ls.getTop() < (int) index)
650 CLuaIHM::fails(ls, "%s : argument %d of expected type ui element was not defined", funcName, index);
652 if (!isUIOnStack(ls, index))
654 CLuaIHM::fails(ls, "%s : argument %d of expected type ui element has bad type : %s", funcName, index, ls.getTypename(ls.type(index)), ls.type(index));
659 // ***************************************************************************
660 CInterfaceElement *CLuaIHM::getUIRelative(CInterfaceElement *pIE, const std::string &propName)
662 //H_AUTO(Lua_CLuaIHM_getUIRelative)
663 if (pIE == NULL) return NULL;
664 // If the prop is "parent", then return the parent of the ui
665 if(propName=="parent")
667 return pIE->getParent();
669 // else try to get a child (if group/exist)
670 else
672 CInterfaceGroup *group= dynamic_cast<CInterfaceGroup*>(pIE);
673 if(group)
675 return group->getElement(group->getId()+":"+propName);
679 return NULL;
683 // ***************************************************************************
684 void CLuaIHM::registerBasics(CLuaState &ls)
686 //H_AUTO(Lua_CLuaIHM_registerBasics)
687 using namespace luabind;
688 lua_State *L= ls.getStatePointer();
690 // RGBA
691 module(L)
693 class_<NLMISC::CRGBA>("CRGBA")
694 .def(constructor<>())
695 .def(constructor<const NLMISC::CRGBA &>())
696 .def(constructor<uint8, uint8, uint8>())
697 .def(constructor<uint8, uint8, uint8, uint8>())
698 .def_readwrite("R", &NLMISC::CRGBA::R)
699 .def_readwrite("G", &NLMISC::CRGBA::G)
700 .def_readwrite("B", &NLMISC::CRGBA::B)
701 .def_readwrite("A", &NLMISC::CRGBA::A)
704 #ifdef RYZOM_LUA_UCSTRING
705 // ucstring
706 module(L)
708 class_<ucstring>("ucstring")
709 .def(constructor<>())
710 .def(constructor<const ucstring &>())
711 .def(constructor<const std::string &>())
712 .def(const_self + other<const std::string>())
713 .def(other<const std::string>() + const_self)
714 // NB nico : luabind crash not solved here -> use concatUCString as a replacement
715 // .def(const_self + other<const ucstring &>())
716 .def(const_self < other<const ucstring&>())
717 .def(const_self == other<const ucstring&>())
718 .def("toUtf8", &ucstring::toUtf8)
719 .def("fromUtf8", &ucstring::fromUtf8)
720 .def("substr", &ucstring::luabind_substr)
721 .def(luabind::tostring(const_self)) // __string metamethod
722 .def("toString", (std::string(ucstring::*)()const)&ucstring::toString)
723 //.def(self + other<ucstring>())
725 #endif
727 // CVector2f
728 module(L)
730 class_<NLMISC::CVector2f>("CVector2f")
731 .def(constructor<float ,float>())
732 .def_readwrite("x", &NLMISC::CVector2f::x)
733 .def_readwrite("y", &NLMISC::CVector2f::y)
739 // ***************************************************************************
740 int CLuaIHM::luaMethodCall(lua_State *ls)
742 //H_AUTO(Lua_CLuaIHM_luaMethodCall)
743 nlassert(ls);
744 const CReflectedProperty *prop = (const CReflectedProperty *) lua_touserdata(ls, lua_upvalueindex(1));
745 CLuaState *state = (CLuaState *) lua_touserdata(ls, lua_upvalueindex(2));
746 nlassert(prop);
747 nlassert(prop->Type == CReflectedProperty::LuaMethod);
748 nlassert(state);
749 if (state->empty())
751 state->push(NLMISC::toString("Error while calling lua method %s:%s : no 'self' reference provided, did you you function call '.' instead of method call ':' ?",
752 prop->ParentClass->ClassName.c_str(), prop->Name.c_str())
754 lua_error(ls);
756 // because this is a method, first parameter is the 'this'
757 CReflectableRefPtrTarget *pRPT = getReflectableOnStack(*state, 1);
758 if (!pRPT)
760 state->push(NLMISC::toString("Error while calling lua method %s:%s : 'self' pointer is nil or of bad type, can't make the call.",
761 prop->ParentClass->ClassName.c_str(), prop->Name.c_str())
763 lua_error(ls);
766 state->remove(1); // remove 'self' reference from parameters stack
768 sint numResults = 0;
769 if (pRPT)
771 sint initialStackSize = state->getTop();
774 // call the actual method
775 numResults = (pRPT->*(prop->GetMethod.GetLuaMethod))(*state);
777 catch (const std::exception & e)
779 // restore stack to its initial size
780 state->setTop(initialStackSize);
781 lua_pushstring(ls, e.what());
782 // TODO : see if this is safe to call lua error there" ... (it does a long jump)
783 lua_error(ls);
786 return numResults;
791 // ***************************************************************************
792 int CLuaIHM::setOnDraw(CLuaState &ls)
794 //H_AUTO(Lua_CLuaIHM_setOnDraw)
795 CLuaStackChecker lsc(&ls, 0);
797 // params: CInterfaceGroup*, "script".
798 // return: none
799 CLuaIHM::checkArgCount(ls, "setOnDraw", 2);
800 CLuaIHM::check(ls, CLuaIHM::isUIOnStack(ls, 1), "setOnDraw() requires a UI object in param 1");
801 CLuaIHM::check(ls, ls.isString(2), "setOnDraw() requires a string in param 2");
803 // retrieve args
804 CInterfaceElement *pIE= CLuaIHM::getUIOnStack(ls, 1);
805 std::string script;
806 ls.toString(2, script);
807 nlassert(pIE);
809 // must be a group
810 CInterfaceGroup *group= dynamic_cast<CInterfaceGroup*>(pIE);
811 if(!group)
812 throw ELuaIHMException("setOnDraw(): '%s' is not a group", pIE->getId().c_str());
813 // Set the script to be executed at each draw
814 group->setLuaScriptOnDraw(script);
816 return 0;
819 // ***************************************************************************
820 int CLuaIHM::getOnDraw(CLuaState &ls)
822 //H_AUTO(Lua_CLuaIHM_getOnDraw
823 CLuaStackChecker lsc(&ls, 1);
825 // params: CInterfaceElement*.
826 // return: "script" (nil if empty)
827 CLuaIHM::checkArgCount(ls, "getOnDraw", 1);
828 CLuaIHM::check(ls, CLuaIHM::isUIOnStack(ls, 1), "getOnDraw() requires a UI object in param 1");
830 // retrieve arguments
831 CInterfaceElement *pIE = CLuaIHM::getUIOnStack(ls, 1);
832 if (pIE)
834 // must be a group
835 CInterfaceGroup *group = dynamic_cast<CInterfaceGroup*>(pIE);
836 if (group)
838 if (!group->getLuaScriptOnDraw().empty()) {
839 ls.push(group->getLuaScriptOnDraw());
840 return 1;
844 ls.pushNil();
845 return 1;
848 // ***************************************************************************
849 int CLuaIHM::addOnDbChange(CLuaState &ls)
851 //H_AUTO(Lua_CLuaIHM_addOnDbChange)
852 CLuaStackChecker lsc(&ls, 0);
854 // params: CInterfaceGroup*, "dblist", "script".
855 // return: none
856 CLuaIHM::checkArgCount(ls, "addOnDbChange", 3);
857 CLuaIHM::check(ls, CLuaIHM::isUIOnStack(ls, 1), "addOnDbChange() requires a UI object in param 1");
858 CLuaIHM::check(ls, ls.isString(2), "addOnDbChange() requires a string in param 2");
859 CLuaIHM::check(ls, ls.isString(3), "addOnDbChange() requires a string in param 3");
861 // retrieve args
862 CInterfaceElement *pIE= CLuaIHM::getUIOnStack(ls, 1);
863 std::string dbList, script;
864 ls.toString(2, dbList);
865 ls.toString(3, script);
866 nlassert(pIE);
868 // must be a group
869 CInterfaceGroup *group= dynamic_cast<CInterfaceGroup*>(pIE);
870 if(!group)
871 throw ELuaIHMException("addOnDbChange(): '%s' is not a group", pIE->getId().c_str());
872 // Set the script to be executed when the given DB change
873 group->addLuaScriptOnDBChange(dbList, script);
875 return 0;
879 // ***************************************************************************
880 int CLuaIHM::removeOnDbChange(CLuaState &ls)
882 //H_AUTO(Lua_CLuaIHM_removeOnDbChange)
883 CLuaStackChecker lsc(&ls, 0);
885 // params: CInterfaceGroup*, "dbList"
886 // return: none
887 CLuaIHM::checkArgCount(ls, "removeOnDbChange", 2);
888 CLuaIHM::check(ls, CLuaIHM::isUIOnStack(ls, 1), "removeOnDbChange() requires a UI object in param 1");
889 CLuaIHM::check(ls, ls.isString(2), "removeOnDbChange() requires a string in param 2");
891 // retrieve args
892 CInterfaceElement *pIE= CLuaIHM::getUIOnStack(ls, 1);
893 std::string dbList;
894 ls.toString(2, dbList);
895 nlassert(pIE);
897 // must be a group
898 CInterfaceGroup *group= dynamic_cast<CInterfaceGroup*>(pIE);
899 if(!group)
900 throw ELuaIHMException("removeOnDbChange(): '%s' is not a group", pIE->getId().c_str());
901 // Remove the script to be executed when the given DB change
902 group->removeLuaScriptOnDBChange(dbList);
904 return 0;
909 // ***************************************************************************
910 int CLuaIHM::setCaptureKeyboard(CLuaState &ls)
912 //H_AUTO(Lua_CLuaIHM_setCaptureKeyboard)
913 const char *funcName = "setCaptureKeyboard";
914 CLuaIHM::checkArgCount(ls, funcName, 1);
915 CLuaIHM::checkArgTypeUIElement(ls, funcName, 1);
916 CCtrlBase *ctrl = dynamic_cast<CCtrlBase *>( CLuaIHM::getUIOnStack(ls, 1));
917 if (!ctrl)
919 CLuaIHM::fails(ls, "%s waits a ui control as arg 1", funcName);
921 CWidgetManager::getInstance()->setCaptureKeyboard(ctrl);
922 return 0;
925 // ***************************************************************************
926 int CLuaIHM::resetCaptureKeyboard(CLuaState &ls)
928 //H_AUTO(Lua_CLuaIHM_resetCaptureKeyboard)
929 const char *funcName = "resetCaptureKeyboard";
930 CLuaIHM::checkArgCount(ls, funcName, 0);
931 CWidgetManager::getInstance()->resetCaptureKeyboard();
932 return 0;
935 // ***************************************************************************
936 int CLuaIHM::getUIId(CLuaState &ls)
938 //H_AUTO(Lua_CLuaIHM_getUIId)
939 CLuaStackChecker lsc(&ls, 1);
941 // params: CInterfaceElement*
942 // return: "ui:interface:...". (empty if error)
943 CLuaIHM::checkArgCount(ls, "getUIId", 1);
944 CLuaIHM::check(ls, CLuaIHM::isUIOnStack(ls, 1), "getUIId() requires a UI object in param 1");
946 // retrieve args
947 CInterfaceElement *pIE= CLuaIHM::getUIOnStack(ls, 1);
949 // convert to id
950 if(pIE)
951 ls.push(pIE->getId());
952 else
953 ls.push("");
955 return 1;
960 // ***************************************************************************
961 int CLuaIHM::runAH(CLuaState &ls)
963 //H_AUTO(Lua_CLuaIHM_runAH)
964 CLuaStackChecker lsc(&ls, 0);
966 // params: CInterfaceElement *, "ah", "params".
967 // return: none
968 CLuaIHM::checkArgCount(ls, "runAH", 3);
969 CLuaIHM::check(ls, CLuaIHM::isUIOnStack(ls, 1) || ls.isNil(1), "runAH() requires a UI object in param 1 (or Nil)");
970 CLuaIHM::check(ls, ls.isString(2), "runAH() requires a string in param 2");
971 CLuaIHM::check(ls, ls.isString(3), "runAH() requires a string in param 3");
973 // retrieve args
974 CInterfaceElement *pIE= CLuaIHM::getUIOnStack(ls, 1);
975 std::string ah, params;
976 ls.toString(2, ah);
977 ls.toString(3, params);
979 // run AH
980 // The element must be ctrl (or NULL)
981 CCtrlBase *ctrl= NULL;
982 if(pIE)
984 ctrl= dynamic_cast<CCtrlBase*>(pIE);
985 if(!ctrl)
986 throw ELuaIHMException("runAH(): '%s' is not a ctrl", pIE->getId().c_str());
988 CAHManager::getInstance()->runActionHandler(ah, ctrl, params);
990 return 0;
993 // ***************************************************************************
994 int CLuaIHM::getWindowSize(CLuaState &ls)
996 //H_AUTO(Lua_CLuaIHM_getWindowSize)
997 CLuaIHM::checkArgCount(ls, "getWindowSize", 0);
998 uint32 w, h;
999 CViewRenderer::getInstance()->getScreenSize(w, h);
1000 ls.push(w);
1001 ls.push(h);
1002 return 2;
1005 // ***************************************************************************
1006 int CLuaIHM::setTopWindow(CLuaState &ls)
1008 //H_AUTO(Lua_CLuaIHM_setTopWindow)
1009 const char *funcName = "setTopWindow";
1010 CLuaIHM::checkArgCount(ls, funcName, 1);
1011 CInterfaceGroup *wnd = dynamic_cast<CInterfaceGroup *>( CLuaIHM::getUIOnStack(ls, 1));
1012 if (!wnd)
1014 CLuaIHM::fails(ls, "%s : interface group expected as arg 1", funcName);
1016 CWidgetManager::getInstance()->setTopWindow(wnd);
1017 return 0;
1020 int CLuaIHM::getTextureSize(CLuaState &ls)
1022 //H_AUTO(Lua_CLuaIHM_getTextureSize)
1023 const char *funcName = "getTextureSize";
1024 CLuaIHM::checkArgCount(ls, funcName, 1);
1025 CLuaIHM::checkArgType(ls, funcName, 1, LUA_TSTRING);
1026 std::string textureName = ls.toString(1);
1028 CBitmap bitmap;
1029 CIFile fs(CPath::lookup(textureName).c_str());
1030 bitmap.load(fs);
1032 ls.push(bitmap.getWidth());
1033 ls.push(bitmap.getHeight());
1035 return 2;
1040 // ***************************************************************************
1041 int CLuaIHM::disableModalWindow(CLuaState &ls)
1043 //H_AUTO(Lua_CLuaIHM_disableModalWindow)
1044 CLuaIHM::checkArgCount(ls, "disableModalWindow", 0);
1045 CWidgetManager::getInstance()->disableModalWindow();
1046 return 0;
1049 // ***************************************************************************
1050 int CLuaIHM::deleteUI(CLuaState &ls)
1052 //H_AUTO(Lua_CLuaIHM_deleteUI)
1053 CLuaStackChecker lsc(&ls, 0);
1055 // params: CInterfaceElement *
1056 // return: none
1057 CLuaIHM::checkArgCount(ls, "deleteUI", 1);
1058 CLuaIHM::check(ls, CLuaIHM::isUIOnStack(ls, 1), "deleteUI() requires a UI object in param 1");
1060 // retrieve args
1061 CInterfaceElement *pIE= CLuaIHM::getUIOnStack(ls, 1);
1062 if(!pIE)
1063 return 0;
1065 // has a parent?
1066 CInterfaceGroup *parent= pIE->getParent();
1067 if(parent)
1069 // correctly remove from parent
1070 parent->delElement(pIE);
1072 else
1074 // just delete
1075 delete pIE;
1078 return 0;
1081 // ***************************************************************************
1082 int CLuaIHM::deleteReflectable(CLuaState &ls)
1084 //H_AUTO(Lua_CLuaIHM_deleteReflectable)
1085 CLuaStackChecker lsc(&ls, 0);
1087 // params: CInterfaceElement *
1088 // return: none
1089 CLuaIHM::checkArgCount(ls, "deleteReflectable", 1);
1090 CLuaIHM::check(ls, CLuaIHM::isReflectableOnStack(ls, 1), "deleteReflectable() requires a reflectable C++ object in param 1");
1092 // retrieve args
1093 CReflectableRefPtrTarget *pRPT= CLuaIHM::getReflectableOnStack(ls, 1);
1094 if(!pRPT)
1095 return 0;
1098 CInterfaceElement *pIE = dynamic_cast<CInterfaceElement *>(pRPT);
1100 if (pIE)
1102 // has a parent?
1103 CInterfaceGroup *parent= pIE->getParent();
1104 if(parent)
1106 // correctly remove from parent
1107 parent->delElement(pIE);
1111 // just delete
1112 delete pIE;
1114 return 0;
1118 int CLuaIHM::getCurrentWindowUnder(CLuaState &ls)
1120 //H_AUTO(Lua_CLuaIHM_getCurrentWindowUnder)
1121 CLuaStackChecker lsc(&ls, 1);
1122 CInterfaceElement *pIE= CWidgetManager::getInstance()->getCurrentWindowUnder();
1123 if(!pIE)
1125 ls.pushNil();
1127 else
1129 CLuaIHM::pushUIOnStack(ls, pIE);
1131 return 1;
1134 // ***************************************************************************
1135 bool CLuaIHM::fileExists(const std::string &fileName)
1137 //H_AUTO(Lua_CLuaIHM_fileExists)
1138 return CPath::exists(fileName);
1141 // ***************************************************************************
1142 int CLuaIHM::runExprAndPushResult(CLuaState &ls, const std::string &expr)
1144 //H_AUTO(Lua_CLuaIHM_runExprAndPushResult)
1145 // Execute expression
1146 CInterfaceExprValue value;
1147 if (CInterfaceExpr::eval(expr, value, NULL))
1149 switch(value.getType())
1151 case CInterfaceExprValue::Boolean:
1152 ls.push(value.getBool());
1153 break;
1154 case CInterfaceExprValue::Integer:
1155 ls.push(value.getInteger());
1156 break;
1157 case CInterfaceExprValue::Double:
1158 ls.push(value.getDouble());
1159 break;
1160 case CInterfaceExprValue::String:
1161 ls.push(value.getString());
1162 break;
1163 case CInterfaceExprValue::RGBA:
1165 CRGBA color = value.getRGBA();
1166 #if defined(LUABIND_STACK_HPP_INCLUDED)
1167 luabind::push(ls.getStatePointer(), color);
1168 #elif (LUABIND_VERSION > 600)
1169 luabind::detail::push(ls.getStatePointer(), color);
1170 #else
1171 luabind::object obj(ls.getStatePointer(), color);
1172 obj.pushvalue();
1173 #endif
1174 break;
1176 break;
1177 case CInterfaceExprValue::UserType: // Yoyo: don't care UserType...
1178 default:
1179 ls.pushNil();
1180 break;
1183 else
1184 ls.pushNil();
1186 return 1;
1189 // ***************************************************************************
1190 int CLuaIHM::runExpr(CLuaState &ls)
1192 //H_AUTO(Lua_CLuaIHM_runExpr)
1193 CLuaStackChecker lsc(&ls, 1);
1195 // params: "expr".
1196 // return: any of: nil, bool, string, number, RGBA, UCString
1197 CLuaIHM::checkArgCount(ls, "runExpr", 1);
1198 CLuaIHM::check(ls, ls.isString(1), "runExpr() requires a string in param 1");
1200 // retrieve args
1201 std::string expr;
1202 ls.toString(1, expr);
1204 // run expression and push result
1205 return runExprAndPushResult(ls, expr);
1208 // ***************************************************************************
1209 int CLuaIHM::runFct(CLuaState &ls)
1211 //H_AUTO(Lua_CLuaIHM_runFct)
1212 CLuaStackChecker lsc(&ls, 1);
1214 // params: "expr", param1, param2...
1215 // return: any of: nil, bool, string, number, RGBA, UCString
1216 CLuaIHM::checkArgMin(ls, "runFct", 1);
1217 CLuaIHM::check(ls, ls.isString(1), "runExpr() requires a string in param 1");
1219 // retrieve fct
1220 std::string expr;
1221 ls.toString(1, expr);
1222 expr+= "(";
1224 // retrieve params
1225 uint top= ls.getTop();
1226 for(uint i=2;i<=top;i++)
1228 if(i>2)
1229 expr+= ", ";
1231 // If it is a number
1232 if(ls.type(i)==LUA_TNUMBER)
1234 std::string paramValue;
1235 ls.toString(i, paramValue); // nb: transformed to a string in the stack
1236 expr+= paramValue;
1238 // else suppose a string
1239 else
1241 // must enclose with "'"
1242 std::string paramValue;
1243 ls.toString(i, paramValue);
1244 expr+= std::string("'") + paramValue + std::string("'") ;
1248 // end fct call
1249 expr+= ")";
1252 // run expression and push result
1253 return runExprAndPushResult(ls, expr);
1256 // ***************************************************************************
1257 int CLuaIHM::runCommand(CLuaState &ls)
1259 //H_AUTO(Lua_CLuaIHM_runCommand)
1260 CLuaStackChecker lsc(&ls, 1);
1261 if (ls.empty())
1263 nlwarning("'runCommand' : Command name expected");
1264 ls.push(false);
1265 return 1;
1267 const char *commandName = ls.toString(1);
1268 if (!commandName)
1270 nlwarning("'runCommand' : Bad command name");
1271 ls.push(false);
1272 return 1;
1274 if (!NLMISC::ICommand::LocalCommands || !NLMISC::ICommand::LocalCommands->count(ls.toString(1)))
1276 nlwarning("'runCommand' : Command %s not found", ls.toString(1));
1277 ls.push(false);
1278 return 1;
1280 std::string rawCommandString = ls.toString(1);
1281 NLMISC::ICommand *command = (*NLMISC::ICommand::LocalCommands)[ls.toString(1)];
1282 nlassert(command);
1283 std::vector<std::string> args(ls.getTop() - 1);
1284 for(uint k = 2; k <= (uint) ls.getTop(); ++k)
1286 if (ls.toString(k))
1288 args[k - 2] = ls.toString(k);
1289 rawCommandString += " " + std::string(ls.toString(k));
1293 ls.push(command->execute(rawCommandString, args, NLMISC::ErrorLog(), false, true));
1294 return 1;
1297 #ifdef RYZOM_LUA_UCSTRING
1298 // ***************************************************************************
1299 int CLuaIHM::isUCString(CLuaState &ls)
1301 //H_AUTO(Lua_CLuaIHM_isUCString)
1302 const char *funcName = "isUCString";
1303 CLuaIHM::checkArgCount(ls, funcName, 1);
1304 ls.push(CLuaIHM::isUCStringOnStack(ls, 1));
1305 return 1;
1308 // ***************************************************************************
1309 int CLuaIHM::concatUCString(CLuaState &ls)
1311 //H_AUTO(Lua_CLuaIHM_concatUCString)
1312 const char *funcName = "concatUCString";
1313 ucstring result;
1314 for (uint k = 1; k <= (uint) ls.getTop(); ++k)
1316 //nlwarning("arg %d = %s", k, ls.getTypename(ls.type(k)));
1317 ucstring part;
1318 if (ls.isString(k))
1320 part.fromUtf8(ls.toString(k));
1322 else
1324 CLuaIHM::checkArgTypeUCString(ls, funcName, k);
1325 nlverify(CLuaIHM::getUCStringOnStack(ls, k, part));
1327 result += part;
1329 CLuaIHM::push(ls, result);
1330 return 1;
1332 #endif
1334 // ***************************************************************************
1335 int CLuaIHM::concatString(CLuaState &ls)
1337 //H_AUTO(Lua_CLuaIHM_concatUCString)
1338 const char *funcName = "concatString";
1339 std::string result;
1340 uint stackSize = ls.getTop();
1341 for (uint k = 1; k <= stackSize; ++k)
1343 CLuaIHM::checkArgType(ls, funcName, k, LUA_TSTRING);
1344 result += ls.toString(k);
1346 ls.push(result);
1347 return 1;
1350 // ***************************************************************************
1351 int CLuaIHM::tableToString(CLuaState &ls)
1353 const char *funcName = "tableToString";
1354 CLuaIHM::checkArgCount(ls, funcName, 1);
1355 CLuaIHM::checkArgType(ls, funcName, 1, LUA_TTABLE);
1356 uint length = 0;
1357 // compute size
1358 ls.pushNil();
1359 while (ls.next(-2))
1361 ls.toString(-1);
1362 length += (uint)ls.strlen(-1);
1363 ls.pop(2);
1365 std::string result;
1366 result.resize(length);
1367 char *dest = &result[0];
1368 // concatenate
1369 ls.pushNil();
1370 while (ls.next(-2))
1372 uint length = (uint)ls.strlen(-1);
1373 if (length)
1375 memcpy(dest, ls.toString(-1), length);
1377 dest += length;
1378 ls.pop(2);
1380 ls.push(result);
1381 return 1;
1385 int CLuaIHM::getPathContent(CLuaState &ls)
1387 //H_AUTO(Lua_CLuaIHM_getPathContent)
1388 const char *funcName = "getPathContent";
1389 CLuaIHM::checkArgCount(ls, funcName, 1);
1390 CLuaIHM::checkArgType(ls, funcName, 1, LUA_TSTRING);
1391 std::vector<std::string> files;
1392 NLMISC::CPath::getPathContent(ls.toString(1), false, false, true, files);
1393 ls.newTable();
1394 for(uint k = 0; k < files.size(); ++k)
1396 ls.push(k);
1397 ls.push(files[k]);
1398 ls.setTable(-3);
1400 return 1;
1406 // ***************************************************************************
1407 void CLuaIHM::luaValueFromReflectedProperty(CLuaState &ls, CReflectable &reflectedObject, const CReflectedProperty &property)
1409 //H_AUTO(Lua_CLuaIHM_luaValueFromReflectedProperty)
1410 switch(property.Type)
1412 case CReflectedProperty::Boolean:
1413 ls.push( (reflectedObject.*(property.GetMethod.GetBool))() );
1414 break;
1415 case CReflectedProperty::SInt32:
1416 ls.push( (reflectedObject.*(property.GetMethod.GetSInt32))() );
1417 break;
1418 case CReflectedProperty::Float:
1419 ls.push( (reflectedObject.*(property.GetMethod.GetFloat))() );
1420 break;
1421 case CReflectedProperty::String:
1422 ls.push( (reflectedObject.*(property.GetMethod.GetString))() );
1423 break;
1424 #ifdef RYZOM_LUA_UCSTRING
1425 case CReflectedProperty::UCString:
1427 ucstring str = (reflectedObject.*(property.GetMethod.GetUCString))();
1428 #if defined(LUABIND_STACK_HPP_INCLUDED)
1429 luabind::push(ls.getStatePointer(), str);
1430 #elif (LUABIND_VERSION > 600)
1431 luabind::detail::push(ls.getStatePointer(), str);
1432 #else
1433 luabind::object obj(ls.getStatePointer(), str);
1434 obj.pushvalue();
1435 #endif
1437 break;
1438 case CReflectedProperty::UCStringRef:
1440 ucstring str = (reflectedObject.*(property.GetMethod.GetUCStringRef))();
1441 #if defined(LUABIND_STACK_HPP_INCLUDED)
1442 luabind::push(ls.getStatePointer(), str);
1443 #elif (LUABIND_VERSION > 600)
1444 luabind::detail::push(ls.getStatePointer(), str);
1445 #else
1446 luabind::object obj(ls.getStatePointer(), str);
1447 obj.pushvalue();
1448 #endif
1450 break;
1451 #endif
1452 case CReflectedProperty::StringRef:
1453 ls.push( (reflectedObject.*(property.GetMethod.GetStringRef))() );
1454 break;
1455 case CReflectedProperty::RGBA:
1457 CRGBA color = (reflectedObject.*(property.GetMethod.GetRGBA))();
1458 #if defined(LUABIND_STACK_HPP_INCLUDED)
1459 luabind::push(ls.getStatePointer(), color);
1460 #elif (LUABIND_VERSION > 600)
1461 luabind::detail::push(ls.getStatePointer(), color);
1462 #else
1463 luabind::object obj(ls.getStatePointer(), color);
1464 obj.pushvalue();
1465 #endif
1467 break;
1468 case CReflectedProperty::LuaMethod:
1470 // must create a closure that will forward the call to the real method
1471 if (!property.LuaMethodRef.isValid())
1473 ls.pushLightUserData((void *) &property);
1474 ls.pushLightUserData((void *) &ls);
1475 ls.pushCClosure(luaMethodCall, 2);
1476 property.LuaMethodRef.pop(ls);
1478 nlassert(property.LuaMethodRef.getLuaState() == &ls); // only one single lua state supported for now
1479 property.LuaMethodRef.push();
1481 break;
1482 default:
1483 nlstop;
1484 break;
1488 // ***************************************************************************
1489 void CLuaIHM::luaValueToReflectedProperty(CLuaState &ls, int stackIndex, CReflectable &target, const CReflectedProperty &property)
1491 //H_AUTO(Lua_property_throw)
1492 if(ls.isNil(stackIndex))
1493 throw ELuaIHMException("Trying to set nil to UI property '%s'", property.Name.c_str());
1494 switch(property.Type)
1496 case CReflectedProperty::Boolean:
1498 bool val= ls.toBoolean(stackIndex);
1499 (target.*(property.SetMethod.SetBool))(val);
1500 return;
1502 case CReflectedProperty::SInt32:
1504 sint32 val= (sint32)ls.toInteger(stackIndex);
1505 (target.*(property.SetMethod.SetSInt32))(val);
1506 return;
1508 case CReflectedProperty::UInt32:
1510 uint32 val= (uint32)ls.toInteger(stackIndex);
1511 (target.*(property.SetMethod.SetUInt32))(val);
1512 return;
1514 case CReflectedProperty::Float:
1516 float val= (float)ls.toNumber(stackIndex);
1517 (target.*(property.SetMethod.SetFloat))(val);
1518 return;
1520 case CReflectedProperty::String:
1521 case CReflectedProperty::StringRef:
1523 std::string val;
1524 ls.toString(stackIndex, val);
1525 (target.*(property.SetMethod.SetString))(val);
1526 return;
1528 #ifdef RYZOM_LUA_UCSTRING
1529 case CReflectedProperty::UCString:
1530 case CReflectedProperty::UCStringRef:
1532 ucstring val;
1533 // Additionaly return of CInterfaceExpr may be std::string... test std string too
1534 if(ls.isString() || ls.isNumber() || ls.isInteger())
1536 std::string str;
1537 ls.toString(stackIndex, str);
1538 val= str;
1540 #ifdef RYZOM_LUA_UCSTRING
1541 else
1543 // else this should be a ucstring
1544 if (!pop(ls, val))
1546 throw ELuaIHMException("You must set a string, number or ucstring to UI property '%s'", property.Name.c_str());
1549 #endif
1550 (target.*(property.SetMethod.SetUCString))(val);
1551 return;
1553 #endif
1554 case CReflectedProperty::RGBA:
1556 CRGBA color;
1557 if (pop(ls, color))
1559 (target.*(property.SetMethod.SetRGBA))(color);
1561 else
1563 throw ELuaIHMException("You must set a CRGBA to UI property '%s'", property.Name.c_str());
1565 return;
1567 default:
1568 nlstop;
1573 // ***************************************************************************
1574 void CLuaIHM::createLuaEnumTable(CLuaState &ls, const std::string &str)
1576 //H_AUTO(Lua_CLuaIHM_createLuaEnumTable)
1577 std::string path = "", script, p;
1578 CSString s = str;
1579 // Create table recursively (ex: 'game.TPVPClan' will check/create the table 'game' and 'game.TPVPClan')
1580 p = s.splitTo('.', true);
1581 while (!p.empty())
1583 if (path.empty() )
1584 path = p;
1585 else
1586 path += "." + p;
1587 script = "if (" + path + " == nil) then " + path + " = {}; end";
1588 ls.executeScript(script);
1589 p = s.splitTo('.', true);
1593 #define LUABIND_ENUM(__enum__, __name__, __num__, __toStringFunc__) \
1594 createLuaEnumTable(ls, __name__); \
1595 for (uint e=0 ; e<__num__ ; e++) \
1597 std::string str = __toStringFunc__((__enum__)e); \
1598 std::string temp = __name__ + toString(".") + __toStringFunc__((__enum__)e) + " = " + toString("%u;", e); \
1599 ls.executeScript(temp); \
1602 // ***************************************************************************
1603 #define LUABIND_FUNC(__func__) luabind::def(#__func__, &__func__)
1605 void CLuaIHM::registerIHM(CLuaState &ls)
1607 //H_AUTO(Lua_CLuaIHM_registerIHM)
1608 CLuaStackChecker lsc(&ls);
1610 // *** Register a Table for ui env.
1611 ls.push(IHM_LUA_ENVTABLE); // "__ui_envtable"
1612 ls.newTable(); // "__ui_envtable" {}
1613 ls.setTable(LUA_REGISTRYINDEX);
1616 // *** Register the MetaTable for UI userdata
1617 ls.push(IHM_LUA_METATABLE); // "__ui_metatable"
1618 ls.newTable(); // "__ui_metatable" {}
1619 // set the '__index' method
1620 ls.push("__index");
1621 ls.push(luaUIIndex);
1622 nlassert(ls.isCFunction());
1623 ls.setTable(-3); // "__ui_metatable" {"__index"= CFunc_luaUIIndex}
1624 // set the '__newindex' method
1625 ls.push("__newindex");
1626 ls.push(luaUINewIndex);
1627 nlassert(ls.isCFunction());
1628 ls.setTable(-3);
1629 // set the '__newindex' method
1630 ls.push("__gc");
1631 ls.push(luaUIDtor);
1632 nlassert(ls.isCFunction());
1633 ls.setTable(-3);
1634 // set the '__eq' method
1635 ls.push("__eq");
1636 ls.push(luaUIEq);
1637 nlassert(ls.isCFunction());
1638 ls.setTable(-3);
1639 // set the custom '__next' method
1640 ls.push("__next");
1641 ls.push(luaUINext);
1642 nlassert(ls.isCFunction());
1643 ls.setTable(-3);
1644 // set registry
1645 ls.setTable(LUA_REGISTRYINDEX);
1648 // *** Register Functions
1649 ls.registerFunc("setOnDraw", setOnDraw);
1650 ls.registerFunc("getOnDraw", getOnDraw);
1651 ls.registerFunc("setCaptureKeyboard", setCaptureKeyboard);
1652 ls.registerFunc("resetCaptureKeyboard", resetCaptureKeyboard);
1653 ls.registerFunc("setTopWindow", setTopWindow);
1654 ls.registerFunc("addOnDbChange", addOnDbChange);
1655 ls.registerFunc("removeOnDbChange", removeOnDbChange);
1656 ls.registerFunc("getUIId", getUIId);
1657 ls.registerFunc("runAH", runAH);
1658 ls.registerFunc("deleteUI", deleteUI);
1659 ls.registerFunc("deleteReflectable", deleteReflectable);
1660 ls.registerFunc("getWindowSize", getWindowSize);
1661 ls.registerFunc("getTextureSize", getTextureSize);
1662 ls.registerFunc("disableModalWindow", disableModalWindow);
1663 #ifdef RYZOM_LUA_UCSTRING
1664 ls.registerFunc("isUCString", isUCString);
1665 ls.registerFunc("concatUCString", concatUCString);
1666 #endif
1667 ls.registerFunc("concatString", concatString);
1668 ls.registerFunc("tableToString", tableToString);
1669 ls.registerFunc("getCurrentWindowUnder", getCurrentWindowUnder);
1670 ls.registerFunc("runExpr", runExpr);
1671 ls.registerFunc("runFct", runFct);
1672 ls.registerFunc("runCommand", runCommand);
1673 ls.registerFunc("getPathContent", getPathContent);
1675 // Through LUABind API
1676 lua_State *L= ls.getStatePointer();
1678 luabind::module(L)
1680 luabind::def("findReplaceAll", (std::string(*)(const std::string &, const std::string &, const std::string &)) &findReplaceAll),
1681 luabind::def("findReplaceAll", (ucstring(*)(const ucstring &, const ucstring &, const ucstring &)) &findReplaceAll),
1682 luabind::def("findReplaceAll", (ucstring(*)(const ucstring &, const std::string &, const std::string &)) &findReplaceAll),
1683 luabind::def("findReplaceAll", (ucstring(*)(const ucstring &, const ucstring &, const std::string &)) &findReplaceAll),
1684 luabind::def("findReplaceAll", (ucstring(*)(const ucstring &, const std::string &, const ucstring &)) &findReplaceAll),
1686 #if !FINAL_VERSION
1687 LUABIND_FUNC(openDoc),
1688 LUABIND_FUNC(launchProgram),
1689 #endif
1691 luabind::def("fileLookup", CMiscFunctions::fileLookup),
1692 luabind::def("shellExecute", CMiscFunctions::shellExecute),
1693 LUABIND_FUNC(fileExists)
1696 // inside i18n table
1697 luabind::module(L, "i18n")
1699 #ifdef RYZOM_LUA_UCSTRING
1700 luabind::def("get", &CI18N::getAsUtf16), // Compatibility
1701 #else
1702 luabind::def("get", &CI18N::get),
1703 #endif
1704 luabind::def("hasTranslation", &CI18N::hasTranslation)
1706 // inside 'nlfile' table
1707 luabind::module(L, "nlfile")
1709 luabind::def("getFilename", NLMISC::CFile::getFilename),
1710 luabind::def("getExtension", NLMISC::CFile::getExtension),
1711 luabind::def("getFilenameWithoutExtension", NLMISC::CFile::getFilenameWithoutExtension)
1713 // inside 'nltime' table
1714 luabind::module(L, "nltime")
1716 luabind::def("getPreciseLocalTime", getPreciseLocalTime),
1717 luabind::def("getSecondsSince1970", NLMISC::CTime::getSecondsSince1970),
1718 luabind::def("getLocalTime", getLocalTime) // NB : use CLuaIHM::getLocalTime instead of NLMISC::CTime::getLocalTime, because the NLMISC
1719 // version returns a uint64, which can't be casted into lua numbers (doubles ...)
1724 // ***************************************************************************
1725 double CLuaIHM::getPreciseLocalTime()
1727 //H_AUTO(Lua_CLuaIHM_getPreciseLocalTime)
1728 // don't export these 2 function to lua directly here, because all uint64 can't be represented with lua 'numbers'
1729 return NLMISC::CTime::ticksToSecond(NLMISC::CTime::getPerformanceTime());
1732 // ***************************************************************************
1733 void CLuaIHM::registerAll(CLuaState &ls)
1735 //H_AUTO(Lua_CLuaIHM_registerAll)
1736 registerBasics(ls);
1737 registerIHM(ls);
1742 //#define CHECK_REFLECTABLE_MT
1745 // ***************************************************************************
1746 void CLuaIHM::pushReflectableOnStack(CLuaState &ls, class CReflectableRefPtrTarget *pRPT)
1748 //H_AUTO(Lua_CLuaIHM_pushReflectableOnStack)
1749 nlassert(pRPT);
1750 CLuaStackChecker lsc(&ls, 1);
1752 if (!pRPT)
1754 ls.pushNil();
1755 return;
1758 //ls.dumpStack();
1759 /** if there's already a ref ptr for this object in the registry, then use it,
1760 * else create a new one to avoid costly allocations
1762 ls.pushLightUserData(pRPT);
1763 ls.getTable(LUA_REGISTRYINDEX);
1764 //ls.dumpStack();
1765 if (ls.isNil())
1767 ls.pop(1);
1768 //ls.dumpStack();
1769 // allocate the user data where to put the ref ptr
1770 void *ptr= ls.newUserData(sizeof(CReflectableLuaRef));
1771 nlassert(ptr);
1772 //ls.dumpStack();
1774 // disable memory leaks detection for placement new
1775 #ifdef new
1776 #undef new
1777 #endif
1779 // initialize it, and copy the given element
1780 new (ptr) CReflectableLuaRef(pRPT);
1782 // reenable memory leaks detection for placement new
1783 #ifdef DEBUG_NEW
1784 #define new DEBUG_NEW
1785 #endif
1787 // Assign to this user data the __ui_metatable
1788 //ls.dumpStack();
1789 ls.push(IHM_LUA_METATABLE); // userdata "__ui_metatable"
1790 //ls.dumpStack();
1791 ls.getTable(LUA_REGISTRYINDEX); // userdata __ui_metatable
1792 //ls.dumpStack();
1793 nlverify(ls.setMetaTable(-2)); // userdata
1794 //ls.dumpStack();
1796 // cache in registry
1797 ls.pushLightUserData(pRPT);
1798 ls.pushValue(-2); // copy for table insertion
1799 //ls.dumpStack();
1800 ls.setTable(LUA_REGISTRYINDEX);
1801 //ls.dumpStack();
1804 // Check that the metatable is correct
1805 #ifdef CHECK_REFLECTABLE_MT
1806 nlverify(ls.getMetaTable(-1)); // userdata __ui_metatable
1807 ls.push("__index");
1808 ls.getTable(-2);
1809 ls.push("__newindex");
1810 ls.getTable(-3);
1811 ls.push("__gc");
1812 ls.getTable(-4);
1813 nlassert(ls.isCFunction(-1));
1814 nlassert(ls.isCFunction(-2));
1815 nlassert(ls.isCFunction(-3));
1816 ls.pop(4);
1817 #endif
1818 //ls.dumpStack();
1821 // ***************************************************************************
1822 bool CLuaIHM::isReflectableOnStack(CLuaState &ls, sint index)
1824 //H_AUTO(Lua_CLuaIHM_isReflectableOnStack)
1825 CLuaStackChecker lsc(&ls);
1827 if(!ls.isUserData(index))
1828 return false;
1829 // verify that it is a UI with its metatable
1830 if(!ls.getMetaTable(index)) // ??? object_metatable
1831 return false;
1832 ls.push(IHM_LUA_METATABLE); // ??? object_metatable "__ui_metatable"
1833 ls.getTable(LUA_REGISTRYINDEX); // ??? object_metatable __ui_metatable
1834 // equal test
1835 bool ok= ls.rawEqual(-2, -1);
1836 // Also must not be nil (maybe nil in case of LuaIHM still not registered)
1837 ok= ok && !ls.isNil(-1);
1838 ls.pop();
1839 ls.pop();
1841 return ok;
1844 // ***************************************************************************
1845 CReflectableRefPtrTarget *CLuaIHM::getReflectableOnStack(CLuaState &ls, sint index)
1847 //H_AUTO(Lua_CLuaIHM_getReflectableOnStack)
1848 if(!isReflectableOnStack(ls, index))
1849 return NULL;
1851 CReflectableLuaRef *p= (CReflectableLuaRef *) ls.toUserData(index);
1852 nlassert(p->Ptr);
1853 return p->Ptr;
1857 // ***************************************************************************
1858 // ***************************************************************************
1859 // LUA IHM Functions
1860 // ***************************************************************************
1861 // ***************************************************************************
1864 // ***************************************************************************
1865 uint32 CLuaIHM::getLocalTime()
1867 //H_AUTO(Lua_CLuaIHM_getLocalTime)
1868 return (uint32) NLMISC::CTime::getLocalTime();
1872 // ***************************************************************************
1873 std::string CLuaIHM::findReplaceAll(const std::string &str, const std::string &search, const std::string &replace)
1875 //H_AUTO(Lua_CLuaIHM_findReplaceAll)
1876 std::string ret= str;
1877 while(strFindReplace(ret, search, replace));
1878 return ret;
1881 #ifdef RYZOM_LUA_UCSTRING
1882 // ***************************************************************************
1883 ucstring CLuaIHM::findReplaceAll(const ucstring &str, const ucstring &search, const ucstring &replace)
1885 //H_AUTO(Lua_CLuaIHM_findReplaceAll)
1886 return strFindReplaceAll(str, search, replace);
1889 // ***************************************************************************
1890 ucstring CLuaIHM::findReplaceAll(const ucstring &str, const std::string &search, const std::string &replace)
1892 //H_AUTO(Lua_CLuaIHM_findReplaceAll)
1893 return findReplaceAll(str, ucstring::makeFromUtf8(search), ucstring::makeFromUtf8(replace));
1896 // ***************************************************************************
1897 ucstring CLuaIHM::findReplaceAll(const ucstring &str, const std::string &search, const ucstring &replace)
1899 //H_AUTO(Lua_CLuaIHM_findReplaceAll)
1900 return findReplaceAll(str, ucstring::makeFromUtf8(search), replace);
1903 // ***************************************************************************
1904 ucstring CLuaIHM::findReplaceAll(const ucstring &str, const ucstring &search, const std::string &replace)
1906 //H_AUTO(Lua_CLuaIHM_findReplaceAll)
1907 return findReplaceAll(str, search, ucstring::makeFromUtf8(replace));
1909 #endif
1911 // ***************************************************************************
1912 void CLuaIHM::fails(CLuaState &ls, const char *format, ...)
1914 //H_AUTO(Lua_CLuaIHM_fails)
1915 std::string reason;
1916 NLMISC_CONVERT_VARGS (reason, format, NLMISC::MaxCStringSize);
1917 std::string stack;
1918 ls.getStackAsString(stack);
1919 // use a std::exception, to avoid Nel Exception warning
1920 throw ELuaIHMException("%s. Lua stack = \n %s", reason.c_str(), stack.c_str());
1924 // ***************************************************************************
1925 void CLuaIHM::checkArgCount(CLuaState &ls, const char* funcName, uint nArgs)
1927 //H_AUTO(Lua_CLuaIHM_checkArgCount)
1928 if(ls.getTop()!=(sint)nArgs)
1930 fails(ls, "%s() need exactly %d arguments (tips : check between method & function call)", funcName, nArgs);
1934 // ***************************************************************************
1935 void CLuaIHM::checkArgMin(CLuaState &ls, const char* funcName, uint nArgs)
1937 //H_AUTO(Lua_CLuaIHM_checkArgMin)
1938 if(ls.getTop()<(sint)nArgs)
1940 fails(ls, "%s() need at least %d arguments (tips : check between method & function call)", funcName, nArgs);
1944 // ***************************************************************************
1945 void CLuaIHM::checkArgMax(CLuaState &ls,const char* funcName,uint nArgs)
1947 //H_AUTO(Lua_CLuaIHM_checkArgMax)
1948 if(ls.getTop()>(sint)nArgs)
1950 fails(ls, "%s() need at most %d arguments.", funcName, nArgs);
1954 // ***************************************************************************
1955 void CLuaIHM::check(CLuaState &ls, bool ok, const std::string &failReason)
1957 //H_AUTO(Lua_CLuaIHM_check)
1958 if(!ok)
1960 fails(ls, failReason.c_str());
1964 // ***************************************************************************
1965 void CLuaIHM::checkArgType(CLuaState &ls, const char *funcName, uint index, int argType)
1967 //H_AUTO(Lua_CLuaIHM_checkArgType)
1968 nlassert(index > 0);
1969 if (ls.getTop() < (int) index)
1971 fails(ls, "%s : argument %d of expected type %s was not defined", funcName, index, ls.getTypename(argType));
1973 if (ls.type(index) != argType)
1975 fails(ls, "%s : argument %d of expected type %s has bad type : %s", funcName, index, ls.getTypename(argType), ls.getTypename(ls.type(index)), ls.type(index));
1979 // ***************************************************************************
1980 void CLuaIHM::checkArgTypeRGBA(CLuaState &ls, const char *funcName, uint index)
1982 //H_AUTO(Lua_CLuaIHM_checkArgTypeRGBA)
1983 nlassert(index > 0);
1984 if (ls.getTop() < (int) index)
1986 fails(ls, "%s : argument %d of expected type RGBA was not defined", funcName, index);
1988 ls.pushValue(index);
1989 CRGBA dummy;
1990 if (!pop(ls, dummy))
1992 fails(ls, "%s : argument %d of expected type RGBA has bad type : %s", funcName, index, ls.getTypename(ls.type(index)), ls.type(index));
1996 #ifdef RYZOM_LUA_UCSTRING
1997 // ***************************************************************************
1998 void CLuaIHM::checkArgTypeUCString(CLuaState &ls, const char *funcName, uint index)
2000 //H_AUTO(Lua_CLuaIHM_checkArgTypeUCString)
2001 nlassert(index > 0);
2002 if (ls.getTop() < (int) index)
2004 fails(ls, "%s : argument %d of expected type ucstring was not defined", funcName, index);
2006 ls.pushValue(index);
2007 ucstring dummy;
2008 if (!pop(ls, dummy))
2010 fails(ls, "%s : argument %d of expected type ucstring has bad type : %s", funcName, index, ls.getTypename(ls.type(index)), ls.type(index));
2013 #endif
2016 // ***************************************************************************
2017 bool CLuaIHM::popString(CLuaState &ls, std::string & dest)
2019 //H_AUTO(Lua_CLuaIHM_popString)
2022 #if LUABIND_VERSION > 600
2023 luabind::object obj(luabind::from_stack(ls.getStatePointer(), -1));
2024 ls.pop();
2025 #else
2026 luabind::object obj(ls.getStatePointer());
2027 obj.set();
2028 #endif
2029 dest = luabind::object_cast<std::string>(obj);
2031 catch(const luabind::cast_failed &)
2033 return false;
2035 return true;
2038 // ***************************************************************************
2039 bool CLuaIHM::popSINT32(CLuaState &ls, sint32 & dest)
2041 //H_AUTO(Lua_CLuaIHM_popSINT32)
2044 #if LUABIND_VERSION > 600
2045 luabind::object obj(luabind::from_stack(ls.getStatePointer(), -1));
2046 ls.pop();
2047 #else
2048 luabind::object obj(ls.getStatePointer());
2049 obj.set();
2050 #endif
2051 dest = luabind::object_cast<sint32>(obj);
2053 catch(const luabind::cast_failed &)
2055 return false;
2057 return true;
2060 // ***************************************************************************
2061 void CLuaIHM::getPoly2DOnStack(CLuaState &ls, sint index, NLMISC::CPolygon2D &dest)
2063 //H_AUTO(Lua_CLuaIHM_getPoly2DOnStack)
2064 ls.pushValue(index);
2065 CLuaObject poly;
2066 poly.pop(ls);
2067 dest.Vertices.clear();
2068 ENUM_LUA_TABLE(poly, it)
2070 it.nextValue().push();
2071 NLMISC::CVector2f pos;
2072 if (!pop(ls, pos))
2074 fails(ls, "2D polygon expects CVector2f for poly coordinates");
2076 dest.Vertices.push_back(pos);