Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Reverted trunk again. We might want to find a way to delete these revisions again (x3n's changes are still available as diff in the commit mails).

  • 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.