wxlcallb.cpp

Go to the documentation of this file.
00001 /////////////////////////////////////////////////////////////////////////////
00002 // Name:        wxlcallb.cpp
00003 // Purpose:     wxLuaEventCallback and wxLuaWinDestroyCallback
00004 // Author:      Francis Irving, John Labenski
00005 // Created:     11/05/2002
00006 // Copyright:   (c) 2002 Creature Labs. All rights reserved.
00007 // Licence:     wxWidgets licence
00008 /////////////////////////////////////////////////////////////////////////////
00009 
00010 // For compilers that support precompilation, includes "wx/wx.h".
00011 #include "wx/wxprec.h"
00012 
00013 #ifdef __BORLANDC__
00014     #pragma hdrstop
00015 #endif
00016 
00017 #ifndef WX_PRECOMP
00018     #include "wx/wx.h"
00019 #endif // WX_PRECOMP
00020 
00021 #include "wxlua/include/wxlcallb.h"
00022 
00023 //-----------------------------------------------------------------------------
00024 // wxLuaEventCallback
00025 //-----------------------------------------------------------------------------
00026 IMPLEMENT_ABSTRACT_CLASS(wxLuaEventCallback, wxObject)
00027 
00028 wxLuaEventCallback::wxLuaEventCallback()
00029                    : m_luafunc_ref(0), //m_wxlState(wxNullLuaState),
00030                      m_evtHandler(NULL), m_id(wxID_ANY), m_last_id(wxID_ANY),
00031                      m_wxlBindEvent(NULL)
00032 {
00033 }
00034 
00035 wxLuaEventCallback::~wxLuaEventCallback()
00036 {
00037     // Remove the reference to the Lua function that we call
00038     if (m_wxlState.Ok())
00039     {
00040         m_wxlState.wxluaR_Unref(m_luafunc_ref, &wxlua_lreg_refs_key);
00041         // delete the reference to this handler
00042         m_wxlState.RemoveTrackedEventCallback(this);
00043     }
00044 }
00045 
00046 wxString wxLuaEventCallback::Connect(const wxLuaState& wxlState, int lua_func_stack_idx,
00047                                      wxWindowID win_id, wxWindowID last_id,
00048                                      wxEventType eventType, wxEvtHandler *evtHandler)
00049 {
00050     // Assert too since these errors are serious and not just bad Lua code.
00051     wxCHECK_MSG(evtHandler != NULL, wxT("Invalid wxEvtHandler in wxLuaEventCallback::Connect()"), wxT("Invalid wxEvtHandler in wxLuaEventCallback::Connect()"));
00052     wxCHECK_MSG((m_evtHandler == NULL) && (m_luafunc_ref == 0), wxT("Attempting to reconnect a wxLuaEventCallback"), wxT("Attempting to reconnect a wxLuaEventCallback"));
00053     wxCHECK_MSG(wxlState.Ok(), wxT("Invalid wxLuaState"), wxT("Invalid wxLuaState"));
00054 
00055     m_wxlState   = wxlState;
00056     m_evtHandler = evtHandler;
00057     m_id         = win_id;
00058     m_last_id    = last_id;
00059 
00060     m_wxlBindEvent = wxlState.GetBindEvent(eventType);
00061 
00062     // Do not install this invalid or unknown event type since we won't know
00063     // what wxEvent type class to use and someone probably made a mistake.
00064     if (m_wxlBindEvent == NULL)
00065     {
00066         return wxString::Format(wxT("wxLua: Invalid or unknown wxEventType for wxEvtHandler::Connect() : %d, winIds %d, %d."),
00067                                   (int)eventType, win_id, last_id);
00068     }
00069 
00070     m_wxlState.AddTrackedEventCallback(this);
00071 
00072     // create a reference to the Lua event handler function
00073     if (lua_func_stack_idx != WXLUAEVENTCALLBACK_NOROUTINE)
00074         m_luafunc_ref = m_wxlState.wxluaR_Ref(lua_func_stack_idx, &wxlua_lreg_refs_key);
00075 
00076     // Note: We use the callback userdata and not the event sink since the event sink
00077     // requires a wxEvtHandler object which is a fairly large class.
00078     // The userdata (i.e. this) is also deleted for us which makes our life easier.
00079     m_evtHandler->Connect(win_id, last_id, eventType,
00080                           (wxObjectEventFunction)&wxLuaEventCallback::OnAllEvents,
00081                           this);
00082     return wxEmptyString;
00083 }
00084 
00085 void wxLuaEventCallback::ClearwxLuaState()
00086 {
00087     m_wxlState.UnRef();
00088 }
00089 
00090 wxString wxLuaEventCallback::GetInfo() const
00091 {
00092     return wxString::Format(wxT("%s(%d) -> wxLuaEventCallback(%p, ids %d, %d)|wxEvtHandler(%p) -> %s : %s"),
00093                 lua2wx(m_wxlBindEvent ? m_wxlBindEvent->name : "?").c_str(), (int)GetEventType(),
00094                 this, m_id, m_last_id,
00095                 m_evtHandler, m_evtHandler->GetClassInfo()->GetClassName(),
00096                 m_wxlState.GetwxLuaTypeName(*m_wxlBindEvent->wxluatype).c_str());
00097 }
00098 
00099 void wxLuaEventCallback::OnAllEvents(wxEvent& event)
00100 {
00101     wxEventType evtType = event.GetEventType();
00102 
00103     // Get the wxLuaEventCallback instance to use which is NOT "this" since
00104     // "this" is a central event handler function. i.e. this != theCallback
00105     wxLuaEventCallback *theCallback = (wxLuaEventCallback *)event.m_callbackUserData;
00106     wxCHECK_RET(theCallback != NULL, wxT("Invalid wxLuaEventCallback in wxEvent user data"));
00107 
00108     // Not an error if !Ok(), the wxLuaState is cleared during shutdown or after a destroy event.
00109     wxLuaState wxlState(theCallback->GetwxLuaState());
00110     if (wxlState.Ok())
00111     {
00112         wxlState.SetInEventType(evtType);
00113         theCallback->OnEvent(&event);
00114         wxlState.SetInEventType(wxEVT_NULL);
00115     }
00116 
00117     // we want the wxLuaWinDestroyCallback to get this too
00118     if (evtType == wxEVT_DESTROY)
00119         event.Skip(true);
00120 }
00121 
00122 void wxLuaEventCallback::OnEvent(wxEvent *event)
00123 {
00124     // Cannot call it if Lua is gone or the interpreter has been destroyed
00125     // This can happen when the program exits since windows may be destroyed
00126     // after Lua has been deleted.
00127     if (!m_wxlState.Ok())
00128         return;
00129 
00130     // ref the state in case this generates a wxEVT_DESTROY which clears us
00131     wxLuaState wxlState(m_wxlState);
00132 
00133     int event_wxl_type = WXLUA_TUNKNOWN;
00134 
00135     // If !m_wxlBindEvent, we would have errored in Connect(), but don't crash...
00136     if (m_wxlBindEvent != NULL)
00137     {
00138         event_wxl_type = *m_wxlBindEvent->wxluatype;
00139 
00140         // These wxEventTypes can be wxScrollEvents or wxSpinEvents
00141         // wxEVT_SCROLL_LINEUP, wxEVT_SCROLL_LINEDOWN, wxEVT_SCROLL_THUMBTRACK
00142 
00143         if ((strcmp(m_wxlBindEvent->name, "wxScrollEvent") == 0) &&
00144             (event->GetClassInfo()->GetClassName() == wxString(wxT("wxSpinEvent"))))
00145         {
00146             const wxLuaBindClass *wxlClass = wxlState.GetBindClass("wxSpinEvent");
00147             if (wxlClass != NULL)
00148                 event_wxl_type = *wxlClass->wxluatype;
00149             else
00150                 event_wxl_type = *p_wxluatype_wxEvent; // get the generic wxluatype_wxEvent
00151         }
00152     }
00153     else
00154         event_wxl_type = *p_wxluatype_wxEvent; // get the generic wxluatype_wxEvent
00155 
00156     // Should know our event type, but error out in case we don't
00157     wxCHECK_RET(event_wxl_type != WXLUA_TUNKNOWN, wxT("Unknown wxEvent wxLua tag for : ") + wxString(event->GetClassInfo()->GetClassName()));
00158 
00159     wxlState.lua_CheckStack(LUA_MINSTACK);
00160     int oldTop = wxlState.lua_GetTop();
00161     if (wxlState.wxluaR_GetRef(m_luafunc_ref, &wxlua_lreg_refs_key))
00162     {
00163         wxlState.lua_PushValue(LUA_GLOBALSINDEX);
00164         if (wxlState.lua_SetFenv(-2) != 0)
00165         {
00166             // Don't track the wxEvent since we don't own it and tracking it
00167             // causes clashes in the object registry table since many can be
00168             // created and deleted and the mem address is resused by C++.
00169             wxlState.wxluaT_PushUserDataType(event, event_wxl_type, false);
00170             wxlState.LuaPCall(1, 0); // one input no returns
00171         }
00172         else
00173             wxlState.wxlua_Error("wxLua: wxEvtHandler::Connect() in wxLuaEventCallback::OnEvent(), function is not a Lua function.");
00174     }
00175     else
00176         wxlState.wxlua_Error("wxLua: wxEvtHandler::Connect() in wxLuaEventCallback::OnEvent(), function to call is not refed.");
00177 
00178     wxlState.lua_SetTop(oldTop); // pop function and error message from the stack (if they're there)
00179 }
00180 
00181 // ----------------------------------------------------------------------------
00182 // wxLuaWinDestroyCallback
00183 // ----------------------------------------------------------------------------
00184 IMPLEMENT_ABSTRACT_CLASS(wxLuaWinDestroyCallback, wxObject)
00185 
00186 wxLuaWinDestroyCallback::wxLuaWinDestroyCallback(const wxLuaState& wxlState,
00187                                                  wxWindow* win)
00188                         :m_wxlState(wxlState), m_window(win)
00189 {
00190     wxCHECK_RET(m_wxlState.Ok(), wxT("Invalid wxLuaState"));
00191     wxCHECK_RET(m_window != NULL, wxT("Invalid wxWindow"));
00192 
00193     m_wxlState.AddTrackedWinDestroyCallback(this);
00194 
00195     // connect the event handler and set this as the callback user data
00196     m_window->Connect(m_window->GetId(), wxEVT_DESTROY,
00197                       (wxObjectEventFunction)&wxLuaWinDestroyCallback::OnAllDestroyEvents,
00198                       this);
00199 }
00200 
00201 wxLuaWinDestroyCallback::~wxLuaWinDestroyCallback()
00202 {
00203     if (m_wxlState.Ok())
00204     {
00205         m_wxlState.RemoveTrackedWinDestroyCallback(this);
00206         m_wxlState.RemoveTrackedWindow(m_window);
00207     }
00208 }
00209 
00210 void wxLuaWinDestroyCallback::ClearwxLuaState()
00211 {
00212     m_wxlState.UnRef();
00213 }
00214 
00215 wxString wxLuaWinDestroyCallback::GetInfo() const
00216 {
00217     wxString winName(wxT("wxWindow?"));
00218     if (m_window && m_window->GetClassInfo())
00219         winName = m_window->GetClassInfo()->GetClassName();
00220 
00221     return wxString::Format(wxT("%s(%p, id=%d)|wxLuaDestroyCallback(%p)"),
00222                 winName.c_str(), m_window, m_window ? m_window->GetId() : -1,
00223                 this);
00224 }
00225 
00226 void wxLuaWinDestroyCallback::OnAllDestroyEvents(wxWindowDestroyEvent& event)
00227 {
00228     // Central handler for events, forward to the specific instance
00229     wxLuaWinDestroyCallback *theCallback = (wxLuaWinDestroyCallback *)event.m_callbackUserData;
00230     if (theCallback && (((wxWindow*)event.GetEventObject()) == theCallback->m_window))
00231     {
00232         theCallback->OnDestroy(event);
00233     }
00234     else
00235         event.Skip();
00236 }
00237 
00238 void wxLuaWinDestroyCallback::OnDestroy(wxWindowDestroyEvent& event)
00239 {
00240     event.Skip();
00241 
00242     // FIXME - Is it an error to receive an event after you've deleted Lua?
00243     //  probably not if Lua is getting shutdown
00244 
00245     // Note: do not remove from wxLuaState's destroyHandlerList here, wait 'till destructor
00246     if (m_wxlState.Ok())
00247     {
00248         lua_State* L = m_wxlState.GetLuaState();
00249 
00250         // clear the metatable for all userdata we're tracking.
00251         wxluaO_untrackweakobject(L, NULL, m_window);
00252         wxlua_removederivedmethods(L, m_window);
00253 
00254         // Clear our own pointer to this window
00255         m_wxlState.RemoveTrackedWindow(m_window);
00256 
00257         wxEvtHandler* evtHandler = m_window->GetEventHandler();
00258 
00259         // Finally, clear out the wxLuaEventCallbacks for the very odd cases where
00260         // (activation) events can be sent during destruction. These can happen
00261         // if you pop up a modal dialog (asking if they want to save perhaps)
00262         // and when the dialog is closed the frame below sends an activation event,
00263         // but we're right in the middle of being destroyed and we crash.
00264 
00265         lua_pushlightuserdata(L, &wxlua_lreg_evtcallbacks_key); // push key
00266         lua_rawget(L, LUA_REGISTRYINDEX);                       // pop key, push value (table)
00267 
00268         lua_pushnil(L);
00269         while (lua_next(L, -2) != 0)
00270         {
00271             // value = -1, key = -2, table = -3
00272             wxLuaEventCallback* wxlCallback = (wxLuaEventCallback*)lua_touserdata(L, -2);
00273             wxCHECK_RET(wxlCallback, wxT("Invalid wxLuaEventCallback"));
00274 
00275             if ((wxlCallback->GetEvtHandler() == evtHandler) ||
00276                 (wxlCallback->GetEvtHandler() == (wxEvtHandler*)m_window))
00277             {
00278                 // remove the ref to the routine since we're clearing the wxLuaState
00279                 // See ~wxLuaEventCallback
00280                 m_wxlState.wxluaR_Unref(wxlCallback->GetLuaFuncRef(), &wxlua_lreg_refs_key);
00281                 wxlCallback->ClearwxLuaState();
00282 
00283                 lua_pop(L, 1);        // pop value
00284 
00285                 // The code below is the equivalent of this, but works while iterating
00286                 //   "m_wxlState.RemoveTrackedEventCallback(wxlCallback);"
00287 
00288                 lua_pushvalue(L, -1); // copy key for next iteration
00289                 lua_pushnil(L);
00290                 lua_rawset(L, -4);    // set t[key] = nil to remove it
00291             }
00292             else
00293                 lua_pop(L, 1);        // pop value, lua_next will pop key at end
00294         }
00295 
00296         lua_pop(L, 1); // pop table
00297     }
00298 }
Generated on Tue Jul 13 10:30:39 2010 for wxLua by  doxygen 1.6.3