1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: mac/toplevel.cpp
3 // Purpose: implements wxTopLevelWindow for MSW
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 2001 SciTech Software, Inc. (www.scitechsoft.com)
9 // License: wxWindows license
10 ///////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
21 #pragma implementation "toplevel.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
33 #include "wx/toplevel.h"
35 #include "wx/string.h"
40 #include "wx/mac/uma.h"
41 #include "wx/mac/aga.h"
42 #include "wx/tooltip.h"
45 #define wxMAC_DEBUG_REDRAW 0
46 #ifndef wxMAC_DEBUG_REDRAW
47 #define wxMAC_DEBUG_REDRAW 0
50 // ----------------------------------------------------------------------------
52 // ----------------------------------------------------------------------------
54 // list of all frames and modeless dialogs
55 wxWindowList wxModelessWindows
;
57 // double click testing
58 static Point gs_lastWhere
;
59 static long gs_lastWhen
= 0;
62 extern int wxBusyCursorCount
;
65 // ============================================================================
66 // wxTopLevelWindowMac implementation
67 // ============================================================================
69 // ---------------------------------------------------------------------------
70 // wxWindowMac utility functions
71 // ---------------------------------------------------------------------------
73 // Find an item given the Macintosh Window Reference
75 wxList
*wxWinMacWindowList
= NULL
;
76 wxTopLevelWindowMac
*wxFindWinFromMacWindow(WXWindow inWindowRef
)
78 wxNode
*node
= wxWinMacWindowList
->Find((long)inWindowRef
);
81 return (wxTopLevelWindowMac
*)node
->Data();
84 void wxAssociateWinWithMacWindow(WXWindow inWindowRef
, wxTopLevelWindowMac
*win
)
86 // adding NULL WindowRef is (first) surely a result of an error and
87 // (secondly) breaks menu command processing
88 wxCHECK_RET( inWindowRef
!= (WindowRef
) NULL
, "attempt to add a NULL WindowRef to window list" );
90 if ( !wxWinMacWindowList
->Find((long)inWindowRef
) )
91 wxWinMacWindowList
->Append((long)inWindowRef
, win
);
94 void wxRemoveMacWindowAssociation(wxTopLevelWindowMac
*win
)
96 wxWinMacWindowList
->DeleteObject(win
);
100 // ----------------------------------------------------------------------------
101 // wxTopLevelWindowMac creation
102 // ----------------------------------------------------------------------------
104 WXHWND
wxTopLevelWindowMac::s_macWindowInUpdate
= NULL
;
106 void wxTopLevelWindowMac::Init()
109 m_maximizeOnShow
= FALSE
;
110 m_macNoEraseUpdateRgn
= NewRgn() ;
111 m_macNeedsErasing
= false ;
115 bool wxTopLevelWindowMac::Create(wxWindow
*parent
,
117 const wxString
& title
,
121 const wxString
& name
)
126 m_windowStyle
= style
;
130 m_windowId
= id
== -1 ? NewControlId() : id
;
132 wxTopLevelWindows
.Append(this);
135 parent
->AddChild(this);
140 wxTopLevelWindowMac::~wxTopLevelWindowMac()
144 wxToolTip::NotifyWindowDelete(m_macWindow
) ;
145 UMADisposeWindow( (WindowRef
) m_macWindow
) ;
148 wxRemoveMacWindowAssociation( this ) ;
150 wxTopLevelWindows
.DeleteObject(this);
152 if ( wxModelessWindows
.Find(this) )
153 wxModelessWindows
.DeleteObject(this);
155 // If this is the last top-level window, exit.
156 if ( wxTheApp
&& (wxTopLevelWindows
.Number() == 0) )
158 wxTheApp
->SetTopWindow(NULL
);
160 if ( wxTheApp
->GetExitOnFrameDelete() )
162 wxTheApp
->ExitMainLoop() ;
165 DisposeRgn( (RgnHandle
) m_macNoEraseUpdateRgn
) ;
169 // ----------------------------------------------------------------------------
170 // wxTopLevelWindowMac maximize/minimize
171 // ----------------------------------------------------------------------------
173 void wxTopLevelWindowMac::Maximize(bool maximize
)
175 // not available on mac
178 bool wxTopLevelWindowMac::IsMaximized() const
183 void wxTopLevelWindowMac::Iconize(bool iconize
)
185 // not available on mac
188 bool wxTopLevelWindowMac::IsIconized() const
190 // mac dialogs cannot be iconized
194 void wxTopLevelWindowMac::Restore()
196 // not available on mac
199 // ----------------------------------------------------------------------------
200 // wxTopLevelWindowMac misc
201 // ----------------------------------------------------------------------------
203 void wxTopLevelWindowMac::SetIcon(const wxIcon
& icon
)
206 wxTopLevelWindowBase::SetIcon(icon
);
209 void wxTopLevelWindowMac::MacCreateRealWindow( const wxString
& title
,
213 const wxString
& name
)
216 m_windowStyle
= style
;
237 ::SetRect(&theBoundsRect
, m_x
, m_y
, m_x
+ m_width
, m_y
+ m_height
);
239 // translate the window attributes in the appropriate window class and attributes
241 WindowClass wclass
= 0;
242 WindowAttributes attr
= kWindowNoAttributes
;
244 if ( HasFlag( wxFRAME_TOOL_WINDOW
) /*|| HasFlag(wxTINY_CAPTION_HORIZ) || HasFlag(wxTINY_CAPTION_VERT)*/ )
246 wclass
= kFloatingWindowClass
;
247 if ( HasFlag(wxTINY_CAPTION_VERT
) )
249 attr
|= kWindowSideTitlebarAttribute
;
252 else if ( HasFlag( wxCAPTION
) )
254 if ( HasFlag( wxDIALOG_MODAL
) )
256 wclass
= kDocumentWindowClass
; // kMovableModalWindowClass ;
260 wclass
= kDocumentWindowClass
;
265 wclass
= kDocumentWindowClass
;
268 if ( HasFlag( wxMINIMIZE_BOX
) || HasFlag( wxMAXIMIZE_BOX
) )
270 attr
|= kWindowFullZoomAttribute
;
271 attr
|= kWindowCollapseBoxAttribute
;
273 if ( HasFlag( wxRESIZE_BORDER
) )
275 attr
|= kWindowResizableAttribute
;
277 if ( HasFlag( wxSYSTEM_MENU
) )
279 attr
|= kWindowCloseBoxAttribute
;
282 ::CreateNewWindow( wclass
, attr
, &theBoundsRect
, (WindowRef
*)&m_macWindow
) ;
283 wxAssociateWinWithMacWindow( m_macWindow
, this ) ;
285 if( wxApp::s_macDefaultEncodingIsPC
)
286 label
= wxMacMakeMacStringFromPC( title
) ;
289 UMASetWTitleC( (WindowRef
)m_macWindow
, label
) ;
290 ::CreateRootControl( (WindowRef
)m_macWindow
, (ControlHandle
*)&m_macRootControl
) ;
295 void wxTopLevelWindowMac::MacGetPortParams(WXPOINTPTR localOrigin
, WXRECTPTR clipRect
, WXHWND
*window
, wxWindowMac
** rootwin
)
297 ((Point
*)localOrigin
)->h
= 0;
298 ((Point
*)localOrigin
)->v
= 0;
299 ((Rect
*)clipRect
)->left
= 0;
300 ((Rect
*)clipRect
)->top
= 0;
301 ((Rect
*)clipRect
)->right
= m_width
;
302 ((Rect
*)clipRect
)->bottom
= m_height
;
303 *window
= m_macWindow
;
307 void wxTopLevelWindowMac::Clear()
312 WXWidget
wxTopLevelWindowMac::MacGetContainerForEmbedding()
314 return m_macRootControl
;
318 void wxTopLevelWindowMac::MacUpdate( long timestamp
)
321 wxMacPortStateHelper
help( (GrafPtr
) GetWindowPort( (WindowRef
) m_macWindow
) ) ;
323 BeginUpdate( (WindowRef
)m_macWindow
) ;
325 RgnHandle updateRgn
= NewRgn();
326 RgnHandle diffRgn
= NewRgn() ;
327 if ( updateRgn
&& diffRgn
)
329 GetPortVisibleRegion( GetWindowPort( (WindowRef
)m_macWindow
), updateRgn
);
330 DiffRgn( updateRgn
, (RgnHandle
) m_macNoEraseUpdateRgn
, diffRgn
) ;
331 if ( !EmptyRgn( updateRgn
) )
333 MacRedraw( updateRgn
, timestamp
, m_macNeedsErasing
|| !EmptyRgn( diffRgn
) ) ;
337 DisposeRgn( updateRgn
);
339 DisposeRgn( diffRgn
);
340 EndUpdate( (WindowRef
)m_macWindow
) ;
341 SetEmptyRgn( (RgnHandle
) m_macNoEraseUpdateRgn
) ;
342 m_macNeedsErasing
= false ;
346 // Raise the window to the top of the Z order
347 void wxTopLevelWindowMac::Raise()
349 ::BringToFront( (WindowRef
)m_macWindow
) ;
352 // Lower the window to the bottom of the Z order
353 void wxTopLevelWindowMac::Lower()
355 ::SendBehind( (WindowRef
)m_macWindow
, NULL
) ;
358 void wxTopLevelWindowMac::MacFireMouseEvent( WXEVENTREF evr
)
360 EventRecord
*ev
= (EventRecord
*) evr
;
361 wxMouseEvent
event(wxEVT_LEFT_DOWN
);
362 bool isDown
= !(ev
->modifiers
& btnState
) ; // 1 is for up
363 bool controlDown
= ev
->modifiers
& controlKey
; // for simulating right mouse
365 event
.m_leftDown
= isDown
&& !controlDown
;
367 event
.m_middleDown
= FALSE
;
368 event
.m_rightDown
= isDown
&& controlDown
;
370 if ( ev
->what
== mouseDown
)
373 event
.SetEventType(wxEVT_RIGHT_DOWN
) ;
375 event
.SetEventType(wxEVT_LEFT_DOWN
) ;
377 else if ( ev
->what
== mouseUp
)
380 event
.SetEventType(wxEVT_RIGHT_UP
) ;
382 event
.SetEventType(wxEVT_LEFT_UP
) ;
386 event
.SetEventType(wxEVT_MOTION
) ;
389 event
.m_shiftDown
= ev
->modifiers
& shiftKey
;
390 event
.m_controlDown
= ev
->modifiers
& controlKey
;
391 event
.m_altDown
= ev
->modifiers
& optionKey
;
392 event
.m_metaDown
= ev
->modifiers
& cmdKey
;
394 Point localwhere
= ev
->where
;
398 ::SetPort( UMAGetWindowPort( (WindowRef
)m_macWindow
) ) ;
399 ::GlobalToLocal( &localwhere
) ;
402 if ( ev
->what
== mouseDown
)
404 if ( ev
->when
- gs_lastWhen
<= GetDblTime() )
406 if ( abs( localwhere
.h
- gs_lastWhere
.h
) < 3 && abs( localwhere
.v
- gs_lastWhere
.v
) < 3 )
408 // This is not right if the second mouse down
409 // event occured in a differen window. We
410 // correct this in MacDispatchMouseEvent.
412 event
.SetEventType(wxEVT_RIGHT_DCLICK
) ;
414 event
.SetEventType(wxEVT_LEFT_DCLICK
) ;
420 gs_lastWhen
= ev
->when
;
422 gs_lastWhere
= localwhere
;
425 event
.m_x
= localwhere
.h
;
426 event
.m_y
= localwhere
.v
;
430 event
.m_timeStamp
= ev
->when
;
431 event
.SetEventObject(this);
432 if ( wxTheApp
->s_captureWindow
)
436 wxTheApp
->s_captureWindow
->ScreenToClient( &x
, &y
) ;
439 event
.SetEventObject( wxTheApp
->s_captureWindow
) ;
440 wxTheApp
->s_captureWindow
->GetEventHandler()->ProcessEvent( event
) ;
442 if ( ev
->what
== mouseUp
)
444 wxTheApp
->s_captureWindow
= NULL
;
445 if ( wxBusyCursorCount
== 0 )
447 m_cursor
.MacInstall() ;
453 MacDispatchMouseEvent( event
) ;
457 void wxTopLevelWindowMac::MacMouseDown( WXEVENTREF ev
, short part
)
459 MacFireMouseEvent( ev
) ;
462 void wxTopLevelWindowMac::MacMouseUp( WXEVENTREF ev
, short part
)
468 MacFireMouseEvent( ev
) ;
474 void wxTopLevelWindowMac::MacMouseMoved( WXEVENTREF ev
, short part
)
480 MacFireMouseEvent( ev
) ;
485 void wxTopLevelWindowMac::MacActivate( WXEVENTREF ev
, bool inIsActivating
)
487 wxActivateEvent
event(wxEVT_ACTIVATE
, inIsActivating
, m_windowId
);
488 event
.m_timeStamp
= ((EventRecord
*)ev
)->when
;
489 event
.SetEventObject(this);
491 GetEventHandler()->ProcessEvent(event
);
493 UMAHighlightAndActivateWindow( (WindowRef
)m_macWindow
, inIsActivating
) ;
495 MacSuperEnabled( inIsActivating
) ;
498 void wxTopLevelWindowMac::MacKeyDown( WXEVENTREF ev
)
502 void wxTopLevelWindowMac::SetTitle(const wxString
& title
)
504 wxWindow::SetTitle( title
) ;
508 if( wxApp::s_macDefaultEncodingIsPC
)
509 label
= wxMacMakeMacStringFromPC( m_label
) ;
513 UMASetWTitleC( (WindowRef
)m_macWindow
, label
) ;
516 bool wxTopLevelWindowMac::Show(bool show
)
518 if ( !wxWindow::Show(show
) )
523 ::ShowWindow( (WindowRef
)m_macWindow
) ;
524 ::SelectWindow( (WindowRef
)m_macWindow
) ;
525 // no need to generate events here, they will get them triggered by macos
526 // actually they should be , but apparently they are not
527 wxSize
size(m_width
, m_height
);
528 wxSizeEvent
event(size
, m_windowId
);
529 event
.SetEventObject(this);
530 GetEventHandler()->ProcessEvent(event
);
534 ::HideWindow( (WindowRef
)m_macWindow
) ;
548 void wxTopLevelWindowMac::DoMoveWindow(int x
, int y
, int width
, int height
)
552 int former_w
= m_width
;
553 int former_h
= m_height
;
555 int actualWidth
= width
;
556 int actualHeight
= height
;
560 if ((m_minWidth
!= -1) && (actualWidth
< m_minWidth
))
561 actualWidth
= m_minWidth
;
562 if ((m_minHeight
!= -1) && (actualHeight
< m_minHeight
))
563 actualHeight
= m_minHeight
;
564 if ((m_maxWidth
!= -1) && (actualWidth
> m_maxWidth
))
565 actualWidth
= m_maxWidth
;
566 if ((m_maxHeight
!= -1) && (actualHeight
> m_maxHeight
))
567 actualHeight
= m_maxHeight
;
569 bool doMove
= false ;
570 bool doResize
= false ;
572 if ( actualX
!= former_x
|| actualY
!= former_y
)
576 if ( actualWidth
!= former_w
|| actualHeight
!= former_h
)
581 if ( doMove
|| doResize
)
585 m_width
= actualWidth
;
586 m_height
= actualHeight
;
589 ::MoveWindow((WindowRef
)m_macWindow
, m_x
, m_y
, false); // don't make frontmost
592 ::SizeWindow((WindowRef
)m_macWindow
, m_width
, m_height
, true);
594 // the OS takes care of invalidating and erasing the new area
595 // we have erased the old one
597 if ( IsKindOf( CLASSINFO( wxFrame
) ) )
599 wxFrame
* frame
= (wxFrame
*) this ;
600 frame
->PositionStatusBar();
601 frame
->PositionToolBar();
604 wxWindowMac::MacTopLevelWindowChangedPosition() ; // like this only children will be notified
606 MacRepositionScrollBars() ;
609 wxPoint
point(m_x
, m_y
);
610 wxMoveEvent
event(point
, m_windowId
);
611 event
.SetEventObject(this);
612 GetEventHandler()->ProcessEvent(event
) ;
616 MacRepositionScrollBars() ;
617 wxSize
size(m_width
, m_height
);
618 wxSizeEvent
event(size
, m_windowId
);
619 event
.SetEventObject(this);
620 GetEventHandler()->ProcessEvent(event
);
627 * Invalidation Mechanism
629 * The update mechanism reflects exactely the windows mechanism
630 * the rect gets added to the window invalidate region, if the eraseBackground flag
631 * has been true for any part of the update rgn the background is erased in the entire region
632 * not just in the specified rect.
634 * In order to achive this, we also have an internal m_macNoEraseUpdateRgn, all rects that have
635 * the eraseBackground flag set to false are also added to this rgn. upon receiving an update event
636 * the update rgn is compared to the m_macNoEraseUpdateRgn and in case they differ, every window
637 * will get the eraseBackground event first
640 void wxTopLevelWindowMac::MacInvalidate( const WXRECTPTR rect
, bool eraseBackground
)
643 GetPort( &formerPort
) ;
644 SetPortWindowPort( (WindowRef
)m_macWindow
) ;
646 m_macNeedsErasing
|= eraseBackground
;
648 // if we already know that we will have to erase, there's no need to track the rest
649 if ( !m_macNeedsErasing
)
651 // we end only here if eraseBackground is false
652 // if we already have a difference between m_macNoEraseUpdateRgn and UpdateRgn
653 // we will have to erase anyway
655 RgnHandle updateRgn
= NewRgn();
656 RgnHandle diffRgn
= NewRgn() ;
657 if ( updateRgn
&& diffRgn
)
659 GetWindowUpdateRgn( (WindowRef
)m_macWindow
, updateRgn
);
661 LocalToGlobal( &pt
) ;
662 OffsetRgn( updateRgn
, -pt
.h
, -pt
.v
) ;
663 DiffRgn( updateRgn
, (RgnHandle
) m_macNoEraseUpdateRgn
, diffRgn
) ;
664 if ( !EmptyRgn( diffRgn
) )
666 m_macNeedsErasing
= true ;
670 DisposeRgn( updateRgn
);
672 DisposeRgn( diffRgn
);
674 if ( !m_macNeedsErasing
)
676 RgnHandle rectRgn
= NewRgn() ;
677 SetRectRgn( rectRgn
, ((Rect
*)rect
)->left
, ((Rect
*)rect
)->top
, ((Rect
*)rect
)->right
, ((Rect
*)rect
)->bottom
) ;
678 UnionRgn( (RgnHandle
) m_macNoEraseUpdateRgn
, rectRgn
, (RgnHandle
) m_macNoEraseUpdateRgn
) ;
679 DisposeRgn( rectRgn
) ;
682 InvalWindowRect( (WindowRef
)m_macWindow
, (Rect
*)rect
) ;
683 // turn this on to debug the refreshing cycle
684 #if wxMAC_DEBUG_REDRAW
687 SetPort( formerPort
) ;