Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/ceguilua/ceguilua-0.6.2/ceguilua/CEGUILuaFunctor.cpp @ 2710

Last change on this file since 2710 was 2710, checked in by rgrieder, 15 years ago

Merged buildsystem3 containing buildsystem2 containing Adi's buildsystem branch back to the trunk.
Please update the media directory if you were not using buildsystem3 before.

  • Property svn:eol-style set to native
File size: 13.7 KB
Line 
1/***********************************************************************
2    filename: CEGUILuaFunctor.cpp
3    created:  Thu Jan 26 2006
4    author:   Tomas Lindquist Olsen <tomas@famolsen.dk>
5
6    purpose:  Implementation for LuaFunctor class
7*************************************************************************/
8/***************************************************************************
9 *   Copyright (C) 2004 - 2008 Paul D Turner & The CEGUI Development Team
10 *
11 *   Permission is hereby granted, free of charge, to any person obtaining
12 *   a copy of this software and associated documentation files (the
13 *   "Software"), to deal in the Software without restriction, including
14 *   without limitation the rights to use, copy, modify, merge, publish,
15 *   distribute, sublicense, and/or sell copies of the Software, and to
16 *   permit persons to whom the Software is furnished to do so, subject to
17 *   the following conditions:
18 *
19 *   The above copyright notice and this permission notice shall be
20 *   included in all copies or substantial portions of the Software.
21 *
22 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
25 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
26 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
27 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28 *   OTHER DEALINGS IN THE SOFTWARE.
29 ***************************************************************************/
30#include "CEGUILuaFunctor.h"
31#include "CEGUILogger.h"
32#include "CEGUIExceptions.h"
33#include "CEGUIPropertyHelper.h"
34#include "CEGUILua.h"
35
36// include Lua libs and tolua++
37extern "C" {
38#include "lua.h"
39#include "lualib.h"
40#include "lauxlib.h"
41}
42
43#include "tolua/tolua++.h"
44
45// Start of CEGUI namespace section
46namespace CEGUI
47{
48
49/*************************************************************************
50    Constructor
51*************************************************************************/
52LuaFunctor::LuaFunctor(lua_State* state, int func, int selfIndex) :
53    L(state),
54    index(func),
55    self(selfIndex),
56    needs_lookup(false),
57    d_ourErrFuncIndex(false)
58{
59    // TODO: This would perhaps be better done another way, to avoid the
60    // TODO: interdependence.
61    LuaScriptModule* sm =
62        static_cast<LuaScriptModule*>(System::getSingleton().getScriptingModule());
63
64    if (sm)
65    {
66        d_errFuncName  = sm->getActivePCallErrorHandlerString();
67        d_errFuncIndex = sm->getActivePCallErrorHandlerReference();
68    }
69}
70
71/*************************************************************************
72    Constructor
73*************************************************************************/
74LuaFunctor::LuaFunctor(lua_State* state, const String& func, int selfIndex) :
75    L(state),
76    index(LUA_NOREF),
77    self(selfIndex),
78    needs_lookup(true),
79    function_name(func),
80    d_ourErrFuncIndex(false)
81{
82    // TODO: This would perhaps be better done another way, to avoid the
83    // TODO: interdependence.
84    LuaScriptModule* sm =
85        static_cast<LuaScriptModule*>(System::getSingleton().getScriptingModule());
86
87    if (sm)
88    {
89        d_errFuncName  = sm->getActivePCallErrorHandlerString();
90        d_errFuncIndex = sm->getActivePCallErrorHandlerReference();
91    }
92}
93
94/*************************************************************************
95    Constructor
96*************************************************************************/
97LuaFunctor::LuaFunctor(const LuaFunctor& cp) :
98    L(cp.L),
99    index(cp.index),
100    self(cp.self),
101    needs_lookup(cp.needs_lookup),
102    function_name(cp.function_name),
103    d_errFuncName(cp.d_errFuncName),
104    d_errFuncIndex(cp.d_errFuncIndex),
105    d_ourErrFuncIndex(cp.d_ourErrFuncIndex)
106{
107}
108
109/*************************************************************************
110    Destructor
111*************************************************************************/
112LuaFunctor::~LuaFunctor()
113{
114    if (self != LUA_NOREF)
115        luaL_unref(L, LUA_REGISTRYINDEX, self);
116
117    if (index != LUA_NOREF)
118        luaL_unref(L, LUA_REGISTRYINDEX, index);
119
120    if (d_ourErrFuncIndex &&
121        (d_errFuncIndex != LUA_NOREF) &&
122        !d_errFuncName.empty())
123            luaL_unref(L, LUA_REGISTRYINDEX, d_errFuncIndex);
124}
125
126/*************************************************************************
127    Call operator
128*************************************************************************/
129bool LuaFunctor::operator()(const EventArgs& args) const
130{
131    // named error handler needs binding?
132    if ((d_errFuncIndex == LUA_NOREF) && !d_errFuncName.empty())
133    {
134        pushNamedFunction(L, d_errFuncName);
135        d_errFuncIndex = luaL_ref(L, LUA_REGISTRYINDEX);
136        d_ourErrFuncIndex = true;
137    }
138
139    // is this a late binding?
140    if (needs_lookup)
141    {
142        pushNamedFunction(L, function_name);
143        // reference function
144        index = luaL_ref(L, LUA_REGISTRYINDEX);
145        needs_lookup = false;
146        CEGUI_LOGINSANE("Late binding of callback '"+function_name+"' performed");
147        function_name.clear();
148    } // if (needs_lookup)
149
150    // put error handler on stack if we're using such a thing
151    int err_idx = 0;
152    if (d_errFuncIndex != LUA_NOREF)
153    {
154        lua_rawgeti(L, LUA_REGISTRYINDEX, d_errFuncIndex);
155        err_idx = lua_gettop(L);
156    }
157
158        ScriptWindowHelper* helper = 0;
159        //Set a global for this window
160        if(args.d_hasWindow)
161        {
162                WindowEventArgs& we = (WindowEventArgs&)args;
163                helper = new ScriptWindowHelper(we.window);
164                tolua_pushusertype(L,(void*)helper,"CEGUI::ScriptWindowHelper");
165                lua_setglobal(L,"this");
166        }
167
168    // retrieve function
169    lua_rawgeti(L, LUA_REGISTRYINDEX, index);
170
171    // possibly self as well?
172    int nargs = 1;
173    if (self != LUA_NOREF)
174    {
175        lua_rawgeti(L, LUA_REGISTRYINDEX, self);
176        ++nargs;
177    }
178
179    // push EventArgs  parameter
180    tolua_pushusertype(L, (void*)&args, "const CEGUI::EventArgs");
181
182    // call it
183    int error = lua_pcall(L, nargs, 1, err_idx);
184
185    // handle errors
186    if (error)
187    {
188        String errStr(lua_tostring(L, -1));
189        lua_pop(L, 1);
190                if(helper)
191                {
192                        delete helper;
193                        helper = 0;
194                }
195        throw ScriptException("Unable to call Lua event handler:\n\n"+errStr+"\n");
196    } // if (error)
197
198    // retrieve result
199    bool ret = lua_isboolean(L, -1) ? lua_toboolean(L, -1 ) : true;
200    lua_pop(L, 1);
201
202        if(helper)
203        {
204                delete helper;
205                helper = 0;
206        }
207
208    return ret;
209}
210
211/*************************************************************************
212    Pushes a named function on the stack
213*************************************************************************/
214void LuaFunctor::pushNamedFunction(lua_State* L, const String& handler_name)
215{
216    int top = lua_gettop(L);
217
218    // do we have any dots in the handler name? if so we grab the function as a table field
219    String::size_type i = handler_name.find_first_of((utf32)'.');
220    if (i!=String::npos)
221    {
222        // split the rest of the string up in parts seperated by '.'
223        // TODO: count the dots and size the vector accordingly from the beginning.
224        std::vector<String> parts;
225        String::size_type start = 0;
226        do
227        {
228            parts.push_back(handler_name.substr(start,i-start));
229            start = i+1;
230            i = handler_name.find_first_of((utf32)'.',start);
231        } while(i!=String::npos);
232
233        // add last part
234        parts.push_back(handler_name.substr(start));
235
236        // first part is the global
237        lua_getglobal(L, parts[0].c_str());
238        if (!lua_istable(L,-1))
239        {
240            lua_settop(L,top);
241            throw ScriptException("Unable to get the Lua event handler: '"+handler_name+"' as first part is not a table");
242        }
243
244        // if there is more than two parts, we have more tables to go through
245        std::vector<String>::size_type visz = parts.size();
246        if (visz-- > 2) // avoid subtracting one later on
247        {
248            // go through all the remaining parts to (hopefully) have a valid Lua function in the end
249            std::vector<String>::size_type vi = 1;
250            while (vi<visz)
251            {
252                // push key, and get the next table
253                lua_pushstring(L,parts[vi].c_str());
254                lua_gettable(L,-2);
255                if (!lua_istable(L,-1))
256                {
257                    lua_settop(L,top);
258                    throw ScriptException("Unable to get the Lua event handler: '"+handler_name+"' as part #"+PropertyHelper::uintToString(uint(vi+1))+" ("+parts[vi]+") is not a table");
259                }
260                // get rid of the last table and move on
261                lua_remove(L,-2);
262                vi++;
263            }
264        }
265
266        // now we are ready to get the function to call ... phew :)
267        lua_pushstring(L,parts[visz].c_str());
268        lua_gettable(L,-2);
269        lua_remove(L,-2); // get rid of the table
270    }
271    // just a regular global function
272    else
273    {
274        lua_getglobal(L, handler_name.c_str());
275    }
276
277    // is it a function
278    if (!lua_isfunction(L,-1))
279    {
280        lua_settop(L,top);
281        throw ScriptException("The Lua event handler: '"+handler_name+"' does not represent a Lua function");
282    }
283}
284
285//----------------------------------------------------------------------------//
286LuaFunctor::LuaFunctor(lua_State* state, const int func, const int selfIndex,
287    const String& error_handler) :
288    // State initialisation
289    L(state),
290    index(func),
291    self(selfIndex),
292    needs_lookup(false),
293    d_errFuncName(error_handler),
294    d_errFuncIndex(LUA_NOREF),
295    d_ourErrFuncIndex(false)
296{
297}
298
299//----------------------------------------------------------------------------//
300LuaFunctor::LuaFunctor(lua_State* state, const String& func, const int selfIndex,
301    const String& error_handler) :
302    // State initialisation
303    L(state),
304    index(LUA_NOREF),
305    self(selfIndex),
306    needs_lookup(true),
307    function_name(func),
308    d_errFuncName(error_handler),
309    d_errFuncIndex(LUA_NOREF),
310    d_ourErrFuncIndex(false)
311{
312}
313
314//----------------------------------------------------------------------------//
315LuaFunctor::LuaFunctor(lua_State* state, const int func, const int selfIndex,
316    const int error_handler) :
317    // State initialisation
318    L(state),
319    index(func),
320    self(selfIndex),
321    needs_lookup(false),
322    d_errFuncIndex(error_handler),
323    d_ourErrFuncIndex(false)
324{
325}
326
327//----------------------------------------------------------------------------//
328LuaFunctor::LuaFunctor(lua_State* state, const String& func, const int selfIndex,
329    const int error_handler) :
330    // State initialisation
331    L(state),
332    index(LUA_NOREF),
333    self(selfIndex),
334    needs_lookup(true),
335    function_name(func),
336    d_errFuncIndex(error_handler),
337    d_ourErrFuncIndex(false)
338{
339}
340
341//----------------------------------------------------------------------------//
342Event::Connection LuaFunctor::SubscribeEvent(EventSet* self,
343    const String& event_name, const int funcIndex, const int selfIndex,
344    const int error_handler, lua_State* L)
345{
346    // deal with error handler function
347    int err_idx = LUA_NOREF;
348    String err_str;
349    if (error_handler != LUA_NOREF)
350    {
351        int err_handler_type = lua_type(L,-1);
352        switch (err_handler_type)
353        {
354        case LUA_TFUNCTION:
355            // reference function
356            err_idx = luaL_ref(L, LUA_REGISTRYINDEX);
357            break;
358        case LUA_TSTRING:
359            err_str = lua_tostring(L, -1);
360            lua_pop(L, 1);
361            break;
362        default:
363            luaL_error(L, "bad error handler function passed to subscribe "
364                          "function. must be a real function, or a string for "
365                          "late binding");
366            break;
367        }
368
369    }
370
371    // should we pass a self to the callback?
372    int thisIndex = LUA_NOREF;
373    if (selfIndex != LUA_NOREF)
374    {
375        // reference self
376        thisIndex = luaL_ref(L, LUA_REGISTRYINDEX);
377    }
378
379    // do the real subscription
380    int type = lua_type(L,-1);
381    Event::Connection con;
382    if (type == LUA_TFUNCTION)
383    {
384        // reference function
385        int index = luaL_ref(L, LUA_REGISTRYINDEX);
386
387        LuaFunctor functor((err_idx != LUA_NOREF) ?
388                        LuaFunctor(L, index, thisIndex, err_idx) :
389                        (!err_str.empty()) ?
390                            LuaFunctor(L, index, thisIndex, err_str) :
391                            LuaFunctor(L, index, thisIndex));
392        con = self->subscribeEvent(String(event_name), Event::Subscriber(functor));
393        // make sure we don't release the reference(s) we just made when the
394        // 'functor' object is destroyed (goes out of scope)
395        functor.index = LUA_NOREF;
396        functor.self = LUA_NOREF;
397        functor.d_errFuncIndex = LUA_NOREF;
398    }
399    else if (type == LUA_TSTRING)
400    {
401        const char* str = lua_tostring(L, -1);
402        LuaFunctor functor((err_idx != LUA_NOREF) ?
403                        LuaFunctor(L, String(str), thisIndex, err_idx) :
404                        (!err_str.empty()) ?
405                            LuaFunctor(L, String(str), thisIndex, err_str) :
406                            LuaFunctor(L, String(str), thisIndex));
407
408        con = self->subscribeEvent(String(event_name), Event::Subscriber(functor));
409        // make sure we don't release the reference(s) we just made when the
410        // 'functor' object is destroyed (goes out of scope)
411        functor.self = LUA_NOREF;
412        functor.d_errFuncIndex = LUA_NOREF;
413    }
414    else
415    {
416        luaL_error(L, "bad function passed to subscribe function. must be a "
417                      "real function, or a string for late binding");
418    }
419
420    // return the event connection
421    return con;
422}
423
424//----------------------------------------------------------------------------//
425
426} // namespace CEGUI
Note: See TracBrowser for help on using the repository browser.