]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/evtloop.cpp
placeholder
[wxWidgets.git] / src / msw / evtloop.cpp
index a36caa946dfa4a1bc8a54bddec951be45fb99661..1909600fa739eaeef56c7a2b9e66c849de3abf3c 100644 (file)
 #endif //WX_PRECOMP
 
 #include "wx/evtloop.h"
+
 #include "wx/tooltip.h"
+#include "wx/except.h"
+#include "wx/ptr_scpd.h"
 
 #include "wx/msw/private.h"
 
+// For MB_TASKMODAL
+#ifdef __WXWINCE__
+#include "wx/msw/wince/missing.h"
+#endif
+
 #if wxUSE_THREADS
+    #include "wx/thread.h"
+
     // define the array of MSG strutures
     WX_DECLARE_OBJARRAY(MSG, wxMsgArray);
-    // VS: this is a bit dirty - it duplicates same declaration in app.cpp
-    //     (and there's no WX_DEFINE_OBJARRAY for that reason - it is already
-    //     defined in app.cpp).
-#endif
+
+    #include "wx/arrimpl.cpp"
+
+    WX_DEFINE_OBJARRAY(wxMsgArray);
+#endif // wxUSE_THREADS
 
 // ----------------------------------------------------------------------------
 // wxEventLoopImpl
@@ -54,7 +65,7 @@ class WXDLLEXPORT wxEventLoopImpl
 {
 public:
     // ctor
-    wxEventLoopImpl() { SetExitCode(0); }
+    wxEventLoopImpl() { m_exitcode = 0; m_shouldExit = false; }
 
     // process a message
     void ProcessMessage(MSG *msg);
@@ -63,8 +74,16 @@ public:
     bool SendIdleMessage();
 
     // set/get the exit code
-    void SetExitCode(int exitcode) { m_exitcode = exitcode; }
+    void Exit(int exitcode) { m_exitcode = exitcode; m_shouldExit = true; }
     int GetExitCode() const { return m_exitcode; }
+    bool ShouldExit() const { return m_shouldExit; }
+
+    enum wxCatchAllResponse {
+        catch_continue,
+        catch_exit,
+        catch_rethrow
+    };
+    wxCatchAllResponse OnCatchAll();
 
 private:
     // preprocess a message, return TRUE if processed (i.e. no further
@@ -73,6 +92,39 @@ private:
 
     // the exit code of the event loop
     int m_exitcode;
+
+    // true if we were asked to terminate
+    bool m_shouldExit;
+};
+
+// ----------------------------------------------------------------------------
+// helper class
+// ----------------------------------------------------------------------------
+
+wxDEFINE_TIED_SCOPED_PTR_TYPE(wxEventLoopImpl);
+
+// this object sets the wxEventLoop given to the ctor as the currently active
+// one and unsets it in its dtor
+class wxEventLoopActivator
+{
+public:
+    wxEventLoopActivator(wxEventLoop **pActive,
+                         wxEventLoop *evtLoop)
+    {
+        m_pActive = pActive;
+        m_evtLoopOld = *pActive;
+        *pActive = evtLoop;
+    }
+
+    ~wxEventLoopActivator()
+    {
+        // restore the previously active event loop
+        *m_pActive = m_evtLoopOld;
+    }
+
+private:
+    wxEventLoop *m_evtLoopOld;
+    wxEventLoop **m_pActive;
 };
 
 // ============================================================================
@@ -173,6 +225,25 @@ bool wxEventLoopImpl::SendIdleMessage()
     return wxTheApp->ProcessIdle();
 }
 
+// ----------------------------------------------------------------------------
+// wxEventLoopImpl exception handling
+// ----------------------------------------------------------------------------
+
+wxEventLoopImpl::wxCatchAllResponse wxEventLoopImpl::OnCatchAll()
+{
+    switch (::MessageBox(NULL, 
+            _T("An unhandled exception occurred. 'Abort' will terminate the program,\r\n\
+'Retry' will close the current dialog, 'Ignore' will try to continue."),
+            _T("Unhandled exception"), 
+            MB_ABORTRETRYIGNORE|MB_ICONERROR|MB_TASKMODAL))
+    {
+        case IDABORT: return catch_rethrow;
+        case IDRETRY: return catch_exit;
+        case IDIGNORE: return catch_continue;
+    }
+    return catch_rethrow;
+}
+
 // ============================================================================
 // wxEventLoop implementation
 // ============================================================================
