+ if ( ypos == lineNo )
+ line += content[n] ;
+ ++xpos ;
+ }
+ }
+ }
+ return line ;
+}
+
+int wxMacMLTEControl::GetLineLength(long lineNo) const
+{
+ Point curpt ;
+ if ( lineNo < GetNumberOfLines() )
+ {
+ // TODO find a better implementation : while we can get the
+ // line metrics of a certain line, we don't get its starting
+ // position, so it would probably be rather a binary search
+ // for the start position
+ long xpos = 0 ;
+ long ypos = 0 ;
+ int lastHeight = 0 ;
+ long lastpos = GetLastPosition() ;
+
+ ItemCount n ;
+ for ( n = 0 ; n <= (ItemCount) lastpos ; ++n )
+ {
+ TXNOffsetToPoint( m_txn , n , &curpt);
+
+ if ( curpt.v > lastHeight )
+ {
+ if ( ypos == lineNo )
+ return xpos ;
+
+ xpos = 0 ;
+ if ( n > 0 )
+ ++ypos ;
+ lastHeight = curpt.v ;
+ }
+ else
+ ++xpos ;
+ }
+ }
+ return 0 ;
+}
+
+
+// ----------------------------------------------------------------------------
+// MLTE control implementation (classic part)
+// ----------------------------------------------------------------------------
+
+// CS:TODO we still have a problem getting properly at the text events of a control because under Carbon
+// the MLTE engine registers itself for the key events thus the normal flow never occurs, the only measure for the
+// moment is to avoid setting the true focus on the control, the proper solution at the end would be to have
+// an alternate path for carbon key events that routes automatically into the same wx flow of events
+
+/* part codes */
+
+/* kmUPTextPart is the part code we return to indicate the user has clicked
+in the text area of our control */
+#define kmUPTextPart 1
+
+
+/* routines for using existing user pane controls.
+These routines are useful for cases where you would like to use an
+existing user pane control in, say, a dialog window as a scrolling
+text edit field.*/
+
+/* Utility Routines */
+
+/* kUserClickedToFocusPart is a part code we pass to the SetKeyboardFocus
+routine. In our focus switching routine this part code is understood
+as meaning 'the user has clicked in the control and we need to switch
+the current focus to ourselves before we can continue'. */
+#define kUserClickedToFocusPart 100
+
+/* STPTextPaneVars is a structure used for storing the the mUP Control's
+internal variables and state information. A handle to this record is
+stored in the pane control's reference value field using the
+SetControlReference routine. */
+
+class STPTextPaneVars {
+public :
+ /* OS records referenced */
+ TXNObject fTXNRec; /* the txn record */
+ TXNFrameID fTXNFrame; /* the txn frame ID */
+ ControlRef fUserPaneRec; /* handle to the user pane control */
+ WindowPtr fOwner; /* window containing control */
+ GrafPtr fDrawingEnvironment; /* grafport where control is drawn */
+ /* flags */
+ Boolean fInFocus; /* true while the focus rect is drawn around the control */
+ Boolean fIsActive; /* true while the control is drawn in the active state */
+ Boolean fTXNObjectActive; /* reflects the activation state of the text edit record */
+ Boolean fFocusDrawState; /* true if focus is drawn (default: true) */
+ /* calculated locations */
+ Rect fRBounds; /* control bounds */
+ Rect fRTextArea; /* area where the text is drawn */
+ Rect fRFocusOutline; /* rectangle used to draw the focus box */
+ Rect fRTextOutline; /* rectangle used to draw the border */
+ RgnHandle fRTextOutlineRegion; /* background region for the text, erased before calling TEUpdate */
+ /* our focus advance override routine */
+ EventHandlerUPP handlerUPP;
+ EventHandlerRef handlerRef;
+ bool fNoBorders ;
+ bool fMultiline ;
+ bool fVisible ;
+} ;
+
+/* Univerals Procedure Pointer variables used by the
+mUP Control. These variables are set up
+the first time that mUPOpenControl is called. */
+ControlUserPaneDrawUPP gTPDrawProc = NULL;
+ControlUserPaneHitTestUPP gTPHitProc = NULL;
+ControlUserPaneTrackingUPP gTPTrackProc = NULL;
+ControlUserPaneIdleUPP gTPIdleProc = NULL;
+ControlUserPaneKeyDownUPP gTPKeyProc = NULL;
+ControlUserPaneActivateUPP gTPActivateProc = NULL;
+ControlUserPaneFocusUPP gTPFocusProc = NULL;
+
+// one place for calculating all
+static void TPCalculateBounds(STPTextPaneVars *varsp, const Rect& bounds)
+{
+ SetRect(&varsp->fRBounds, bounds.left, bounds.top, bounds.right, bounds.bottom);
+ SetRect(&varsp->fRFocusOutline, bounds.left, bounds.top, bounds.right, bounds.bottom);
+ // eventually make TextOutline inset 1,1
+ SetRect(&varsp->fRTextOutline, bounds.left, bounds.top, bounds.right, bounds.bottom);
+ if ( !varsp->fNoBorders )
+ {
+ SetRect(&varsp->fRTextArea, bounds.left + 2 , bounds.top + (varsp->fMultiline ? 0 : 2) ,
+ bounds.right - (varsp->fMultiline ? 0 : 2), bounds.bottom - (varsp->fMultiline ? 0 : 2));
+ }
+ else
+ {
+ SetRect(&varsp->fRTextArea, bounds.left , bounds.top ,
+ bounds.right, bounds.bottom);
+ }
+}
+
+OSStatus MLTESetObjectVisibility( STPTextPaneVars *varsp, Boolean vis , long wxStyle)
+{
+ OSStatus err = noErr ;
+#if TARGET_API_MAC_OSX
+ TXNControlTag iControlTags[1] = { kTXNVisibilityTag };
+ TXNControlData iControlData[1] = {{ vis }};
+ err = ::TXNSetTXNObjectControls( varsp->fTXNRec, false, 1, iControlTags, iControlData );
+#endif
+ wxTextCtrl* textctrl = (wxTextCtrl*) GetControlReference(varsp->fUserPaneRec);
+ if ( vis && textctrl )
+ {
+ Rect bounds ;
+ UMAGetControlBoundsInWindowCoords( varsp->fUserPaneRec, &bounds);
+ TPCalculateBounds( varsp , bounds ) ;
+ wxMacWindowClipper cl(textctrl) ;
+ TXNSetFrameBounds( varsp->fTXNRec, varsp->fRTextArea.top, varsp->fRTextArea.left,
+ varsp->fRTextArea.bottom, varsp->fRTextArea.right, varsp->fTXNFrame);
+ TXNShowSelection( varsp->fTXNRec, kTXNShowStart);
+ }
+ return err ;
+}
+
+// make sure we don't miss changes as carbon events are not available for these under classic
+static void TPUpdateVisibility(ControlRef theControl) {
+ wxTextCtrl* textctrl = (wxTextCtrl*) GetControlReference(theControl);
+ if ( textctrl == NULL )
+ return ;
+
+ STPTextPaneVars *varsp = (STPTextPaneVars *) ((wxMacMLTEClassicControl*)textctrl->GetPeer())->m_macTXNvars ;
+
+ Rect bounds ;
+ UMAGetControlBoundsInWindowCoords(theControl, &bounds);
+ if ( textctrl->MacIsReallyShown() != varsp->fVisible )
+ {
+ // invalidate old position
+ // InvalWindowRect( GetControlOwner( theControl ) , &varsp->fRBounds ) ;
+ varsp->fVisible = textctrl->MacIsReallyShown() ;
+ }
+ if ( !EqualRect( &bounds , &varsp->fRBounds ) )
+ {
+ // old position
+ Rect oldBounds = varsp->fRBounds ;
+ TPCalculateBounds( varsp , bounds ) ;
+ // we only recalculate when visible, otherwise scrollbars get drawn at incorrect places
+ if ( varsp->fVisible )
+ {
+ wxMacWindowClipper cl(textctrl) ;
+ TXNSetFrameBounds( varsp->fTXNRec, varsp->fRTextArea.top, varsp->fRTextArea.left,
+ varsp->fRTextArea.bottom, varsp->fRTextArea.right, varsp->fTXNFrame);
+ }
+ InvalWindowRect( GetControlOwner( theControl ) , &oldBounds ) ;
+ InvalWindowRect( GetControlOwner( theControl ) , &varsp->fRBounds ) ;
+ }
+}
+
+// make correct activations
+static void TPActivatePaneText(STPTextPaneVars *varsp, Boolean setActive) {
+
+ wxTextCtrl* textctrl = (wxTextCtrl*) GetControlReference(varsp->fUserPaneRec);
+ if (varsp->fTXNObjectActive != setActive && textctrl->MacIsReallyShown() )
+ {
+ varsp->fTXNObjectActive = setActive;
+ TXNActivate(varsp->fTXNRec, varsp->fTXNFrame, varsp->fTXNObjectActive);
+ if (varsp->fInFocus)
+ TXNFocus( varsp->fTXNRec, varsp->fTXNObjectActive);
+ }
+}
+
+// update focus outlines
+static void TPRedrawFocusOutline(STPTextPaneVars *varsp) {
+
+ /* state changed */
+ if (varsp->fFocusDrawState != (varsp->fIsActive && varsp->fInFocus))
+ {
+ varsp->fFocusDrawState = (varsp->fIsActive && varsp->fInFocus);
+ DrawThemeFocusRect(&varsp->fRFocusOutline, varsp->fFocusDrawState);
+ }
+}
+
+// update TXN focus state
+static void TPFocusPaneText(STPTextPaneVars *varsp, Boolean setFocus) {
+ wxTextCtrl* textctrl = (wxTextCtrl*) GetControlReference(varsp->fUserPaneRec);
+
+ if (varsp->fInFocus != setFocus && textctrl->MacIsReallyShown()) {
+ varsp->fInFocus = setFocus;
+ TXNFocus( varsp->fTXNRec, varsp->fInFocus);
+ }
+}
+
+// draw the control
+static pascal void TPPaneDrawProc(ControlRef theControl, ControlPartCode thePart) {
+ /* set up our globals */
+
+ wxTextCtrl* textctrl = (wxTextCtrl*) GetControlReference(theControl);
+ if ( textctrl == NULL )
+ return ;
+ TPUpdateVisibility( theControl ) ;
+
+ STPTextPaneVars *varsp = (STPTextPaneVars *) ((wxMacMLTEClassicControl*)textctrl->GetPeer())->m_macTXNvars ;
+ if ( textctrl->MacIsReallyShown() )
+ {
+ wxMacWindowClipper clipper( textctrl ) ;
+ TXNDraw(varsp->fTXNRec, NULL);
+ if ( !varsp->fNoBorders )
+ DrawThemeEditTextFrame(&varsp->fRTextOutline, varsp->fIsActive ? kThemeStateActive: kThemeStateInactive);
+ TPRedrawFocusOutline( varsp ) ;
+ }
+
+}
+
+
+/* TPPaneHitTestProc is called when the control manager would
+like to determine what part of the control the mouse resides over.
+We also call this routine from our tracking proc to determine how
+to handle mouse clicks. */
+static pascal ControlPartCode TPPaneHitTestProc(ControlRef theControl, Point where) {
+ ControlPartCode result;
+ /* set up our locals and lock down our globals*/
+ result = 0;
+ wxTextCtrl* textctrl = (wxTextCtrl*) GetControlReference(theControl);
+ if ( textctrl == NULL )
+ return 0 ;
+ TPUpdateVisibility( theControl ) ;
+ STPTextPaneVars *varsp = (STPTextPaneVars *) ((wxMacMLTEClassicControl*)textctrl->GetPeer())->m_macTXNvars ;
+ if (textctrl->MacIsReallyShown() )
+ {
+ if (PtInRect(where, &varsp->fRBounds))
+ result = kmUPTextPart;
+ else
+ {
+ // sometimes we get the coords also in control local coordinates, therefore test again
+ if ( textctrl->MacGetTopLevelWindow()->MacUsesCompositing() )
+ {
+ int x = 0 , y = 0 ;
+ textctrl->MacClientToRootWindow( &x , &y ) ;
+ where.h += x ;
+ where.v += y ;
+ }
+ if (PtInRect(where, &varsp->fRBounds))
+ result = kmUPTextPart;
+ else
+ result = 0;
+ }
+ }
+ return result;
+}
+
+
+
+
+
+/* TPPaneTrackingProc is called when the mouse is being held down
+over our control. This routine handles clicks in the text area
+and in the scroll bar. */
+static pascal ControlPartCode TPPaneTrackingProc(ControlRef theControl, Point startPt, ControlActionUPP actionProc) {
+
+ ControlPartCode partCodeResult;
+ /* make sure we have some variables... */
+ partCodeResult = 0;
+ wxTextCtrl* textctrl = (wxTextCtrl*) GetControlReference(theControl);
+ if ( textctrl == NULL )
+ return 0;
+ TPUpdateVisibility( theControl ) ;
+ STPTextPaneVars *varsp = (STPTextPaneVars *) ((wxMacMLTEClassicControl*)textctrl->GetPeer())->m_macTXNvars ;
+ if (textctrl->MacIsReallyShown() )
+ {
+ /* we don't do any of these functions unless we're in focus */
+ if ( ! varsp->fInFocus) {
+ WindowPtr owner;
+ owner = GetControlOwner(theControl);
+ ClearKeyboardFocus(owner);
+ SetKeyboardFocus(owner, theControl, kUserClickedToFocusPart);
+ }
+ /* find the location for the click */
+ // for compositing, we must convert these into toplevel window coordinates, because hittesting expects them
+ if ( textctrl->MacGetTopLevelWindow()->MacUsesCompositing() )
+ {
+ int x = 0 , y = 0 ;
+ textctrl->MacClientToRootWindow( &x , &y ) ;
+ startPt.h += x ;
+ startPt.v += y ;
+ }
+
+ switch (TPPaneHitTestProc(theControl, startPt))
+ {
+
+ /* handle clicks in the text part */
+ case kmUPTextPart:
+ {
+ wxMacWindowClipper clipper( textctrl ) ;
+
+ EventRecord rec ;
+ ConvertEventRefToEventRecord( (EventRef) wxTheApp->MacGetCurrentEvent() , &rec ) ;
+ TXNClick( varsp->fTXNRec, &rec );
+
+ }
+ break;
+
+ }
+ }
+ return partCodeResult;
+}
+
+
+/* TPPaneIdleProc is our user pane idle routine. When our text field
+is active and in focus, we use this routine to set the cursor. */
+static pascal void TPPaneIdleProc(ControlRef theControl) {
+ /* set up locals */
+ wxTextCtrl* textctrl = (wxTextCtrl*) GetControlReference(theControl);
+ if ( textctrl == NULL )
+ return ;
+ TPUpdateVisibility( theControl ) ;
+ STPTextPaneVars *varsp = (STPTextPaneVars *) ((wxMacMLTEClassicControl*)textctrl->GetPeer())->m_macTXNvars ;
+ if (textctrl->MacIsReallyShown()) {
+ /* if we're not active, then we have nothing to say about the cursor */
+ if (varsp->fIsActive) {
+ Rect bounds;
+ Point mousep;
+
+ wxMacWindowClipper clipper( textctrl ) ;
+ GetMouse(&mousep);
+ /* there's a 'focus thing' and an 'unfocused thing' */
+ if (varsp->fInFocus) {
+ /* flash the cursor */
+ SetPort(varsp->fDrawingEnvironment);
+ TXNIdle(varsp->fTXNRec);
+ /* set the cursor */
+ if (PtInRect(mousep, &varsp->fRTextArea)) {
+ RgnHandle theRgn;
+ RectRgn((theRgn = NewRgn()), &varsp->fRTextArea);
+ TXNAdjustCursor(varsp->fTXNRec, theRgn);
+ DisposeRgn(theRgn);