diff --git a/NatlinkSource/DragonCode.cpp b/NatlinkSource/DragonCode.cpp index 4db819c..410d0f4 100644 --- a/NatlinkSource/DragonCode.cpp +++ b/NatlinkSource/DragonCode.cpp @@ -1203,37 +1203,45 @@ void CDragonCode::onTimer() void CDragonCode::onMenuCommand( WPARAM wParam ) { - if( LOWORD(wParam) == IDD_RELOAD ) - { - - // currently we do not support this operation if we are using thread - // support because I have not worked through the issues about what - // to do about the thread state - if( m_pThreadState ) - { - return; - } + WORD nMenuItem = LOWORD( wParam ); + switch ( nMenuItem ) + { + case IDD_RELOAD: + // note by DF: + // PyWin32, a library we all depend on, does not support + // interpreter reinitialization. Please see the following + // page for more information: + // https://mail.python.org/pipermail/python-win32/2013-January/012671.html + // + // With this limitation in mind, I have adjusted Natlink's + // reload mechanism for narrower code-reload. // reload the Python subsystem displayText( "Reloading Python subsystem...\r\n", FALSE, FALSE ); - // Although we do not really care about the Python reference count - // we do want to reset the callbacks so we do not make a call into - // an obselete intrepreter. + // reset the callbacks set from Python setChangeCallback( Py_None ); setBeginCallback( Py_None ); setTimerCallback( Py_None ); setTrayIcon( "", "", Py_None ); - // We call this because the reinitialization will not free up the - // python objects. Note that we do free the COM objects but we do - // not free the Python objects. This means that we will have a - // minor memory leak but no object leaks (which would prevent - // shutdown of NatSpeak). + // release user Python-Natlink objects releaseObjects(); + break; - m_pAppClass->reloadPython(); + case IDD_EXIT: + // nothing special to do here + break; + + default: + return; } + + // invoke the message window callback for menu commands handled + // above + makeCallback( + m_pMessageWindowCallback, + Py_BuildValue( "(i)", nMenuItem ) ); } //--------------------------------------------------------------------------- @@ -1536,16 +1544,10 @@ BOOL CDragonCode::initSecondWindow() return FALSE; } - // we store out class pointer in the window's extra data field so we + // we store our class pointer in the window's extra data field so we // get called back when a menu message occurs SetWindowLong( m_hMsgWnd, 0, (LONG)this ); - // tell the thread about out message window - if( m_pSecdThrd ) - { - m_pSecdThrd->setMsgWnd( m_hMsgWnd ); - } - return TRUE; } @@ -1687,14 +1689,6 @@ BOOL CDragonCode::natConnect( IServiceProvider * pIDgnSite, BOOL bUseThreads ) } #endif - // here we start the second thread for displaying messages; we only need - // this when we are called as a compatibility module - - if( pIDgnSite != NULL ) - { - m_pSecdThrd = new MessageWindow( 0 ); - } - // Connect to NatSpeak if( !initGetSiteObject( pIDgnSite ) ) @@ -1882,12 +1876,8 @@ BOOL CDragonCode::natDisconnect() m_pIDgnSRTraining = NULL; m_pISRCentral = NULL; - // shutdown the second thread - if( m_pSecdThrd ) - { - delete m_pSecdThrd; - m_pSecdThrd = NULL; - } + // kill the message window + setMessageWindow( Py_None ); // destroy our hidden window if( m_hMsgWnd ) @@ -3693,6 +3683,51 @@ BOOL CDragonCode::setTrayIcon( return TRUE; } +//--------------------------------------------------------------------------- + +BOOL CDragonCode::setMessageWindow( + PyObject * pCallback, DWORD dwFlags ) +{ + if( pCallback == Py_None ) + { + Py_XDECREF( m_pMessageWindowCallback ); + m_pMessageWindowCallback = NULL; + + // shutdown the second thread + if( m_pSecdThrd ) + { + delete m_pSecdThrd; + m_pSecdThrd = NULL; + } + } + else + { + NOTBEFORE_INIT( "setMessageWindow" ); + + Py_XINCREF( pCallback ); + Py_XDECREF( m_pMessageWindowCallback ); + m_pMessageWindowCallback = pCallback; + + // start the second thread for displaying messages, if + // necessary. Otherwise, pass on the second parameter + if( !m_pSecdThrd ) + { + m_pSecdThrd = new MessageWindow( dwFlags ); + + // tell the thread about our message window + m_pSecdThrd->setMsgWnd( m_hMsgWnd ); + } + else + { + m_pSecdThrd->updateWindow( dwFlags ); + } + } + + return TRUE; +} + +//--------------------------------------------------------------------------- + void CDragonCode::logCookie( const char * pText, QWORD qCookie ) { DWORD *pCookie = (DWORD*)&qCookie; diff --git a/NatlinkSource/DragonCode.h b/NatlinkSource/DragonCode.h index 38102b7..230073b 100644 --- a/NatlinkSource/DragonCode.h +++ b/NatlinkSource/DragonCode.h @@ -48,6 +48,7 @@ class CDragonCode m_pszLogFile = NULL; m_bHasTrayIcon = FALSE; m_pTrayIconCallback = NULL; + m_pMessageWindowCallback = NULL; m_pMessageStack = NULL; m_pIDgnSSvcOutputEventA=0; m_pIDgnSSvcOutputEvent=0; @@ -89,6 +90,7 @@ class CDragonCode BOOL deleteWord( char * wordName ); BOOL setWordInfo( char * wordName, DWORD wordInfo ); BOOL setTrayIcon( char * iconName, char * toolTip, PyObject * pCallback ); + BOOL setMessageWindow( PyObject * pCallback, DWORD dwflags = 0 ); PyObject * getCurrentModule(); PyObject * getCurrentUser(); @@ -267,6 +269,9 @@ class CDragonCode BOOL m_bHasTrayIcon; PyObject *m_pTrayIconCallback; + // set when the message window is started + PyObject * m_pMessageWindowCallback; + // This is what we call when we are ready to recume recognition void doPausedProcessing( QWORD dwCookie ); diff --git a/NatlinkSource/natlink.txt b/NatlinkSource/natlink.txt index 603f019..e96a0bb 100644 --- a/NatlinkSource/natlink.txt +++ b/NatlinkSource/natlink.txt @@ -572,6 +572,26 @@ waitForSpeech( timeout ) timeout except that the message box is not displayed (the function only returns when the timeout elapses). +setMessageWindow( callback, flags ) + This function enables, disables and updates Natlink's message window. + + The function has one required parameter whose value should be a Python + function or None. If a function is passed for this parameter, it + should take one parameter which is the type of message window event: + idd_reload or idd_exit (each defined in natlinkutils) + + Passing a callback function enables the message window, passing None + disables it. + + The second parameter is optional. It is a combination of one or more + of the following flags: + 0x01 # enable the window's File>Exit menu item + + Note: The idd_reload event occurs when the File>Reload menu item is + activated. Before this function's callback is invoked, Natlink will + unload all loaded grammars, destroy all existing ResObjs and DictObjs, + and clear all callbacks except the message window callback -- this one. + Inside natlink, each grammar is maintained as a separate object. To expose this structure, each grammar object will be exposed to Python as a class. The GrammarBase class defined in natlinkutils encapusulates and extends the diff --git a/NatlinkSource/pythwrap.cpp b/NatlinkSource/pythwrap.cpp index 73eeedc..501c2a6 100644 --- a/NatlinkSource/pythwrap.cpp +++ b/NatlinkSource/pythwrap.cpp @@ -1194,6 +1194,37 @@ natlink_setTrayIcon( PyObject *self, PyObject *args ) return Py_None; } +//--------------------------------------------------------------------------- +// natlink.setMessageWindow( callback, flags ) +// +// See natlink.txt for documentation. + +extern "C" static PyObject * +natlink_setMessageWindow( PyObject *self, PyObject *args ) +{ + PyObject * pFunc; + DWORD dwFlags = 0; + + if( !PyArg_ParseTuple( args, "O|i:setMessageWindow", &pFunc, &dwFlags) ) + { + return NULL; + } + + if( pFunc != Py_None && !PyCallable_Check( pFunc ) ) + { + PyErr_SetString( PyExc_TypeError, "first parameter must be callable" ); + return NULL; + } + + if( !cDragon.setMessageWindow( pFunc, dwFlags ) ) + { + return NULL; + } + + Py_INCREF( Py_None ); + return Py_None; +} + ///////////////////////////////////////////////////////////////////////////// //--------------------------------------------------------------------------- @@ -2314,6 +2345,7 @@ static struct PyMethodDef natlink_methods[] = { { "setWordInfo", natlink_setWordInfo, METH_VARARGS }, { "getWordProns", natlink_getWordProns, METH_VARARGS }, { "setTrayIcon", natlink_setTrayIcon, METH_VARARGS }, + { "setMessageWindow", natlink_setMessageWindow, METH_VARARGS }, { NULL } };