@@ -198,47 +269,97 @@ int wxEventLoop::Run()
     // event loops are not recursive, you need to create another loop!
     wxCHECK_MSG( !IsRunning(), -1, _T("can't reenter a message loop") );
 
-    m_impl = new wxEventLoopImpl;
-    
-    wxEventLoop *oldLoop = ms_activeLoop;
-    ms_activeLoop = this;
-
-    for ( ;; )
+    // SendIdleMessage() and Dispatch() below may throw so the code here should
+    // be exception-safe, hence we must use local objects for all actions we
+    // should undo
+    wxEventLoopActivator activate(&ms_activeLoop, this);
+    wxEventLoopImplTiedPtr impl(&m_impl, new wxEventLoopImpl);
+
+    // we must ensure that OnExit() is called even if an exception is thrown
+    // from inside Dispatch() but we must call it from Exit() in normal
+    // situations because it is supposed to be called synchronously,
+    // wxModalEventLoop depends on this (so we can't just use ON_BLOCK_EXIT or
+    // something similar here)
+#if wxUSE_EXCEPTIONS
+    bool retryAfterException = false;
+    do {
+        retryAfterException=false;
+#endif
+    wxTRY
     {
-#if wxUSE_THREADS
-        wxMutexGuiLeaveOrEnter();
-#endif // wxUSE_THREADS
+        for ( ;; )
+        {
+    #if wxUSE_THREADS
+            wxMutexGuiLeaveOrEnter();
+    #endif // wxUSE_THREADS
+
+            // generate and process idle events for as long as we don't have
+            // anything else to do
+            while ( !Pending() && m_impl->SendIdleMessage() )
+                ;
+
+            // if the "should exit" flag is set, the loop should terminate but
+            // not before processing any remaining messages so while Pending()
+            // returns true, do process them
+            if ( m_impl->ShouldExit() )
+            {
+                while ( Pending() )
+                    Dispatch();
 
-        // generate and process idle events for as long as we don't have
-        // anything else to do
-        while ( !Pending() && m_impl->SendIdleMessage() )
-            ;
+                break;
+            }
 
-        // a message came or no more idle processing to do, sit in Dispatch()
-        // waiting for the next message
-        if ( !Dispatch() )
-        {
-            // we got WM_QUIT
-            break;
+            // a message came or no more idle processing to do, sit in
+            // Dispatch() waiting for the next message
+            if ( !Dispatch() )
+            {
+                // we got WM_QUIT
+                break;
+            }
+        }
         }
+        wxCATCH_ALL( 
+            switch (m_impl->OnCatchAll()) {
+                case wxEventLoopImpl::catch_continue:
+                    retryAfterException=true;
+                    break;
+                case wxEventLoopImpl::catch_exit:
+                    OnExit();
+                    break;
+                case wxEventLoopImpl::catch_rethrow:
+                    OnExit();
+                    // should be replaced with wx macro, but
+                    // there is none yet. OTOH, wxCATCH_ALL isn't
+                    // expanded unless wxUSE_EXCEPTIONS, so its
+                    // safe to use throw here.
+                    throw;
+                default:
+                    break;
     }
+        )
+#if wxUSE_EXCEPTIONS
+    } while (retryAfterException);
+#endif
 
-    int exitcode = m_impl->GetExitCode();
-    delete m_impl;
-    m_impl = NULL;
-
-    ms_activeLoop = oldLoop;
-
-    return exitcode;
+    return m_impl->GetExitCode();
 }
 
 void wxEventLoop::Exit(int rc)
 {
     wxCHECK_RET( IsRunning(), _T("can't call Exit() if not running") );
 
-    m_impl->SetExitCode(rc);
+    m_impl->Exit(rc);
+
+    OnExit();
 
-    ::PostQuitMessage(rc);
+    // all we have to do to exit from the loop is to (maybe) wake it up so that
+    // it can notice that Exit() had been called
+    //
+    // in particular, we do *not* use PostQuitMessage() here because we're not
+    // sure that WM_QUIT is going to be processed by the correct event loop: it
+    // is possible that another one is started before this one has a chance to
+    // process WM_QUIT
+    ::PostMessage(NULL, WM_NULL, 0, 0);
 }
 
 // ----------------------------------------------------------------------------