1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/mgl/window.cpp
4 // Author: Vaclav Slavik
5 // (based on GTK & MSW implementations)
7 // Copyright: (c) 2001 SciTech Software, Inc. (www.scitechsoft.com)
8 // Licence: wxWindows license
9 /////////////////////////////////////////////////////////////////////////////
11 // ===========================================================================
13 // ===========================================================================
15 // ---------------------------------------------------------------------------
17 // ---------------------------------------------------------------------------
20 #pragma implementation "window.h"
23 // For compilers that support precompilation, includes "wx.h".
24 #include "wx/wxprec.h"
31 #include "wx/window.h"
35 #include "wx/dcclient.h"
42 #if wxUSE_DRAG_AND_DROP
47 #include "wx/sysopt.h"
48 #include "wx/mgl/private.h"
50 #include "wx/dcscreen.h"
55 #include "wx/tooltip.h"
58 // ---------------------------------------------------------------------------
60 // ---------------------------------------------------------------------------
62 // MGL window manager and associated DC.
63 winmng_t
*g_winMng
= NULL
;
64 MGLDevCtx
*g_displayDC
= NULL
;
66 extern wxList WXDLLEXPORT wxPendingDelete
;
69 // the window that has keyboard+joystick focus:
70 static wxWindowMGL
*g_focusedWindow
= NULL
;
71 // the window that is currently under mouse cursor:
72 static wxWindowMGL
*g_windowUnderMouse
= NULL
;
74 // ---------------------------------------------------------------------------
76 // ---------------------------------------------------------------------------
78 // Custom identifiers used to distinguish between various event handlers
79 // and capture handlers passed to MGL_wm
82 wxMGL_CAPTURE_MOUSE
= 1,
83 wxMGL_CAPTURE_KEYB
= 2
87 // ---------------------------------------------------------------------------
89 // ---------------------------------------------------------------------------
91 // wxCreateMGL_WM creates MGL display DC and associates it with winmng_t
92 // structure. Dimensions and depth of the DC are fetched from wxSystemOptions
94 // This function is *not* called from wxApp's initialization but rather at
95 // the time when WM is needed, i.e. when first wxWindow is created. This
96 // has two important effects:
97 // a) it is possible to write windowless wxMGL apps
98 // b) the app has plenty of time in wxApp::OnInit to feed wxSystemOptions
99 // with desired settings
101 // FIXME_MGL -- move to app.cpp??
102 static void wxDesktopPainter(window_t
*wnd
, MGLDC
*dc
)
104 // FIXME_MGL - for now...
105 MGL_setColorRGB(0x63, 0x63, 0x96);
106 MGL_fillRectCoord(0, 0, wnd
->width
, wnd
->height
);
110 bool wxCreateMGL_WM()
113 int width
= 640, height
= 480, depth
= 16;
114 int refresh
= MGL_DEFAULT_REFRESH
;
116 #if wxUSE_SYSTEM_OPTIONS
117 // FIXME_MGL -- so what is The Proper Way?
118 if ( wxSystemOptions::HasOption(wxT("mgl.screen-width") )
119 width
= wxSystemOptions::GetOptionInt(wxT("mgl.screen-width"));
120 if ( wxSystemOptions::HasOption(wxT("mgl.screen-height") )
121 height
= wxSystemOptions::GetOptionInt(wxT("mgl.screen-height"));
122 if ( wxSystemOptions::HasOption(wxT("mgl.screen-depth") )
123 depth
= wxSystemOptions::GetOptionInt(wxT("mgl.screen-depth"));
124 if ( wxSystemOptions::HasOption(wxT("mgl.screen-refresh") )
125 refresh
= wxSystemOptions::GetOptionInt(wxT("mgl.screen-refresh"));
128 mode
= MGL_findMode(width
, height
, depth
);
131 wxLogWarning(_("Mode %ix%i-%i not available, falling back to default mode."), width
, height
, depth
);
132 mode
= 0; // always available
134 g_displayDC
= new MGLDisplayDC(mode
, 1, refresh
);
135 if ( !g_displayDC
->isValid() )
142 g_winMng
= MGL_wmCreate(g_displayDC
->getDC());
146 MGL_wmSetWindowPainter(MGL_wmGetRootWindow(g_winMng
), wxDesktopPainter
);
151 void wxDestroyMGL_WM()
155 MGL_wmDestroy(g_winMng
);
165 // ---------------------------------------------------------------------------
167 // ---------------------------------------------------------------------------
169 static void wxWindowPainter(window_t
*wnd
, MGLDC
*dc
)
171 wxWindowMGL
*w
= (wxWindow
*) wnd
->userData
;
175 w
->HandlePaint(&ctx
);
177 // FIXME_MGL -- root window should be a regular window so that
178 // enter/leave and activate/deactivate events work correctly
181 static ibool
wxWindowMouseHandler(window_t
*wnd
, event_t
*e
)
183 wxWindowMGL
*win
= (wxWindowMGL
*)MGL_wmGetWindowUserData(wnd
);
184 wxPoint where
= win
->ScreenToClient(wxPoint(e
->where_x
, e
->where_y
));
186 wxEventType type
= wxEVT_NULL
;
188 event
.SetEventObject(win
);
189 event
.SetTimestamp(e
->when
);
192 event
.m_shiftDown
= e
->modifiers
& EVT_SHIFTKEY
;
193 event
.m_controlDown
= e
->modifiers
& EVT_CTRLSTATE
;
194 event
.m_altDown
= e
->modifiers
& EVT_LEFTALT
;
195 event
.m_metaDown
= e
->modifiers
& EVT_RIGHTALT
;
196 event
.m_leftDown
= e
->modifiers
& EVT_LEFTBUT
;
197 event
.m_middleDown
= e
->modifiers
& EVT_MIDDLEBUT
;
198 event
.m_rightDown
= e
->modifiers
& EVT_RIGHTBUT
;
203 if ( e
->message
& EVT_LEFTBMASK
)
204 type
= (e
->message
& EVT_DBLCLICK
) ?
205 wxEVT_LEFT_DCLICK
: wxEVT_LEFT_DOWN
;
206 else if ( e
->message
& EVT_MIDDLEBMASK
)
207 type
= (e
->message
& EVT_DBLCLICK
) ?
208 wxEVT_MIDDLE_DCLICK
: wxEVT_MIDDLE_DOWN
;
209 else if ( e
->message
& EVT_RIGHTBMASK
)
210 type
= (e
->message
& EVT_DBLCLICK
) ?
211 wxEVT_RIGHT_DCLICK
: wxEVT_RIGHT_DOWN
;
215 if ( e
->message
& EVT_LEFTBMASK
)
216 type
= wxEVT_LEFT_UP
;
217 else if ( e
->message
& EVT_MIDDLEBMASK
)
218 type
= wxEVT_MIDDLE_UP
;
219 else if ( e
->message
& EVT_RIGHTBMASK
)
220 type
= wxEVT_RIGHT_UP
;
224 if ( win
!= g_windowUnderMouse
)
226 if ( g_windowUnderMouse
)
228 wxMouseEvent
event2(event
);
229 wxPoint where2
= g_windowUnderMouse
->ScreenToClient(
230 wxPoint(e
->where_x
, e
->where_y
));
231 event2
.m_x
= where2
.x
;
232 event2
.m_y
= where2
.y
;
233 event2
.SetEventObject(g_windowUnderMouse
);
234 event2
.SetEventType(wxEVT_LEAVE_WINDOW
);
235 g_windowUnderMouse
->GetEventHandler()->ProcessEvent(event2
);
238 wxMouseEvent
event3(event
);
239 event3
.SetEventType(wxEVT_ENTER_WINDOW
);
240 win
->GetEventHandler()->ProcessEvent(event3
);
242 g_windowUnderMouse
= win
;
252 if ( type
== wxEVT_NULL
)
258 event
.SetEventType(type
);
259 return win
->GetEventHandler()->ProcessEvent(event
);
263 static ibool
wxWindowKeybHandler(window_t
*wnd
, event_t
*e
)
269 static ibool
wxWindowJoyHandler(window_t
*wnd
, event_t
*e
)
275 // ---------------------------------------------------------------------------
277 // ---------------------------------------------------------------------------
279 // in wxUniv this class is abstract because it doesn't have DoPopupMenu()
280 IMPLEMENT_ABSTRACT_CLASS(wxWindowMGL
, wxWindowBase
)
282 BEGIN_EVENT_TABLE(wxWindowMGL
, wxWindowBase
)
285 // ===========================================================================
287 // ===========================================================================
289 // ----------------------------------------------------------------------------
290 // constructors and such
291 // ----------------------------------------------------------------------------
293 void wxWindowMGL::Init()
301 m_isBeingDeleted
= FALSE
;
308 wxWindowMGL::~wxWindowMGL()
310 m_isBeingDeleted
= TRUE
;
312 if ( g_focusedWindow
== this )
314 if ( g_windowUnderMouse
== this )
315 g_windowUnderMouse
= NULL
;
317 // VS: destroy children first and _then_ detach *this from its parent.
318 // If we'd do it the other way around, children wouldn't be able
319 // find their parent frame (see above).
323 m_parent
->RemoveChild(this);
326 MGL_wmDestroyWindow(m_wnd
);
329 // real construction (Init() must have been called before!)
330 bool wxWindowMGL::Create(wxWindow
*parent
,
335 const wxString
& name
)
337 // FIXME_MGL -- temporary!
338 //wxCHECK_MSG( parent, FALSE, wxT("can't create wxWindow without parent") );
340 if ( !CreateBase(parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
) )
343 if ( parent
) // FIXME_MGL temporary
344 parent
->AddChild(this);
346 m_isShown
=FALSE
;// FIXME_MGL -- temporary, simulates wxTLW/wxFrame
348 if ( style
& wxPOPUP_WINDOW
)
350 // it is created hidden as other top level windows
355 x
= pos
.x
, y
= pos
.y
;
357 x
= 0; // FIXME_MGL, something better, see GTK+
359 y
= 0; // FIXME_MGL, something better, see GTK+
360 w
= WidthDefault(size
.x
);
361 h
= HeightDefault(size
.y
);
363 m_wnd
= MGL_wmCreateWindow(g_winMng
,
364 parent
? parent
->GetHandle() : NULL
,
367 MGL_wmSetWindowUserData(m_wnd
, (void*) this);
368 MGL_wmSetWindowPainter(m_wnd
, wxWindowPainter
);
369 MGL_wmShowWindow(m_wnd
, m_isShown
);
370 MGL_wmSetWindowCursor(m_wnd
, *wxSTANDARD_CURSOR
->GetMGLCursor());
372 MGL_wmPushWindowEventHandler(m_wnd
, wxWindowMouseHandler
, EVT_MOUSEEVT
, 0);
373 MGL_wmPushWindowEventHandler(m_wnd
, wxWindowKeybHandler
, EVT_KEYEVT
, 0);
374 MGL_wmPushWindowEventHandler(m_wnd
, wxWindowJoyHandler
, EVT_JOYEVT
, 0);
379 // ---------------------------------------------------------------------------
381 // ---------------------------------------------------------------------------
383 void wxWindowMGL::SetFocus()
386 g_focusedWindow
->KillFocus();
388 g_focusedWindow
= this;
390 MGL_wmCaptureEvents(GetHandle(), EVT_KEYEVT
|EVT_JOYEVT
, wxMGL_CAPTURE_KEYB
);
393 // caret needs to be informed about focus change
394 wxCaret
*caret
= GetCaret();
397 #endif // wxUSE_CARET
401 wxActivateEvent
event(wxEVT_ACTIVATE
, TRUE
, GetId());
402 event
.SetEventObject(this);
403 GetEventHandler()->ProcessEvent(event
);
406 wxFocusEvent
event(wxEVT_SET_FOCUS
, GetId());
407 event
.SetEventObject(this);
408 GetEventHandler()->ProcessEvent(event
);
411 void wxWindowMGL::KillFocus()
413 if ( g_focusedWindow
!= this ) return;
414 g_focusedWindow
= NULL
;
416 if ( m_isBeingDeleted
) return;
418 MGL_wmUncaptureEvents(GetHandle(), wxMGL_CAPTURE_KEYB
);
421 // caret needs to be informed about focus change
422 wxCaret
*caret
= GetCaret();
424 caret
->OnKillFocus();
425 #endif // wxUSE_CARET
429 wxActivateEvent
event(wxEVT_ACTIVATE
, FALSE
, GetId());
430 event
.SetEventObject(this);
431 GetEventHandler()->ProcessEvent(event
);
434 wxFocusEvent
event(wxEVT_KILL_FOCUS
, GetId());
435 event
.SetEventObject(this);
436 GetEventHandler()->ProcessEvent(event
);
439 // ----------------------------------------------------------------------------
440 // this wxWindowBase function is implemented here (in platform-specific file)
441 // because it is static and so couldn't be made virtual
442 // ----------------------------------------------------------------------------
443 wxWindow
*wxWindowBase::FindFocus()
445 return (wxWindow
*)g_focusedWindow
;
448 bool wxWindowMGL::Show(bool show
)
450 if ( !wxWindowBase::Show(show
) )
453 MGL_wmShowWindow(m_wnd
, show
);
457 // Raise the window to the top of the Z order
458 void wxWindowMGL::Raise()
460 MGL_wmRaiseWindow(m_wnd
);
463 // Lower the window to the bottom of the Z order
464 void wxWindowMGL::Lower()
466 MGL_wmLowerWindow(m_wnd
);
469 void wxWindowMGL::CaptureMouse()
471 MGL_wmCaptureEvents(m_wnd
, EVT_MOUSEEVT
, wxMGL_CAPTURE_MOUSE
);
474 void wxWindowMGL::ReleaseMouse()
476 MGL_wmUncaptureEvents(m_wnd
, wxMGL_CAPTURE_MOUSE
);
479 /* static */ wxWindow
*wxWindowBase::GetCapture()
481 for (captureentry_t
*c
= g_winMng
->capturedEvents
; c
; c
= c
->next
)
483 if ( c
->id
== wxMGL_CAPTURE_MOUSE
)
484 return (wxWindow
*)c
->wnd
->userData
;
489 bool wxWindowMGL::SetCursor(const wxCursor
& cursor
)
491 if ( !wxWindowBase::SetCursor(cursor
) )
498 MGL_wmSetWindowCursor(m_wnd
, *m_cursor
.GetMGLCursor());
500 MGL_wmSetWindowCursor(m_wnd
, *wxSTANDARD_CURSOR
->GetMGLCursor());
502 // FIXME_MGL -- should it set children's cursor or not?!
507 void wxWindowMGL::WarpPointer(int x
, int y
)
509 ClientToScreen(&x
, &y
);
510 EVT_setMousePos(x
, y
);
513 #if WXWIN_COMPATIBILITY
514 // If nothing defined for this, try the parent.
515 // E.g. we may be a button loaded from a resource, with no callback function
517 void wxWindowMGL::OnCommand(wxWindow
& win
, wxCommandEvent
& event
)
519 if ( GetEventHandler()->ProcessEvent(event
) )
522 m_parent
->GetEventHandler()->OnCommand(win
, event
);
524 #endif // WXWIN_COMPATIBILITY_2
526 #if WXWIN_COMPATIBILITY
527 wxObject
* wxWindowMGL::GetChild(int number
) const
529 // Return a pointer to the Nth object in the Panel
530 wxNode
*node
= GetChildren().First();
536 wxObject
*obj
= (wxObject
*)node
->Data();
542 #endif // WXWIN_COMPATIBILITY
544 // Set this window to be the child of 'parent'.
545 bool wxWindowMGL::Reparent(wxWindowBase
*parent
)
547 if ( !wxWindowBase::Reparent(parent
) )
550 MGL_wmReparentWindow(m_wnd
, parent
->GetHandle());
556 // ---------------------------------------------------------------------------
558 // ---------------------------------------------------------------------------
560 #if wxUSE_DRAG_AND_DROP
562 void wxWindowMGL::SetDropTarget(wxDropTarget
*pDropTarget
)
564 if ( m_dropTarget
!= 0 ) {
565 m_dropTarget
->Revoke(m_hWnd
);
569 m_dropTarget
= pDropTarget
;
570 if ( m_dropTarget
!= 0 )
571 m_dropTarget
->Register(m_hWnd
);
574 #endif // wxUSE_DRAG_AND_DROP
576 // old style file-manager drag&drop support: we retain the old-style
577 // DragAcceptFiles in parallel with SetDropTarget.
578 void wxWindowMGL::DragAcceptFiles(bool accept
)
581 HWND hWnd
= GetHwnd();
583 ::DragAcceptFiles(hWnd
, (BOOL
)accept
);
587 // ---------------------------------------------------------------------------
588 // moving and resizing
589 // ---------------------------------------------------------------------------
592 void wxWindowMGL::DoGetSize(int *x
, int *y
) const
594 if (x
) *x
= m_wnd
->width
;
595 if (y
) *y
= m_wnd
->height
;
598 void wxWindowMGL::DoGetPosition(int *x
, int *y
) const
600 if (x
) *x
= m_wnd
->x
;
601 if (y
) *y
= m_wnd
->y
;
604 void wxWindowMGL::DoScreenToClient(int *x
, int *y
) const
607 wxPoint co
= GetClientAreaOrigin();
609 MGL_wmCoordGlobalToLocal(m_wnd
, m_wnd
->x
, m_wnd
->y
, &ax
, &ay
);
618 void wxWindowMGL::DoClientToScreen(int *x
, int *y
) const
621 wxPoint co
= GetClientAreaOrigin();
623 MGL_wmCoordGlobalToLocal(m_wnd
, m_wnd
->x
+co
.x
, m_wnd
->y
+co
.y
, &ax
, &ay
);
630 // Get size *available for subwindows* i.e. excluding menu bar etc.
631 void wxWindowMGL::DoGetClientSize(int *x
, int *y
) const
636 void wxWindowMGL::DoMoveWindow(int x
, int y
, int width
, int height
)
638 MGL_wmSetWindowPosition(GetHandle(), x
, y
, width
, height
);
641 // set the size of the window: if the dimensions are positive, just use them,
642 // but if any of them is equal to -1, it means that we must find the value for
643 // it ourselves (unless sizeFlags contains wxSIZE_ALLOW_MINUS_ONE flag, in
644 // which case -1 is a valid value for x and y)
646 // If sizeFlags contains wxSIZE_AUTO_WIDTH/HEIGHT flags (default), we calculate
647 // the width/height to best suit our contents, otherwise we reuse the current
649 void wxWindowMGL::DoSetSize(int x
, int y
, int width
, int height
, int sizeFlags
)
651 // get the current size and position...
652 int currentX
, currentY
;
653 GetPosition(¤tX
, ¤tY
);
654 int currentW
,currentH
;
655 GetSize(¤tW
, ¤tH
);
657 // ... and don't do anything (avoiding flicker) if it's already ok
658 if ( x
== currentX
&& y
== currentY
&&
659 width
== currentW
&& height
== currentH
)
664 if ( x
== -1 && !(sizeFlags
& wxSIZE_ALLOW_MINUS_ONE
) )
666 if ( y
== -1 && !(sizeFlags
& wxSIZE_ALLOW_MINUS_ONE
) )
669 #if 0 // FIXME_MGL -- what's this good for?
670 AdjustForParentClientOrigin(x
, y
, sizeFlags
);
676 if ( sizeFlags
& wxSIZE_AUTO_WIDTH
)
678 size
= DoGetBestSize();
683 // just take the current one
690 if ( sizeFlags
& wxSIZE_AUTO_HEIGHT
)
694 size
= DoGetBestSize();
696 //else: already called DoGetBestSize() above
702 // just take the current one
707 DoMoveWindow(x
, y
, width
, height
);
709 wxSizeEvent
event(wxSize(width
, height
), GetId());
710 event
.SetEventObject(this);
711 GetEventHandler()->ProcessEvent(event
);
714 void wxWindowMGL::DoSetClientSize(int width
, int height
)
716 SetSize(width
, height
);
719 // ---------------------------------------------------------------------------
721 // ---------------------------------------------------------------------------
723 int wxWindowMGL::GetCharHeight() const
727 return dc
.GetCharHeight();
730 int wxWindowMGL::GetCharWidth() const
734 return dc
.GetCharWidth();
737 void wxWindowMGL::GetTextExtent(const wxString
& string
,
739 int *descent
, int *externalLeading
,
740 const wxFont
*theFont
) const
745 dc
.GetTextExtent(string
, x
, y
, descent
, externalLeading
, (wxFont
*)theFont
);
748 #if wxUSE_CARET && WXWIN_COMPATIBILITY
749 // ---------------------------------------------------------------------------
750 // Caret manipulation
751 // ---------------------------------------------------------------------------
753 void wxWindowMGL::CreateCaret(int w
, int h
)
755 SetCaret(new wxCaret(this, w
, h
));
758 void wxWindowMGL::CreateCaret(const wxBitmap
*WXUNUSED(bitmap
))
760 wxFAIL_MSG("not implemented");
763 void wxWindowMGL::ShowCaret(bool show
)
765 wxCHECK_RET( m_caret
, "no caret to show" );
770 void wxWindowMGL::DestroyCaret()
775 void wxWindowMGL::SetCaretPos(int x
, int y
)
777 wxCHECK_RET( m_caret
, "no caret to move" );
782 void wxWindowMGL::GetCaretPos(int *x
, int *y
) const
784 wxCHECK_RET( m_caret
, "no caret to get position of" );
786 m_caret
->GetPosition(x
, y
);
788 #endif // wxUSE_CARET
791 // ---------------------------------------------------------------------------
793 // ---------------------------------------------------------------------------
795 void wxWindowMGL::Clear()
797 wxClientDC
dc((wxWindow
*)this);
798 wxBrush
brush(GetBackgroundColour(), wxSOLID
);
799 dc
.SetBackground(brush
);
803 void wxWindowMGL::Refresh(bool WXUNUSED(eraseBack
), const wxRect
*rect
)
808 r
.left
= rect
->GetLeft(), r
.right
= rect
->GetRight();
809 r
.top
= rect
->GetTop(), r
.bottom
= rect
->GetBottom();
810 MGL_wmInvalidateWindowRect(GetHandle(), &r
);
813 MGL_wmInvalidateWindow(GetHandle());
816 void wxWindowMGL::Update()
819 MGL_wmUpdateDC(g_winMng
);
822 void wxWindowMGL::Freeze()
825 m_refreshAfterThaw
= FALSE
;
828 void wxWindowMGL::Thaw()
831 if ( m_refreshAfterThaw
)
835 void wxWindowMGL::HandlePaint(MGLDevCtx
*dc
)
839 // Don't paint anything if the window is frozen.
840 m_refreshAfterThaw
= TRUE
;
845 dc
->getClipRegion(clip
);
846 m_updateRegion
= wxRegion(clip
);
850 wxWindowDC
dc((wxWindow
*)this);
851 wxEraseEvent
eventEr(m_windowId
, &dc
);
852 eventEr
.SetEventObject(this);
853 GetEventHandler()->ProcessEvent(eventEr
);
856 wxNcPaintEvent
eventNc(GetId());
857 eventNc
.SetEventObject(this);
858 GetEventHandler()->ProcessEvent(eventNc
);
860 wxPaintEvent
eventPt(GetId());
861 eventPt
.SetEventObject(this);
862 GetEventHandler()->ProcessEvent(eventPt
);
865 m_updateRegion
.Clear();
869 // Find the wxWindow at the current mouse position, returning the mouse
871 wxWindow
* wxFindWindowAtPointer(wxPoint
& pt
)
873 return wxFindWindowAtPoint(pt
= wxGetMousePosition());
876 wxWindow
* wxFindWindowAtPoint(const wxPoint
& pt
)
878 window_t
*wnd
= MGL_wmGetWindowAtPosition(g_winMng
, pt
.x
, pt
.y
);
879 return (wxWindow
*)wnd
->userData
;