Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/pickups/src/ceguilua-0.6.1/ceguilua/CEGUILuaFunctor.cpp @ 2255

Last change on this file since 2255 was 1810, checked in by rgrieder, 16 years ago

merged ceguilua branch back to trunk

  • Property svn:eol-style set to native
File size: 9.1 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 - 2006 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
35// include Lua libs and tolua++
36#include "lua/lua.hpp"
37#include "tolua/tolua++.h"
38
39// Start of CEGUI namespace section
40namespace CEGUI
41{
42
43/*************************************************************************
44    Constructor
45*************************************************************************/
46LuaFunctor::LuaFunctor(lua_State* state, int func, int selfIndex) :
47    L(state),
48    index(func),
49    self(selfIndex),
50    needs_lookup(false)
51{
52}
53
54/*************************************************************************
55    Constructor
56*************************************************************************/
57LuaFunctor::LuaFunctor(lua_State* state, const String& func, int selfIndex) :
58    L(state),
59    index(LUA_NOREF),
60    self(selfIndex),
61    needs_lookup(true),
62    function_name(func)
63{
64}
65
66/*************************************************************************
67    Constructor
68*************************************************************************/
69LuaFunctor::LuaFunctor(const LuaFunctor& cp) :
70    L(cp.L),
71    index(cp.index),
72    self(cp.self),
73    needs_lookup(cp.needs_lookup),
74    function_name(cp.function_name)
75{
76}
77
78/*************************************************************************
79    Destructor
80*************************************************************************/
81LuaFunctor::~LuaFunctor()
82{
83    if (self!=LUA_NOREF)
84    {
85        luaL_unref(L, LUA_REGISTRYINDEX, self);
86    }
87    if (index!=LUA_NOREF)
88    {
89        luaL_unref(L, LUA_REGISTRYINDEX, index);
90    }
91}
92
93/*************************************************************************
94    Call operator
95*************************************************************************/
96bool LuaFunctor::operator()(const EventArgs& args) const
97{
98    // is this a late binding?
99    if (needs_lookup)
100    {
101        pushNamedFunction(L, function_name);
102        // reference function
103        index = luaL_ref(L, LUA_REGISTRYINDEX);
104        needs_lookup = false;
105        CEGUI_LOGINSANE("Late binding of callback '"+function_name+"' performed");
106        function_name.clear();
107    } // if (needs_lookup)
108
109        ScriptWindowHelper* helper = 0;
110        //Set a global for this window
111        if(args.d_hasWindow)
112        {
113                WindowEventArgs& we = (WindowEventArgs&)args;
114                helper = new ScriptWindowHelper(we.window);
115                tolua_pushusertype(L,(void*)helper,"CEGUI::ScriptWindowHelper");
116                lua_setglobal(L,"this");
117        }
118
119    // retrieve function
120    lua_rawgeti(L, LUA_REGISTRYINDEX, index);
121
122    // possibly self as well?
123    int nargs = 1;
124    if (self != LUA_NOREF)
125    {
126        lua_rawgeti(L, LUA_REGISTRYINDEX, self);
127        ++nargs;
128    }
129
130    // push EventArgs  parameter
131    tolua_pushusertype(L, (void*)&args, "const CEGUI::EventArgs");
132
133    // call it
134    int error = lua_pcall(L, nargs, 0, 0);
135
136    // handle errors
137    if (error)
138    {
139        String errStr(lua_tostring(L, -1));
140        lua_pop(L, 1);
141                if(helper)
142                {
143                        delete helper;
144                        helper = 0;
145                }
146        throw ScriptException("Unable to call Lua event handler:\n\n"+errStr+"\n");
147    } // if (error)
148
149        if(helper)
150        {
151                delete helper;
152                helper = 0;
153        }
154
155    return true;
156}
157
158/*************************************************************************
159    do event subscription by reference instead of by name
160*************************************************************************/
161Event::Connection LuaFunctor::SubscribeEvent(EventSet* self, const String& event_name, int funcIndex, int selfIndex, lua_State* L)
162{
163    // should we pass a self to the callback?
164    int thisIndex = LUA_NOREF;
165    if (selfIndex != LUA_NOREF)
166    {
167        // reference self
168        thisIndex = luaL_ref(L, LUA_REGISTRYINDEX);
169    }
170
171    // do the real subscription
172    int type = lua_type(L,-1);
173    Event::Connection con;
174    if (type == LUA_TFUNCTION)
175    {
176        // reference function
177        int index = luaL_ref(L, LUA_REGISTRYINDEX);
178        LuaFunctor functor(L,index,thisIndex);
179        con = self->subscribeEvent(String(event_name), Event::Subscriber(functor));
180        // make sure we don't release the reference(s) we just made when this call returns
181        functor.index = LUA_NOREF;
182        functor.self = LUA_NOREF;
183    }
184    else if (type == LUA_TSTRING)
185    {
186        const char* str = lua_tostring(L, -1);
187        LuaFunctor functor(L,String(str),thisIndex);
188        con = self->subscribeEvent(String(event_name), Event::Subscriber(functor));
189        // make sure we don't release the reference we just (maybe) made when this call returns
190        functor.self = LUA_NOREF;
191    }
192    else
193    {
194        luaL_error(L,"bad function passed to subscribe function. must be a real function, or a string for late binding");
195    }
196
197    // return the event connection
198    return con;
199}
200
201/*************************************************************************
202    Pushes a named function on the stack
203*************************************************************************/
204void LuaFunctor::pushNamedFunction(lua_State* L, const String& handler_name)
205{
206    int top = lua_gettop(L);
207
208    // do we have any dots in the handler name? if so we grab the function as a table field
209    String::size_type i = handler_name.find_first_of((utf32)'.');
210    if (i!=String::npos)
211    {
212        // split the rest of the string up in parts seperated by '.'
213        // TODO: count the dots and size the vector accordingly from the beginning.
214        std::vector<String> parts;
215        String::size_type start = 0;
216        do
217        {
218            parts.push_back(handler_name.substr(start,i-start));
219            start = i+1;
220            i = handler_name.find_first_of((utf32)'.',start);
221        } while(i!=String::npos);
222
223        // add last part
224        parts.push_back(handler_name.substr(start));
225
226        // first part is the global
227        lua_getglobal(L, parts[0].c_str());
228        if (!lua_istable(L,-1))
229        {
230            lua_settop(L,top);
231            throw ScriptException("Unable to get the Lua event handler: '"+handler_name+"' as first part is not a table");
232        }
233
234        // if there is more than two parts, we have more tables to go through
235        std::vector<String>::size_type visz = parts.size();
236        if (visz-- > 2) // avoid subtracting one later on
237        {
238            // go through all the remaining parts to (hopefully) have a valid Lua function in the end
239            std::vector<String>::size_type vi = 1;
240            while (vi<visz)
241            {
242                // push key, and get the next table
243                lua_pushstring(L,parts[vi].c_str());
244                lua_gettable(L,-2);
245                if (!lua_istable(L,-1))
246                {
247                    lua_settop(L,top);
248                    throw ScriptException("Unable to get the Lua event handler: '"+handler_name+"' as part #"+PropertyHelper::uintToString(uint(vi+1))+" ("+parts[vi]+") is not a table");
249                }
250                // get rid of the last table and move on
251                lua_remove(L,-2);
252                vi++;
253            }
254        }
255
256        // now we are ready to get the function to call ... phew :)
257        lua_pushstring(L,parts[visz].c_str());
258        lua_gettable(L,-2);
259        lua_remove(L,-2); // get rid of the table
260    }
261    // just a regular global function
262    else
263    {
264        lua_getglobal(L, handler_name.c_str());
265    }
266
267    // is it a function
268    if (!lua_isfunction(L,-1))
269    {
270        lua_settop(L,top);
271        throw ScriptException("The Lua event handler: '"+handler_name+"' does not represent a Lua function");
272    }
273}
274
275} // namespace CEGUI
Note: See TracBrowser for help on using the repository browser.