From 7e1bcfa80e19ac853b8bc7084075fe3b01e63f03 Mon Sep 17 00:00:00 2001 From: Mattia Barbon Date: Sun, 2 Feb 2003 15:48:50 +0000 Subject: [PATCH] Implemented wxEventLoop for wxMotif, and used it in wxDialog::ShowModal, wxWindow::DoPopupMenu and for the application main loop. Implemented wxWakeUpIdle. Fixed crash when a popup menu entry is used to close/destroy the parent window of the menu. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@19078 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- distrib/msw/tmake/filelist.txt | 1 + include/wx/motif/app.h | 25 +- include/wx/motif/dialog.h | 3 + include/wx/motif/menu.h | 3 +- include/wx/motif/private.h | 34 ++- src/motif/app.cpp | 226 ++-------------- src/motif/dialog.cpp | 53 +--- src/motif/evtloop.cpp | 467 +++++++++++++++++++++++++++++++++ src/motif/files.lst | 2 + src/motif/menu.cpp | 50 +--- src/motif/menuitem.cpp | 9 + src/motif/window.cpp | 48 ++-- 12 files changed, 575 insertions(+), 346 deletions(-) create mode 100644 src/motif/evtloop.cpp diff --git a/distrib/msw/tmake/filelist.txt b/distrib/msw/tmake/filelist.txt index a5510edd27..bab1a3ae7e 100644 --- a/distrib/msw/tmake/filelist.txt +++ b/distrib/msw/tmake/filelist.txt @@ -584,6 +584,7 @@ dcclient.cpp Motif dcmemory.cpp Motif dcscreen.cpp Motif dialog.cpp Motif +evtloop.cpp Motif filedlg.cpp Motif font.cpp Motif frame.cpp Motif diff --git a/include/wx/motif/app.h b/include/wx/motif/app.h index 650736e9c3..b8e43947c4 100644 --- a/include/wx/motif/app.h +++ b/include/wx/motif/app.h @@ -20,7 +20,6 @@ // headers // ---------------------------------------------------------------------------- -#include "wx/gdicmn.h" #include "wx/event.h" // ---------------------------------------------------------------------------- @@ -32,6 +31,7 @@ class WXDLLEXPORT wxWindow; class WXDLLEXPORT wxApp; class WXDLLEXPORT wxKeyEvent; class WXDLLEXPORT wxLog; +class WXDLLEXPORT wxEventLoop; // ---------------------------------------------------------------------------- // the wxApp class for Motif - see wxAppBase for more details @@ -43,7 +43,7 @@ class WXDLLEXPORT wxApp : public wxAppBase public: wxApp(); - ~wxApp() {} + virtual ~wxApp(); // override base class (pure) virtuals // ----------------------------------- @@ -71,20 +71,6 @@ public: // Returns TRUE if more idle time is requested. bool SendIdleEvents(wxWindow* win); - // Motif implementation. - - // Processes an X event. - virtual void ProcessXEvent(WXEvent* event); - - // Returns TRUE if an accelerator has been processed - virtual bool CheckForAccelerator(WXEvent* event); - - // Returns TRUE if a key down event has been processed - virtual bool CheckForKeyDown(WXEvent* event); - - // Returns TRUE if a key up event has been processed - virtual bool CheckForKeyUp(WXEvent* event); - protected: bool m_showOnInit; @@ -105,12 +91,11 @@ public: // This handler is called when a property change event occurs virtual void HandlePropertyChange(WXEvent *event); -public: +private: static long sm_lastMessageTime; int m_nCmdShow; - -protected: - bool m_keepGoing; + + wxEventLoop* m_eventLoop; // Motif-specific WXAppContext m_appContext; diff --git a/include/wx/motif/dialog.h b/include/wx/motif/dialog.h index c8d36d63fa..0e3b7980f6 100644 --- a/include/wx/motif/dialog.h +++ b/include/wx/motif/dialog.h @@ -18,6 +18,8 @@ WXDLLEXPORT_DATA(extern const char*) wxDialogNameStr; +class WXDLLEXPORT wxEventLoop; + // Dialog boxes class WXDLLEXPORT wxDialog : public wxDialogBase { @@ -102,6 +104,7 @@ private: //// Motif-specific bool m_modalShowing; + wxEventLoop* m_eventLoop; protected: virtual void DoSetSize(int x, int y, diff --git a/include/wx/motif/menu.h b/include/wx/motif/menu.h index 50af6f3781..fb1b8d8709 100644 --- a/include/wx/motif/menu.h +++ b/include/wx/motif/menu.h @@ -93,7 +93,8 @@ public: WXWidget GetHandle() const { return m_menuWidget; } bool IsTearOff() const { return (m_style & wxMENU_TEAROFF) != 0; } - + + void DestroyWidgetAndDetach(); public: // Motif-specific data int m_numColumns; diff --git a/include/wx/motif/private.h b/include/wx/motif/private.h index ec1f7efa65..5d1f132aad 100644 --- a/include/wx/motif/private.h +++ b/include/wx/motif/private.h @@ -25,10 +25,12 @@ // ---------------------------------------------------------------------------- // All widgets should have this as their resize proc. -extern void wxWidgetResizeProc(Widget w, XConfigureEvent *event, String args[], int *num_args); +extern void wxWidgetResizeProc(Widget w, XConfigureEvent *event, + String args[], int *num_args); // For repainting arbitrary windows -void wxUniversalRepaintProc(Widget w, XtPointer WXUNUSED(c_data), XEvent *event, char *); +void wxUniversalRepaintProc(Widget w, XtPointer WXUNUSED(c_data), + XEvent *event, char *); // ---------------------------------------------------------------------------- // we maintain a hash table which contains the mapping from Widget to wxWindow @@ -51,11 +53,17 @@ extern XmString wxFindAcceleratorText (const char *s); // ---------------------------------------------------------------------------- // TranslateXXXEvent() functions - translate Motif event to wxWindow one // ---------------------------------------------------------------------------- -extern bool wxTranslateMouseEvent(wxMouseEvent& wxevent, wxWindow *win, Widget widget, XEvent *xevent); -extern bool wxTranslateKeyEvent(wxKeyEvent& wxevent, wxWindow *win, Widget widget, XEvent *xevent); -extern void wxDoChangeForegroundColour(WXWidget widget, wxColour& foregroundColour); -extern void wxDoChangeBackgroundColour(WXWidget widget, wxColour& backgroundColour, bool changeArmColour = FALSE); +extern bool wxTranslateMouseEvent(wxMouseEvent& wxevent, wxWindow *win, + Widget widget, XEvent *xevent); +extern bool wxTranslateKeyEvent(wxKeyEvent& wxevent, wxWindow *win, + Widget widget, XEvent *xevent); + +extern void wxDoChangeForegroundColour(WXWidget widget, + wxColour& foregroundColour); +extern void wxDoChangeBackgroundColour(WXWidget widget, + wxColour& backgroundColour, + bool changeArmColour = FALSE); #define wxNO_COLORS 0x00 #define wxBACK_COLORS 0x01 @@ -78,6 +86,11 @@ extern XColor itemColors[5] ; class wxXmString { public: + wxXmString(const char* str) + { + m_string = XmStringCreateLtoR((char *)str, XmSTRING_DEFAULT_CHARSET); + } + wxXmString(const wxString& str) { m_string = XmStringCreateLtoR((char *)str.c_str(), @@ -94,6 +107,15 @@ private: XmString m_string; }; +// ---------------------------------------------------------------------------- +// executes one main loop iteration (implemented in src/motif/evtloop.cpp) +// ---------------------------------------------------------------------------- + +class wxEventLoop; + +// returns true if the loop should be exited +bool wxDoEventLoopIteration( wxEventLoop& evtLoop ); + // ---------------------------------------------------------------------------- // macros to avoid casting WXFOO to Foo all the time // ---------------------------------------------------------------------------- diff --git a/src/motif/app.cpp b/src/motif/app.cpp index 3cfc2853e2..330ba91007 100644 --- a/src/motif/app.cpp +++ b/src/motif/app.cpp @@ -35,6 +35,7 @@ #include "wx/memory.h" #include "wx/log.h" #include "wx/intl.h" +#include "wx/evtloop.h" #if wxUSE_THREADS #include "wx/thread.h" @@ -61,6 +62,7 @@ #include extern wxList wxPendingDelete; +extern bool wxAddIdleCallback(); wxApp *wxTheApp = NULL; @@ -267,6 +269,7 @@ wxApp::wxApp() argc = 0; argv = NULL; + m_eventLoop = new wxEventLoop; m_mainColormap = (WXColormap) NULL; m_appContext = (WXAppContext) NULL; m_topLevelWidget = (WXWidget) NULL; @@ -274,6 +277,11 @@ wxApp::wxApp() m_initialDisplay = (WXDisplay*) 0; } +wxApp::~wxApp() +{ + delete m_eventLoop; +} + bool wxApp::Initialized() { if (GetTopWindow()) @@ -284,8 +292,6 @@ bool wxApp::Initialized() int wxApp::MainLoop() { - m_keepGoing = TRUE; - /* * Sit around forever waiting to process X-events. Property Change * event are handled special, because they have to refer to @@ -297,129 +303,23 @@ int wxApp::MainLoop() XDefaultRootWindow(XtDisplay((Widget) wxTheApp->GetTopLevelWidget())), PropertyChangeMask); - XEvent event; - - // Use this flag to allow breaking the loop via wxApp::ExitMainLoop() - while (m_keepGoing) - { - XtAppNextEvent( (XtAppContext) wxTheApp->GetAppContext(), &event); - - ProcessXEvent((WXEvent*) & event); - - if (XtAppPending( (XtAppContext) wxTheApp->GetAppContext() ) == 0) - { - if (!ProcessIdle()) - { -#if wxUSE_THREADS - // leave the main loop to give other threads a chance to - // perform their GUI work - wxMutexGuiLeave(); - wxUsleep(20); - wxMutexGuiEnter(); -#endif - } - } - - } + m_eventLoop->Run(); return 0; } -// Processes an X event. -void wxApp::ProcessXEvent(WXEvent* _event) -{ - XEvent* event = (XEvent*) _event; - - if (event->type == KeyPress) - { -#if 0 // def __WXDEBUG__ - Widget widget = XtWindowToWidget(event->xany.display, event->xany.window); - wxLogDebug("Got key press event for 0x%08x (parent = 0x%08x)", - widget, XtParent(widget)); -#endif // DEBUG - - if (CheckForAccelerator(_event)) - { - // Do nothing! We intercepted and processed the event as an - // accelerator. - return; - } -#if 1 - // It seemed before that this hack was redundant and - // key down events were being generated by wxCanvasInputEvent. - // But no longer - why ??? - // - else if (CheckForKeyDown(_event)) - { - // We intercepted and processed the key down event - return; - } -#endif - else - { - XtDispatchEvent(event); - return; - } - } - else if (event->type == KeyRelease) - { - // TODO: work out why we still need this ! -michael - // - if (CheckForKeyUp(_event)) - { - // We intercepted and processed the key up event - return; - } - else - { - XtDispatchEvent(event); - return; - } - } - else if (event->type == PropertyNotify) - { - HandlePropertyChange(_event); - return; - } - else if (event->type == ResizeRequest) - { - /* Terry Gitnick - 1/21/98 - * If resize event, don't resize until the last resize event for this - * window is recieved. Prevents flicker as windows are resized. - */ - - Display *disp = XtDisplay((Widget) wxTheApp->GetTopLevelWidget()); - Window win = event->xany.window; - XEvent report; - - // to avoid flicker - report = * event; - while( XCheckTypedWindowEvent (disp, win, ResizeRequest, &report)); - - // TODO: when implementing refresh optimization, we can use - // XtAddExposureToRegion to expand the window's paint region. - - XtDispatchEvent(event); - } - else - { - XtDispatchEvent(event); - } -} - +// Processes an idle event. // Returns TRUE if more time is needed. bool wxApp::ProcessIdle() { wxIdleEvent event; - event.SetEventObject(this); - ProcessEvent(event); - return event.MoreRequested(); + return ProcessEvent(event) && event.MoreRequested(); } void wxApp::ExitMainLoop() { - m_keepGoing = FALSE; + m_eventLoop->Exit(); } // Is a message/event pending? @@ -435,11 +335,7 @@ bool wxApp::Pending() // Dispatch a message. void wxApp::Dispatch() { - // XtAppProcessEvent( (XtAppContext) wxTheApp->GetAppContext(), XtIMAll); - - XEvent event; - XtAppNextEvent((XtAppContext) GetAppContext(), &event); - ProcessXEvent((WXEvent*) & event); + m_eventLoop->Dispatch(); } // This should be redefined in a derived class for @@ -489,13 +385,6 @@ void wxApp::OnIdle(wxIdleEvent& event) inOnIdle = FALSE; } -void wxWakeUpIdle() -{ - // **** please implement me! **** - // Wake up the idle handler processor, even if it is in another thread... -} - - // Send idle event to all top-level windows bool wxApp::SendIdleEvents() { @@ -539,7 +428,7 @@ bool wxApp::SendIdleEvents(wxWindow* win) void wxApp::DeletePendingObjects() { - wxNode *node = wxPendingDelete.GetFirst(); + wxList::Node *node = wxPendingDelete.GetFirst(); while (node) { wxObject *obj = node->GetData(); @@ -610,6 +499,8 @@ bool wxApp::OnInitGui() GetMainColormap(dpy); m_maxRequestSize = XMaxRequestSize((Display*) dpy); + wxAddIdleCallback(); + return TRUE; } @@ -629,91 +520,6 @@ WXColormap wxApp::GetMainColormap(WXDisplay* display) return (WXColormap) c; } -// Returns TRUE if an accelerator has been processed -bool wxApp::CheckForAccelerator(WXEvent* event) -{ - XEvent* xEvent = (XEvent*) event; - if (xEvent->xany.type == KeyPress) - { - // Find a wxWindow for this window - // TODO: should get display for the window, not the current display - Widget widget = XtWindowToWidget((Display*) wxGetDisplay(), xEvent->xany.window); - wxWindow* win = NULL; - - // Find the first wxWindow that corresponds to this event window - while (widget && !(win = wxGetWindowFromTable(widget))) - widget = XtParent(widget); - - if (!widget || !win) - return FALSE; - - wxKeyEvent keyEvent(wxEVT_CHAR); - wxTranslateKeyEvent(keyEvent, win, (Widget) 0, xEvent); - - // Now we have a wxKeyEvent and we have a wxWindow. - // Go up the hierarchy until we find a matching accelerator, - // or we get to the top. - while (win) - { - if (win->ProcessAccelerator(keyEvent)) - return TRUE; - win = win->GetParent(); - } - return FALSE; - } - return FALSE; -} - -bool wxApp::CheckForKeyDown(WXEvent* event) -{ - XEvent* xEvent = (XEvent*) event; - if (xEvent->xany.type == KeyPress) - { - Widget widget = XtWindowToWidget((Display*) wxGetDisplay(), - xEvent->xany.window); - wxWindow* win = NULL; - - // Find the first wxWindow that corresponds to this event window - while (widget && !(win = wxGetWindowFromTable(widget))) - widget = XtParent(widget); - - if (!widget || !win) - return FALSE; - - wxKeyEvent keyEvent(wxEVT_KEY_DOWN); - wxTranslateKeyEvent(keyEvent, win, (Widget) 0, xEvent); - - return win->ProcessEvent( keyEvent ); - } - - return FALSE; -} - -bool wxApp::CheckForKeyUp(WXEvent* event) -{ - XEvent* xEvent = (XEvent*) event; - if (xEvent->xany.type == KeyRelease) - { - Widget widget = XtWindowToWidget((Display*) wxGetDisplay(), - xEvent->xany.window); - wxWindow* win = NULL; - - // Find the first wxWindow that corresponds to this event window - while (widget && !(win = wxGetWindowFromTable(widget))) - widget = XtParent(widget); - - if (!widget || !win) - return FALSE; - - wxKeyEvent keyEvent(wxEVT_KEY_UP); - wxTranslateKeyEvent(keyEvent, win, (Widget) 0, xEvent); - - return win->ProcessEvent( keyEvent ); - } - - return FALSE; -} - void wxExit() { int retValue = 0; diff --git a/src/motif/dialog.cpp b/src/motif/dialog.cpp index 5fdd39cb9f..1e3191c27e 100644 --- a/src/motif/dialog.cpp +++ b/src/motif/dialog.cpp @@ -24,6 +24,7 @@ #include "wx/utils.h" #include "wx/app.h" #include "wx/settings.h" +#include "wx/evtloop.h" #ifdef __VMS__ #pragma message disable nosimpint @@ -56,7 +57,7 @@ // A stack of modal_showing flags, since we can't rely // on accessing wxDialog::m_modalShowing within // wxDialog::Show in case a callback has deleted the wxDialog. -static wxList wxModalShowingStack; +// static wxList wxModalShowingStack; // Lists to keep track of windows, so we can disable/enable them // for modal dialogs @@ -81,6 +82,7 @@ END_EVENT_TABLE() wxDialog::wxDialog() { m_modalShowing = FALSE; + m_eventLoop = NULL; m_backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE); } @@ -98,6 +100,7 @@ bool wxDialog::Create(wxWindow *parent, wxWindowID id, return FALSE; m_modalShowing = FALSE; + m_eventLoop = NULL; m_backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE); m_foregroundColour = *wxBLACK; @@ -204,6 +207,7 @@ void wxDialog::SetModal(bool flag) wxDialog::~wxDialog() { m_isBeingDeleted = TRUE; + delete m_eventLoop; if (m_mainWidget) { @@ -312,52 +316,24 @@ int wxDialog::ShowModal() if (m_modalShowing) return 0; - - wxModalShowingStack.Insert((wxObject *)TRUE); + m_eventLoop = new wxEventLoop; m_modalShowing = TRUE; XtAddGrab((Widget) m_mainWidget, TRUE, FALSE); - XEvent event; - - // Loop until we signal that the dialog should be closed - while ((wxModalShowingStack.Number() > 0) && ((int)(wxModalShowingStack.First()->Data()) != 0)) - { - // XtAppProcessEvent((XtAppContext) wxTheApp->GetAppContext(), XtIMAll); - - XtAppNextEvent((XtAppContext) wxTheApp->GetAppContext(), &event); - wxTheApp->ProcessXEvent((WXEvent*) &event); - - if (XtAppPending( (XtAppContext) wxTheApp->GetAppContext() ) == 0) - { - if (!wxTheApp->ProcessIdle()) - { -#if wxUSE_THREADS - // leave the main loop to give other threads a chance to - // perform their GUI work - wxMutexGuiLeave(); - wxUsleep(20); - wxMutexGuiEnter(); -#endif - } - } - } - - // Remove modal dialog flag from stack - wxNode *node = wxModalShowingStack.First(); - if (node) - delete node; + m_eventLoop->Run(); // Now process all events in case they get sent to a destroyed dialog XSync(XtDisplay((Widget) wxTheApp->GetTopLevelWidget()), FALSE); - while (XtAppPending((XtAppContext) wxTheApp->GetAppContext())) + while (m_eventLoop->Pending()) { XFlush(XtDisplay((Widget) wxTheApp->GetTopLevelWidget())); - XtAppNextEvent((XtAppContext) wxTheApp->GetAppContext(), &event); - - wxTheApp->ProcessXEvent((WXEvent*) &event); + m_eventLoop->Dispatch(); } + delete m_eventLoop; + m_eventLoop = NULL; + // TODO: is it safe to call this, if the dialog may have been deleted // by now? Probably only if we're using delayed deletion of dialogs. return GetReturnCode(); @@ -376,10 +352,7 @@ void wxDialog::EndModal(int retCode) Show(FALSE); m_modalShowing = FALSE; - - wxNode *node = wxModalShowingStack.First(); - if (node) - node->SetData((wxObject *)FALSE); + m_eventLoop->Exit(); } // Standard buttons diff --git a/src/motif/evtloop.cpp b/src/motif/evtloop.cpp new file mode 100644 index 0000000000..57a3283542 --- /dev/null +++ b/src/motif/evtloop.cpp @@ -0,0 +1,467 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: motif/evtloop.cpp +// Purpose: implements wxEventLoop for Motif +// Author: Mattia Barbon +// Modified by: +// Created: 01.11.02 +// RCS-ID: $Id$ +// Copyright: (c) 2002 Mattia Barbon +// License: wxWindows license +/////////////////////////////////////////////////////////////////////////////// + +// ============================================================================ +// declarations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +#ifdef __GNUG__ + #pragma implementation "evtloop.h" +#endif + +#ifdef __VMS +#define XtParent XTPARENT +#define XtDisplay XTDISPLAY +#endif + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP +#endif //WX_PRECOMP + +#include "wx/evtloop.h" +#include "wx/event.h" +#include "wx/app.h" + +#ifdef __VMS__ +#pragma message disable nosimpint +#endif +#include +#include +#ifdef __VMS__ +#pragma message enable nosimpint +#endif + +#include "wx/motif/private.h" + +static bool CheckForKeyUp(XEvent* event); +static bool CheckForKeyDown(XEvent* event); +static bool CheckForAccelerator(XEvent* event); +static void ProcessXEvent(XEvent* event); +static void wxBreakDispatch(); + +// ---------------------------------------------------------------------------- +// wxEventLoopImpl +// ---------------------------------------------------------------------------- + +class WXDLLEXPORT wxEventLoopImpl +{ +public: + // ctor + wxEventLoopImpl() { SetExitCode(0); } + + // set/get the exit code + void SetExitCode(int exitcode) { m_exitcode = exitcode; } + int GetExitCode() const { return m_exitcode; } + + bool SendIdleMessage(); + bool GetKeepGoing() const { return m_keepGoing; } + void SetKeepGoing(bool keepGoing) { m_keepGoing = keepGoing; } +private: + // the exit code of the event loop + int m_exitcode; + bool m_keepGoing; +}; + +// ---------------------------------------------------------------------------- +// wxEventLoopImpl idle event processing +// ---------------------------------------------------------------------------- + +static bool SendIdleMessage() +{ + wxIdleEvent event; + + return wxTheApp->ProcessEvent(event) && event.MoreRequested(); +} + +bool wxEventLoopImpl::SendIdleMessage() +{ + return ::SendIdleMessage(); +} + +// ============================================================================ +// wxEventLoop implementation +// ============================================================================ + +// ---------------------------------------------------------------------------- +// wxEventLoop running and exiting +// ---------------------------------------------------------------------------- + +wxEventLoop *wxEventLoop::ms_activeLoop = NULL; + +wxEventLoop::~wxEventLoop() +{ + wxASSERT_MSG( !m_impl, _T("should have been deleted in Run()") ); +} + +bool wxEventLoop::IsRunning() const +{ + return m_impl != NULL; +} + +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") ); + + wxEventLoop *oldLoop = ms_activeLoop; + ms_activeLoop = this; + + m_impl = new wxEventLoopImpl; + m_impl->SetKeepGoing( true ); + + for( ;; ) + { + if( !wxDoEventLoopIteration( *this ) ) + break; + } + + int exitcode = m_impl->GetExitCode(); + delete m_impl; + m_impl = NULL; + + ms_activeLoop = oldLoop; + + return exitcode; +} + +void wxEventLoop::Exit(int rc) +{ + wxCHECK_RET( IsRunning(), _T("can't call Exit() if not running") ); + + m_impl->SetExitCode(rc); + m_impl->SetKeepGoing( false ); + + ::wxBreakDispatch(); +} + +// ---------------------------------------------------------------------------- +// wxEventLoop message processing dispatching +// ---------------------------------------------------------------------------- + +bool wxEventLoop::Pending() const +{ + return XtAppPending( (XtAppContext)wxTheApp->GetAppContext() ) != 0; +} + +bool wxEventLoop::Dispatch() +{ + XEvent event; + XtAppContext context = (XtAppContext)wxTheApp->GetAppContext(); + + if( XtAppPeekEvent( context, &event ) != 0 ) + { + XtAppNextEvent( context, &event ); + ProcessXEvent( &event ); + } + else + XtAppProcessEvent( context, XtIMTimer|XtIMAlternateInput|XtIMSignal ); + + return m_impl ? m_impl->GetKeepGoing() : true; +} + +// ---------------------------------------------------------------------------- +// Static functions (originally in app.cpp) +// ---------------------------------------------------------------------------- + +void ProcessXEvent(XEvent* event) +{ + if (event->type == KeyPress) + { + if (CheckForAccelerator(event)) + { + // Do nothing! We intercepted and processed the event as an + // accelerator. + return; + } + // It seemed before that this hack was redundant and + // key down events were being generated by wxCanvasInputEvent. + // But no longer - why ??? + // + else if (CheckForKeyDown(event)) + { + // We intercepted and processed the key down event + return; + } + else + { + XtDispatchEvent(event); + return; + } + } + else if (event->type == KeyRelease) + { + // TODO: work out why we still need this ! -michael + // + if (::CheckForKeyUp(event)) + { + // We intercepted and processed the key up event + return; + } + else + { + XtDispatchEvent(event); + return; + } + } + else if (event->type == PropertyNotify) + { + wxTheApp->HandlePropertyChange(event); + return; + } + else if (event->type == ResizeRequest) + { + /* Terry Gitnick - 1/21/98 + * If resize event, don't resize until the last resize event for this + * window is recieved. Prevents flicker as windows are resized. + */ + + Display *disp = XtDisplay((Widget) wxTheApp->GetTopLevelWidget()); + Window win = event->xany.window; + XEvent report; + + // to avoid flicker + report = * event; + while( XCheckTypedWindowEvent (disp, win, ResizeRequest, &report)); + + // TODO: when implementing refresh optimization, we can use + // XtAddExposureToRegion to expand the window's paint region. + + XtDispatchEvent(event); + } + else + { + XtDispatchEvent(event); + } +} + +// Returns true if an accelerator has been processed +bool CheckForAccelerator(XEvent* event) +{ + if (event->xany.type == KeyPress) + { + // Find a wxWindow for this window + // TODO: should get display for the window, not the current display + Widget widget = XtWindowToWidget((Display*) wxGetDisplay(), + event->xany.window); + wxWindow* win = NULL; + + // Find the first wxWindow that corresponds to this event window + while (widget && !(win = wxGetWindowFromTable(widget))) + widget = XtParent(widget); + + if (!widget || !win) + return false; + + wxKeyEvent keyEvent(wxEVT_CHAR); + wxTranslateKeyEvent(keyEvent, win, (Widget) 0, event); + + // Now we have a wxKeyEvent and we have a wxWindow. + // Go up the hierarchy until we find a matching accelerator, + // or we get to the top. + while (win) + { + if (win->ProcessAccelerator(keyEvent)) + return true; + win = win->GetParent(); + } + return false; + } + return false; +} + +// bool wxApp::CheckForKeyDown(WXEvent* event) { wxFAIL; return false; } + +bool CheckForKeyDown(XEvent* event) +{ + if (event->xany.type == KeyPress) + { + Widget widget = XtWindowToWidget((Display*) wxGetDisplay(), + event->xany.window); + wxWindow* win = NULL; + + // Find the first wxWindow that corresponds to this event window + while (widget && !(win = wxGetWindowFromTable(widget))) + widget = XtParent(widget); + + if (!widget || !win) + return false; + + wxKeyEvent keyEvent(wxEVT_KEY_DOWN); + wxTranslateKeyEvent(keyEvent, win, (Widget) 0, event); + + return win->ProcessEvent( keyEvent ); + } + + return false; +} + +// bool wxApp::CheckForKeyUp(WXEvent* event) { wxFAIL; return false; } + +bool CheckForKeyUp(XEvent* event) +{ + if (event->xany.type == KeyRelease) + { + Widget widget = XtWindowToWidget((Display*) wxGetDisplay(), + event->xany.window); + wxWindow* win = NULL; + + // Find the first wxWindow that corresponds to this event window + while (widget && !(win = wxGetWindowFromTable(widget))) + widget = XtParent(widget); + + if (!widget || !win) + return false; + + wxKeyEvent keyEvent(wxEVT_KEY_UP); + wxTranslateKeyEvent(keyEvent, win, (Widget) 0, event); + + return win->ProcessEvent( keyEvent ); + } + + return false; +} + +// ---------------------------------------------------------------------------- +// executes one main loop iteration (declared in include/wx/motif/private.h) +// ---------------------------------------------------------------------------- + +bool wxDoEventLoopIteration( wxEventLoop& evtLoop ) +{ +#if wxUSE_THREADS + // leave the main loop to give other threads a chance to + // perform their GUI work + wxMutexGuiLeave(); + wxUsleep(20); + wxMutexGuiEnter(); +#endif + + while ( !evtLoop.Pending() && ::SendIdleMessage() ) + ; + + if( !evtLoop.Dispatch() ) + return false; + + return true; +} + +// ---------------------------------------------------------------------------- +// ::wxWakeUpIdle implementation +// ---------------------------------------------------------------------------- + +// As per Vadim's suggestion, we open a pipe, and XtAppAddInputSource it; +// writing a single byte to the pipe will cause wxEventLoop::Pending +// to return true, and wxEventLoop::Dispatch to dispatch an input handler +// that simply removes the byte(s), and to return, which will cause +// an idle event to be sent + +// also wxEventLoop::Exit is implemented that way, so that exiting an +// event loop won't require an event being in the queue + +#include "wx/module.h" + +#include +#include +#include + +static XtInputId inputId; +static int idleFds[2] = { -1, -1 }; + +class wxIdlePipeModule : public wxModule +{ +public: + wxIdlePipeModule() {}; + + virtual bool OnInit() + { + if( pipe(idleFds) != 0 ) + return false; + + return true; + } + + virtual void OnExit() + { + close( idleFds[0] ); + close( idleFds[1] ); + } +private: + DECLARE_DYNAMIC_CLASS(wxIdlePipeModule); +}; + +IMPLEMENT_DYNAMIC_CLASS(wxIdlePipeModule, wxModule); + +static void wxInputCallback( XtPointer, int* fd, XtInputId* ) +{ + char buffer[128]; + + // wxWakeUpIdle may have been called more than once + for(;;) + { + fd_set in; + struct timeval timeout; + + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + FD_ZERO( &in ); + FD_SET( *fd, &in ); + + if( select( *fd + 1, &in, NULL, NULL, &timeout ) <= 0 ) + break; + if( read( *fd, buffer, sizeof(buffer) - 1 ) != sizeof(buffer) - 1 ) + break; + } +} + +static void wxBreakDispatch() +{ + char dummy; + + // check if wxWakeUpIdle has already been called + fd_set in; + struct timeval timeout; + + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + FD_ZERO( &in ); + FD_SET( idleFds[0], &in ); + + if( select( idleFds[0] + 1, &in, NULL, NULL, &timeout ) > 0 ) + return; + + // write a single byte + write( idleFds[1], &dummy, 1 ); +} + +void wxWakeUpIdle() +{ + ::wxBreakDispatch(); +} + +bool wxAddIdleCallback() +{ + // install input handler for wxWakeUpIdle + inputId = XtAppAddInput( (XtAppContext) wxTheApp->GetAppContext(), + idleFds[0], + (XtPointer)XtInputReadMask, + wxInputCallback, + NULL ); + + return true; +} + diff --git a/src/motif/files.lst b/src/motif/files.lst index 75620f067a..98b08d38af 100644 --- a/src/motif/files.lst +++ b/src/motif/files.lst @@ -199,6 +199,7 @@ ALL_SOURCES = \ motif/dcmemory.cpp \ motif/dcscreen.cpp \ motif/dialog.cpp \ + motif/evtloop.cpp \ motif/filedlg.cpp \ motif/font.cpp \ motif/frame.cpp \ @@ -834,6 +835,7 @@ GUIOBJS = \ dcmemory.o \ dcscreen.o \ dialog.o \ + evtloop.o \ filedlg.o \ font.o \ frame.o \ diff --git a/src/motif/menu.cpp b/src/motif/menu.cpp index b7e9194893..8a42ba4f1d 100644 --- a/src/motif/menu.cpp +++ b/src/motif/menu.cpp @@ -440,28 +440,20 @@ bool wxMenuBar::DestroyMenuBar() return TRUE; } -//// Motif-specific -static XtWorkProcId WorkProcMenuId; - -/* Since PopupMenu under Motif stills grab right mouse button events -* after it was closed, we need to delete the associated widgets to -* allow next PopUpMenu to appear... -*/ - -int PostDeletionOfMenu( XtPointer* clientData ) +// Since PopupMenu under Motif stills grab right mouse button events +// after it was closed, we need to delete the associated widgets to +// allow next PopUpMenu to appear... +void wxMenu::DestroyWidgetAndDetach() { - XtRemoveWorkProc(WorkProcMenuId); - wxMenu *menu = (wxMenu *)clientData; - - if (menu->GetMainWidget()) + if (GetMainWidget()) { - wxMenu *menuParent = menu->GetParent(); + wxMenu *menuParent = GetParent(); if ( menuParent ) { wxMenuItemList::Node *node = menuParent->GetMenuItems().GetFirst(); while ( node ) { - if ( node->GetData()->GetSubMenu() == menu ) + if ( node->GetData()->GetSubMenu() == this ) { menuParent->GetMenuItems().DeleteNode(node); @@ -472,33 +464,11 @@ int PostDeletionOfMenu( XtPointer* clientData ) } } - menu->DestroyMenu(TRUE); + DestroyMenu(TRUE); } // Mark as no longer popped up - menu->m_menuId = -1; - - return TRUE; -} - -void -wxMenuPopdownCallback(Widget WXUNUSED(w), XtPointer clientData, - XtPointer WXUNUSED(ptr)) -{ - wxMenu *menu = (wxMenu *)clientData; - - // Added by JOREL Jean-Charles - /* Since Callbacks of MenuItems are not yet processed, we put a - * background job which will be done when system will be idle. - * What awful hack!! :( - */ - - WorkProcMenuId = XtAppAddWorkProc( - (XtAppContext) wxTheApp->GetAppContext(), - (XtWorkProc) PostDeletionOfMenu, - (XtPointer) menu ); - // Apparently not found in Motif headers - // XtVaSetValues( w, XmNpopupEnabled, XmPOPUP_DISABLED, NULL ); + m_menuId = -1; } /* @@ -518,10 +488,12 @@ WXWidget wxMenu::CreateMenu (wxMenuBar * menuBar, WXWidget parent, wxMenu * topM if (!pullDown) { menu = XmCreatePopupMenu ((Widget) parent, "popup", args, 2); +#if 0 XtAddCallback(menu, XmNunmapCallback, (XtCallbackProc)wxMenuPopdownCallback, (XtPointer)this); +#endif } else { diff --git a/src/motif/menuitem.cpp b/src/motif/menuitem.cpp index c0006a0433..6df0931d6c 100644 --- a/src/motif/menuitem.cpp +++ b/src/motif/menuitem.cpp @@ -354,6 +354,7 @@ void wxMenuItemCallback (Widget WXUNUSED(w), XtPointer clientData, item->GetMenuBar()->GetMenuBarFrame()->GetEventHandler()->ProcessEvent(commandEvent); } + // this is the child of a popup menu else if (item->GetTopMenu()) { wxCommandEvent event (wxEVT_COMMAND_MENU_SELECTED, item->GetId()); @@ -361,6 +362,14 @@ void wxMenuItemCallback (Widget WXUNUSED(w), XtPointer clientData, event.SetInt( item->GetId() ); item->GetTopMenu()->ProcessCommand (event); + + // Since PopupMenu under Motif stills grab right mouse + // button events after it was closed, we need to delete + // the associated widgets to allow next PopUpMenu to + // appear; this needs to be done there because doing it in + // a WorkProc as before may cause crashes if a menu item causes + // the parent window of the menu to be destroyed + item->GetTopMenu()->DestroyWidgetAndDetach(); } } } diff --git a/src/motif/window.cpp b/src/motif/window.cpp index e77149f3a9..4ee8f71d5b 100644 --- a/src/motif/window.cpp +++ b/src/motif/window.cpp @@ -45,6 +45,7 @@ #include "wx/module.h" #include "wx/menuitem.h" #include "wx/log.h" +#include "wx/evtloop.h" #if wxUSE_DRAG_AND_DROP #include "wx/dnd.h" @@ -1151,6 +1152,8 @@ void wxWindow::DoSetToolTip(wxToolTip * WXUNUSED(tooltip)) // popup menus // ---------------------------------------------------------------------------- +#if wxUSE_MENUS + bool wxWindow::DoPopupMenu(wxMenu *menu, int x, int y) { Widget widget = (Widget) GetMainWidget(); @@ -1165,7 +1168,8 @@ bool wxWindow::DoPopupMenu(wxMenu *menu, int x, int y) if (menu->GetParent() && (menu->GetId() != -1)) return FALSE; - if (menu->GetMainWidget()) { + if (menu->GetMainWidget()) + { menu->DestroyMenu(TRUE); } @@ -1214,7 +1218,6 @@ bool wxWindow::DoPopupMenu(wxMenu *menu, int x, int y) XmMenuPosition (menuWidget, &event); XtManageChild (menuWidget); - XEvent x_event; // The ID of a pop-up menu is 1 when active, and is set to 0 by the // idle-time destroy routine. // Waiting until this ID changes causes this function to block until @@ -1222,29 +1225,19 @@ bool wxWindow::DoPopupMenu(wxMenu *menu, int x, int y) // In other words, once this routine returns, it is safe to delete // the menu object. // Ian Brown + + wxEventLoop evtLoop; + while (menu->GetId() == 1) { - XtAppNextEvent( (XtAppContext) wxTheApp->GetAppContext(), &x_event); - - wxTheApp->ProcessXEvent((WXEvent*) & x_event); - - if (XtAppPending( (XtAppContext) wxTheApp->GetAppContext() ) == 0) - { - if (!wxTheApp->ProcessIdle()) - { -#if wxUSE_THREADS - // leave the main loop to give other threads a chance to - // perform their GUI work - wxMutexGuiLeave(); - wxUsleep(20); - wxMutexGuiEnter(); -#endif - } - } + wxDoEventLoopIteration( evtLoop ); } + return TRUE; } +#endif + // --------------------------------------------------------------------------- // moving and resizing // --------------------------------------------------------------------------- @@ -1352,9 +1345,9 @@ void wxWindow::DoSetSizeIntr(int x, int y, int width, int height, y = oldY; } - if ( width == -1 ) + if ( width <= 0 ) width = oldW; - if ( height == -1 ) + if ( height <= 0 ) height = oldH; bool nothingChanged = (x == oldX) && (y == oldY) && @@ -1390,15 +1383,6 @@ void wxWindow::DoSetSizeIntr(int x, int y, int width, int height, if (managed) XtManageChild(widget); - - // How about this bit. Maybe we don't need to generate size events - // all the time -- they'll be generated when the window is sized anyway. -#if 0 - wxSizeEvent sizeEvent(wxSize(width, height), GetId()); - sizeEvent.SetEventObject(this); - - GetEventHandler()->ProcessEvent(sizeEvent); -#endif // 0 } } @@ -1727,6 +1711,7 @@ void wxWindow::OnIdle(wxIdleEvent& WXUNUSED(event)) bool wxWindow::ProcessAccelerator(wxKeyEvent& event) { +#if wxUSE_ACCEL if (!m_acceleratorTable.Ok()) return FALSE; @@ -1753,6 +1738,7 @@ bool wxWindow::ProcessAccelerator(wxKeyEvent& event) wxFrame* frame = wxDynamicCast(parent, wxFrame); if ( frame ) { +#if wxUSE_MENUS // Try for a menu command if (frame->GetMenuBar()) { @@ -1767,6 +1753,7 @@ bool wxWindow::ProcessAccelerator(wxKeyEvent& event) return frame->GetEventHandler()->ProcessEvent(commandEvent); } } +#endif } // Find a child matching the command id @@ -1788,6 +1775,7 @@ bool wxWindow::ProcessAccelerator(wxKeyEvent& event) return FALSE; } // matches event }// for +#endif // We didn't match the key event against an accelerator. return FALSE;