1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/osx/carbon/textctrl.cpp
3 // Purpose:     wxTextCtrl
4 // Author:      Stefan Csomor
5 // Modified by: Ryan Norton (MLTE GetLineLength and GetLineText)
6 // Created:     1998-01-01
7 // Copyright:   (c) Stefan Csomor
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 #include "wx/wxprec.h"
12 
13 #if wxUSE_TEXTCTRL
14 
15 #include "wx/textctrl.h"
16 
17 #ifndef WX_PRECOMP
18     #include "wx/intl.h"
19     #include "wx/app.h"
20     #include "wx/utils.h"
21     #include "wx/dc.h"
22     #include "wx/button.h"
23     #include "wx/menu.h"
24     #include "wx/settings.h"
25     #include "wx/msgdlg.h"
26     #include "wx/toplevel.h"
27 #endif
28 
29 #ifdef __DARWIN__
30     #include <sys/types.h>
31     #include <sys/stat.h>
32 #else
33     #include <stat.h>
34 #endif
35 
36 #if wxUSE_STD_IOSTREAM
37     #if wxUSE_IOSTREAMH
38         #include <fstream.h>
39     #else
40         #include <fstream>
41     #endif
42 #endif
43 
44 #include "wx/filefn.h"
45 #include "wx/sysopt.h"
46 #include "wx/thread.h"
47 
48 #include "wx/osx/private.h"
49 #include "wx/osx/carbon/private/mactext.h"
50 
51 class wxMacFunctor
52 {
53 public :
wxMacFunctor()54     wxMacFunctor() {}
~wxMacFunctor()55     virtual ~wxMacFunctor() {}
56 
57     virtual void* operator()() = 0 ;
58 
CallBackProc(void * param)59     static void* CallBackProc( void *param )
60     {
61         wxMacFunctor* f = (wxMacFunctor*) param ;
62         void *result = (*f)() ;
63         return result ;
64     }
65 } ;
66 
67 template<typename classtype, typename param1type>
68 
69 class wxMacObjectFunctor1 : public wxMacFunctor
70 {
71     typedef void (classtype::*function)( param1type p1 ) ;
72     typedef void (classtype::*ref_function)( const param1type& p1 ) ;
73 public :
wxMacObjectFunctor1(classtype * obj,function f,param1type p1)74     wxMacObjectFunctor1( classtype *obj , function f , param1type p1 ) :
75         wxMacFunctor()
76     {
77         m_object = obj ;
78         m_function = f ;
79         m_param1 = p1 ;
80     }
81 
wxMacObjectFunctor1(classtype * obj,ref_function f,param1type p1)82     wxMacObjectFunctor1( classtype *obj , ref_function f , param1type p1 ) :
83         wxMacFunctor()
84     {
85         m_object = obj ;
86         m_refFunction = f ;
87         m_param1 = p1 ;
88     }
89 
~wxMacObjectFunctor1()90     virtual ~wxMacObjectFunctor1() {}
91 
operator ()()92     virtual void* operator()()
93     {
94         (m_object->*m_function)( m_param1 ) ;
95         return NULL ;
96     }
97 
98 private :
99     classtype* m_object ;
100     param1type m_param1 ;
101     union
102     {
103         function m_function ;
104         ref_function m_refFunction ;
105     } ;
106 } ;
107 
108 template<typename classtype, typename param1type>
wxMacMPRemoteCall(classtype * object,void (classtype::* function)(param1type p1),param1type p1)109 void* wxMacMPRemoteCall( classtype *object , void (classtype::*function)( param1type p1 ) , param1type p1 )
110 {
111     wxMacObjectFunctor1<classtype, param1type> params(object, function, p1) ;
112     void *result =
113         MPRemoteCall( wxMacFunctor::CallBackProc , &params , kMPOwningProcessRemoteContext ) ;
114     return result ;
115 }
116 
117 template<typename classtype, typename param1type>
wxMacMPRemoteCall(classtype * object,void (classtype::* function)(const param1type & p1),param1type p1)118 void* wxMacMPRemoteCall( classtype *object , void (classtype::*function)( const param1type& p1 ) , param1type p1 )
119 {
120     wxMacObjectFunctor1<classtype,param1type> params(object, function, p1) ;
121     void *result =
122         MPRemoteCall( wxMacFunctor::CallBackProc , &params , kMPOwningProcessRemoteContext ) ;
123     return result ;
124 }
125 
126 template<typename classtype, typename param1type>
wxMacMPRemoteGUICall(classtype * object,void (classtype::* function)(param1type p1),param1type p1)127 void* wxMacMPRemoteGUICall( classtype *object , void (classtype::*function)( param1type p1 ) , param1type p1 )
128 {
129     wxMutexGuiLeave() ;
130     void *result = wxMacMPRemoteCall( object , function , p1 ) ;
131     wxMutexGuiEnter() ;
132     return result ;
133 }
134 
135 template<typename classtype, typename param1type>
wxMacMPRemoteGUICall(classtype * object,void (classtype::* function)(const param1type & p1),param1type p1)136 void* wxMacMPRemoteGUICall( classtype *object , void (classtype::*function)( const param1type& p1 ) , param1type p1 )
137 {
138     wxMutexGuiLeave() ;
139     void *result = wxMacMPRemoteCall( object , function , p1 ) ;
140     wxMutexGuiEnter() ;
141     return result ;
142 }
143 
144 class WXDLLEXPORT wxMacPortSaver
145 {
146     wxDECLARE_NO_COPY_CLASS(wxMacPortSaver);
147 
148 public:
149     wxMacPortSaver( GrafPtr port );
150     ~wxMacPortSaver();
151 private :
152     GrafPtr m_port;
153 };
154 
155 
156 /*
157  Clips to the visible region of a control within the current port
158  */
159 
160 class WXDLLEXPORT wxMacWindowClipper : public wxMacPortSaver
161 {
162     wxDECLARE_NO_COPY_CLASS(wxMacWindowClipper);
163 
164 public:
165     wxMacWindowClipper( const wxWindow* win );
166     ~wxMacWindowClipper();
167 private:
168     GrafPtr   m_newPort;
169     RgnHandle m_formerClip;
170     RgnHandle m_newClip;
171 };
172 
wxMacPortSaver(GrafPtr port)173 wxMacPortSaver::wxMacPortSaver( GrafPtr port )
174 {
175     ::GetPort( &m_port );
176     ::SetPort( port );
177 }
178 
~wxMacPortSaver()179 wxMacPortSaver::~wxMacPortSaver()
180 {
181     ::SetPort( m_port );
182 }
183 
wxMacWindowClipper(const wxWindow * win)184 wxMacWindowClipper::wxMacWindowClipper( const wxWindow* win ) :
185 wxMacPortSaver( (GrafPtr) GetWindowPort( (WindowRef) win->MacGetTopLevelWindowRef() ) )
186 {
187     m_newPort = (GrafPtr) GetWindowPort( (WindowRef) win->MacGetTopLevelWindowRef() ) ;
188     m_formerClip = NewRgn() ;
189     m_newClip = NewRgn() ;
190     GetClip( m_formerClip ) ;
191 
192     if ( win )
193     {
194         // guard against half constructed objects, this just leads to a empty clip
195         if ( win->GetPeer() )
196         {
197             int x = 0 , y = 0;
198             win->MacWindowToRootWindow( &x, &y ) ;
199 
200             // get area including focus rect
201             HIShapeGetAsQDRgn( ((wxWindow*)win)->MacGetVisibleRegion(true).GetWXHRGN() , m_newClip );
202             if ( !EmptyRgn( m_newClip ) )
203                 OffsetRgn( m_newClip , x , y ) ;
204         }
205 
206         SetClip( m_newClip ) ;
207     }
208 }
209 
~wxMacWindowClipper()210 wxMacWindowClipper::~wxMacWindowClipper()
211 {
212     SetPort( m_newPort ) ;
213     SetClip( m_formerClip ) ;
214     DisposeRgn( m_newClip ) ;
215     DisposeRgn( m_formerClip ) ;
216 }
217 
218 // common parts for implementations based on MLTE
219 
220 class wxMacMLTEControl : public wxMacControl, public wxTextWidgetImpl
221 {
222 public :
223     wxMacMLTEControl( wxTextCtrl *peer ) ;
~wxMacMLTEControl()224     ~wxMacMLTEControl() {}
225 
CanFocus() const226     virtual bool        CanFocus() const
227                         { return true; }
228 
229     virtual wxString GetStringValue() const ;
230     virtual void SetStringValue( const wxString &str ) ;
231 
232     static TXNFrameOptions FrameOptionsFromWXStyle( long wxStyle ) ;
233 
234     void AdjustCreationAttributes( const wxColour& background, bool visible ) ;
235 
236     virtual void SetFont( const wxFont & font, const wxColour& foreground, long windowStyle, bool ignoreBlack ) ;
237     virtual void SetBackgroundColour(const wxColour& col );
238     virtual void SetStyle( long start, long end, const wxTextAttr& style ) ;
239     virtual void Copy() ;
240     virtual void Cut() ;
241     virtual void Paste() ;
242     virtual bool CanPaste() const ;
243     virtual void SetEditable( bool editable ) ;
244     virtual long GetLastPosition() const ;
245     virtual void Replace( long from, long to, const wxString &str ) ;
246     virtual void Remove( long from, long to ) ;
247     virtual void GetSelection( long* from, long* to ) const ;
248     virtual void SetSelection( long from, long to ) ;
249 
250     virtual void WriteText( const wxString& str ) ;
251 
HasOwnContextMenu() const252     virtual bool HasOwnContextMenu() const
253     {
254         TXNCommandEventSupportOptions options ;
255         TXNGetCommandEventSupport( m_txn , & options ) ;
256         return options & kTXNSupportEditCommandProcessing ;
257     }
258 
CheckSpelling(bool check)259     virtual void CheckSpelling(bool check)
260     {
261         TXNSetSpellCheckAsYouType( m_txn, (Boolean) check );
262     }
263     virtual void Clear() ;
264 
265     virtual bool CanUndo() const ;
266     virtual void Undo() ;
267     virtual bool CanRedo()  const;
268     virtual void Redo() ;
269     virtual int GetNumberOfLines() const ;
270     virtual long XYToPosition(long x, long y) const ;
271     virtual bool PositionToXY(long pos, long *x, long *y) const ;
272     virtual void ShowPosition( long pos ) ;
273     virtual int GetLineLength(long lineNo) const ;
274     virtual wxString GetLineText(long lineNo) const ;
275 
276     void SetTXNData( const wxString& st , TXNOffset start , TXNOffset end ) ;
GetTXNObject()277     TXNObject GetTXNObject() { return m_txn ; }
278 
279 protected :
280     void TXNSetAttribute( const wxTextAttr& style , long from , long to ) ;
281 
282     TXNObject m_txn ;
283 } ;
284 
285 // implementation available under OSX
286 
287 class wxMacMLTEHIViewControl : public wxMacMLTEControl
288 {
289 public :
290     wxMacMLTEHIViewControl( wxTextCtrl *wxPeer,
291                              const wxString& str,
292                              const wxPoint& pos,
293                              const wxSize& size, long style ) ;
294     virtual ~wxMacMLTEHIViewControl() ;
295 
296     virtual bool SetFocus() ;
297     virtual bool HasFocus() const ;
298     virtual void SetBackgroundColour(const wxColour& col ) ;
299 
300 protected :
301     HIViewRef m_scrollView ;
302     HIViewRef m_textView ;
303 };
304 
305 // 'classic' MLTE implementation
306 
307 class wxMacMLTEClassicControl : public wxMacMLTEControl
308 {
309 public :
310     wxMacMLTEClassicControl( wxTextCtrl *wxPeer,
311                              const wxString& str,
312                              const wxPoint& pos,
313                              const wxSize& size, long style ) ;
314     virtual ~wxMacMLTEClassicControl() ;
315 
316     virtual void VisibilityChanged(bool shown) ;
317     virtual void SuperChangedPosition() ;
318 
319     virtual void            MacControlUserPaneDrawProc(wxInt16 part) ;
320     virtual wxInt16         MacControlUserPaneHitTestProc(wxInt16 x, wxInt16 y) ;
321     virtual wxInt16         MacControlUserPaneTrackingProc(wxInt16 x, wxInt16 y, void* actionProc) ;
322     virtual void            MacControlUserPaneIdleProc() ;
323     virtual wxInt16         MacControlUserPaneKeyDownProc(wxInt16 keyCode, wxInt16 charCode, wxInt16 modifiers) ;
324     virtual void            MacControlUserPaneActivateProc(bool activating) ;
325     virtual wxInt16         MacControlUserPaneFocusProc(wxInt16 action) ;
326     virtual void            MacControlUserPaneBackgroundProc(void* info) ;
327 
SetupCursor(const wxPoint & WXUNUSED (pt))328     virtual bool SetupCursor( const wxPoint& WXUNUSED(pt) )
329     {
330         MacControlUserPaneIdleProc();
331         return true;
332     }
333 
334     virtual void            Move(int x, int y, int width, int height);
335 
336 protected :
337     OSStatus                 DoCreate();
338 
339     void                    MacUpdatePosition() ;
340     void                    MacActivatePaneText(bool setActive) ;
341     void                    MacFocusPaneText(bool setFocus) ;
342     void                    MacSetObjectVisibility(bool vis) ;
343 
344 private :
345     TXNFrameID              m_txnFrameID ;
346     GrafPtr                 m_txnPort ;
347     WindowRef               m_txnWindow ;
348     // bounds of the control as we last did set the txn frames
349     Rect                    m_txnControlBounds ;
350     Rect                    m_txnVisBounds ;
351 
352     static pascal void TXNScrollActionProc( ControlRef controlRef , ControlPartCode partCode ) ;
353     static pascal void TXNScrollInfoProc(
354         SInt32 iValue, SInt32 iMaximumValue,
355         TXNScrollBarOrientation iScrollBarOrientation, SInt32 iRefCon ) ;
356 
357     ControlRef              m_sbHorizontal ;
358     SInt32                  m_lastHorizontalValue ;
359     ControlRef              m_sbVertical ;
360     SInt32                  m_lastVerticalValue ;
361 };
362 
CreateTextControl(wxTextCtrl * wxpeer,wxWindowMac * WXUNUSED (parent),wxWindowID WXUNUSED (id),const wxString & str,const wxPoint & pos,const wxSize & size,long style,long WXUNUSED (extraStyle))363 wxWidgetImplType* wxWidgetImpl::CreateTextControl( wxTextCtrl* wxpeer,
364                                     wxWindowMac* WXUNUSED(parent),
365                                     wxWindowID WXUNUSED(id),
366                                     const wxString& str,
367                                     const wxPoint& pos,
368                                     const wxSize& size,
369                                     long style,
370                                     long WXUNUSED(extraStyle))
371 {
372     return new wxMacMLTEHIViewControl( wxpeer , str , pos , size , style ) ;
373 }
374 
375 // ----------------------------------------------------------------------------
376 // standard unicode control implementation
377 // ----------------------------------------------------------------------------
378 
379 // the current unicode textcontrol implementation has a bug : only if the control
380 // is currently having the focus, the selection can be retrieved by the corresponding
381 // data tag. So we have a mirroring using a member variable
382 // TODO : build event table using virtual member functions for wxMacControl
383 
384 static const EventTypeSpec unicodeTextControlEventList[] =
385 {
386     { kEventClassControl , kEventControlSetFocusPart } ,
387 } ;
388 
wxMacUnicodeTextControlControlEventHandler(EventHandlerCallRef handler,EventRef event,void * data)389 static pascal OSStatus wxMacUnicodeTextControlControlEventHandler( EventHandlerCallRef handler , EventRef event , void *data )
390 {
391     OSStatus result = eventNotHandledErr ;
392     wxMacUnicodeTextControl* focus = (wxMacUnicodeTextControl*) data ;
393     wxMacCarbonEvent cEvent( event ) ;
394 
395     switch ( GetEventKind( event ) )
396     {
397         case kEventControlSetFocusPart :
398         {
399             ControlPartCode controlPart = cEvent.GetParameter<ControlPartCode>(kEventParamControlPart , typeControlPartCode );
400             if ( controlPart == kControlFocusNoPart )
401             {
402                 // about to lose focus -> store selection to field
403                 focus->GetData<ControlEditTextSelectionRec>( 0, kControlEditTextSelectionTag, &focus->m_selection );
404             }
405             result = CallNextEventHandler(handler,event) ;
406             if ( controlPart != kControlFocusNoPart )
407             {
408                 // about to gain focus -> set selection from field
409                 focus->SetData<ControlEditTextSelectionRec>( 0, kControlEditTextSelectionTag, &focus->m_selection );
410             }
411             break;
412         }
413         default:
414             break ;
415     }
416 
417     return result ;
418 }
419 
wxMacUnicodeTextControlEventHandler(EventHandlerCallRef handler,EventRef event,void * data)420 static pascal OSStatus wxMacUnicodeTextControlEventHandler( EventHandlerCallRef handler , EventRef event , void *data )
421 {
422     OSStatus result = eventNotHandledErr ;
423 
424     switch ( GetEventClass( event ) )
425     {
426         case kEventClassControl :
427             result = wxMacUnicodeTextControlControlEventHandler( handler , event , data ) ;
428             break ;
429 
430         default :
431             break ;
432     }
433     return result ;
434 }
435 
DEFINE_ONE_SHOT_HANDLER_GETTER(wxMacUnicodeTextControlEventHandler)436 DEFINE_ONE_SHOT_HANDLER_GETTER( wxMacUnicodeTextControlEventHandler )
437 
438 wxMacUnicodeTextControl::wxMacUnicodeTextControl( wxTextCtrl *wxPeer )
439     : wxMacControl( wxPeer ),
440       wxTextWidgetImpl( wxPeer )
441 {
442 }
443 
wxMacUnicodeTextControl(wxTextCtrl * wxPeer,const wxString & str,const wxPoint & pos,const wxSize & size,long style)444 wxMacUnicodeTextControl::wxMacUnicodeTextControl( wxTextCtrl *wxPeer,
445     const wxString& str,
446     const wxPoint& pos,
447     const wxSize& size, long style )
448     : wxMacControl( wxPeer ),
449       wxTextWidgetImpl( wxPeer )
450 {
451     m_font = wxPeer->GetFont() ;
452     m_windowStyle = style ;
453     m_selection.selStart = m_selection.selEnd = 0;
454     Rect bounds = wxMacGetBoundsForControl( wxPeer , pos , size ) ;
455     wxString st = str ;
456     wxMacConvertNewlines10To13( &st ) ;
457     wxCFStringRef cf(st , m_font.GetEncoding()) ;
458 
459     m_valueTag = kControlEditTextCFStringTag ;
460     Boolean isPassword = ( m_windowStyle & wxTE_PASSWORD ) != 0 ;
461     if ( isPassword )
462     {
463         m_valueTag = kControlEditTextPasswordCFStringTag ;
464     }
465     OSStatus err = CreateEditUnicodeTextControl(
466         MAC_WXHWND(wxPeer->MacGetTopLevelWindowRef()), &bounds , cf ,
467         isPassword , NULL , &m_controlRef ) ;
468     verify_noerr( err );
469 
470     if ( !(m_windowStyle & wxTE_MULTILINE) )
471         SetData<Boolean>( kControlEditTextPart , kControlEditTextSingleLineTag , true ) ;
472 
473     InstallEventHandlers();
474 }
475 
InstallEventHandlers()476 void wxMacUnicodeTextControl::InstallEventHandlers()
477 {
478     ::InstallControlEventHandler( m_controlRef , GetwxMacUnicodeTextControlEventHandlerUPP(),
479                                 GetEventTypeCount(unicodeTextControlEventList), unicodeTextControlEventList, this,
480                                 (EventHandlerRef*) &m_macTextCtrlEventHandler);
481 }
482 
~wxMacUnicodeTextControl()483 wxMacUnicodeTextControl::~wxMacUnicodeTextControl()
484 {
485     ::RemoveEventHandler((EventHandlerRef) m_macTextCtrlEventHandler);
486 }
487 
VisibilityChanged(bool shown)488 void wxMacUnicodeTextControl::VisibilityChanged(bool shown)
489 {
490     if ( !(m_windowStyle & wxTE_MULTILINE) && shown )
491     {
492         // work around a refresh issue insofar as not always the entire content is shown,
493         // even if this would be possible
494         ControlEditTextSelectionRec sel ;
495         CFStringRef value = NULL ;
496 
497         verify_noerr( GetData<ControlEditTextSelectionRec>( 0, kControlEditTextSelectionTag, &sel ) );
498         verify_noerr( GetData<CFStringRef>( 0, m_valueTag, &value ) );
499         verify_noerr( SetData<CFStringRef>( 0, m_valueTag, &value ) );
500         verify_noerr( SetData<ControlEditTextSelectionRec>( 0, kControlEditTextSelectionTag, &sel ) );
501 
502         CFRelease( value ) ;
503     }
504 }
505 
GetStringValue() const506 wxString wxMacUnicodeTextControl::GetStringValue() const
507 {
508     wxString result ;
509     CFStringRef value = GetData<CFStringRef>(0, m_valueTag) ;
510     if ( value )
511     {
512         wxCFStringRef cf(value) ;
513         result = cf.AsString() ;
514     }
515 
516 #if '\n' == 10
517     wxMacConvertNewlines13To10( &result ) ;
518 #else
519     wxMacConvertNewlines10To13( &result ) ;
520 #endif
521 
522     return result ;
523 }
524 
SetStringValue(const wxString & str)525 void wxMacUnicodeTextControl::SetStringValue( const wxString &str )
526 {
527     wxString st = str ;
528     wxMacConvertNewlines10To13( &st ) ;
529     wxCFStringRef cf( st , m_font.GetEncoding() ) ;
530     verify_noerr( SetData<CFStringRef>( 0, m_valueTag , cf ) ) ;
531 }
532 
Copy()533 void wxMacUnicodeTextControl::Copy()
534 {
535     SendHICommand( kHICommandCopy ) ;
536 }
537 
Cut()538 void wxMacUnicodeTextControl::Cut()
539 {
540     SendHICommand( kHICommandCut ) ;
541 }
542 
Paste()543 void wxMacUnicodeTextControl::Paste()
544 {
545     SendHICommand( kHICommandPaste ) ;
546 }
547 
CanPaste() const548 bool wxMacUnicodeTextControl::CanPaste() const
549 {
550     return true ;
551 }
552 
SetEditable(bool WXUNUSED (editable))553 void wxMacUnicodeTextControl::SetEditable(bool WXUNUSED(editable))
554 {
555 #if 0 // leads to problem because text cannot be selected anymore
556     SetData<Boolean>( kControlEditTextPart , kControlEditTextLockedTag , (Boolean) !editable ) ;
557 #endif
558 }
559 
GetSelection(long * from,long * to) const560 void wxMacUnicodeTextControl::GetSelection( long* from, long* to ) const
561 {
562     ControlEditTextSelectionRec sel ;
563     if (HasFocus())
564         verify_noerr( GetData<ControlEditTextSelectionRec>( 0, kControlEditTextSelectionTag, &sel ) ) ;
565     else
566         sel = m_selection ;
567 
568     if ( from )
569         *from = sel.selStart ;
570     if ( to )
571         *to = sel.selEnd ;
572 }
573 
SetSelection(long from,long to)574 void wxMacUnicodeTextControl::SetSelection( long from , long to )
575 {
576     ControlEditTextSelectionRec sel ;
577     wxString result ;
578     int textLength = 0 ;
579     CFStringRef value = GetData<CFStringRef>(0, m_valueTag) ;
580     if ( value )
581     {
582         wxCFStringRef cf(value) ;
583         textLength = cf.AsString().length() ;
584     }
585 
586     if ((from == -1) && (to == -1))
587     {
588         from = 0 ;
589         to = textLength ;
590     }
591     else
592     {
593         from = wxMin(textLength,wxMax(from,0)) ;
594         if ( to == -1 )
595             to = textLength;
596         else
597             to = wxMax(0,wxMin(textLength,to)) ;
598     }
599 
600     sel.selStart = from ;
601     sel.selEnd = to ;
602     if ( HasFocus() )
603         SetData<ControlEditTextSelectionRec>( 0, kControlEditTextSelectionTag, &sel ) ;
604     else
605         m_selection = sel;
606 }
607 
WriteText(const wxString & str)608 void wxMacUnicodeTextControl::WriteText( const wxString& str )
609 {
610     // TODO: this MPRemoting will be moved into a remoting peer proxy for any command
611     if ( !wxIsMainThread() )
612     {
613 #if wxOSX_USE_CARBON
614         // unfortunately CW 8 is not able to correctly deduce the template types,
615         // so we have to instantiate explicitly
616         wxMacMPRemoteGUICall<wxTextCtrl,wxString>( (wxTextCtrl*) GetWXPeer() , &wxTextCtrl::WriteText , str ) ;
617 #endif
618         return ;
619     }
620 
621     wxString st = str ;
622     wxMacConvertNewlines10To13( &st ) ;
623 
624     if ( HasFocus() )
625     {
626         wxCFStringRef cf(st , m_font.GetEncoding() ) ;
627         CFStringRef value = cf ;
628         SetData<CFStringRef>( 0, kControlEditTextInsertCFStringRefTag, &value );
629     }
630     else
631     {
632         wxString val = GetStringValue() ;
633         long start , end ;
634         GetSelection( &start , &end ) ;
635         val.Remove( start , end - start ) ;
636         val.insert( start , str ) ;
637         SetStringValue( val ) ;
638         SetSelection( start + str.length() , start + str.length() ) ;
639     }
640 }
641 
642 // ----------------------------------------------------------------------------
643 // MLTE control implementation (common part)
644 // ----------------------------------------------------------------------------
645 
646 // if MTLE is read only, no changes at all are allowed, not even from
647 // procedural API, in order to allow changes via API all the same we must undo
648 // the readonly status while we are executing, this class helps to do so
649 
650 class wxMacEditHelper
651 {
652 public :
wxMacEditHelper(TXNObject txn)653     wxMacEditHelper( TXNObject txn )
654     {
655         TXNControlTag tag[] = { kTXNIOPrivilegesTag } ;
656         m_txn = txn ;
657         TXNGetTXNObjectControls( m_txn , 1 , tag , m_data ) ;
658         if ( m_data[0].uValue == kTXNReadOnly )
659         {
660             TXNControlData data[] = { { kTXNReadWrite } } ;
661             TXNSetTXNObjectControls( m_txn , false , 1 , tag , data ) ;
662         }
663     }
664 
~wxMacEditHelper()665     ~wxMacEditHelper()
666     {
667         TXNControlTag tag[] = { kTXNIOPrivilegesTag } ;
668         if ( m_data[0].uValue == kTXNReadOnly )
669             TXNSetTXNObjectControls( m_txn , false , 1 , tag , m_data ) ;
670     }
671 
672 protected :
673     TXNObject m_txn ;
674     TXNControlData m_data[1] ;
675 } ;
676 
wxMacMLTEControl(wxTextCtrl * peer)677 wxMacMLTEControl::wxMacMLTEControl( wxTextCtrl *peer )
678     : wxMacControl( peer ),
679       wxTextWidgetImpl( peer )
680 {
681     SetNeedsFocusRect( true ) ;
682 }
683 
GetStringValue() const684 wxString wxMacMLTEControl::GetStringValue() const
685 {
686     wxString result ;
687     OSStatus err ;
688     Size actualSize = 0;
689 
690     {
691 #if wxUSE_UNICODE
692         Handle theText ;
693         err = TXNGetDataEncoded( m_txn, kTXNStartOffset, kTXNEndOffset, &theText, kTXNUnicodeTextData );
694 
695         // all done
696         if ( err != noErr )
697         {
698             actualSize = 0 ;
699         }
700         else
701         {
702             actualSize = GetHandleSize( theText ) / sizeof(UniChar) ;
703             if ( actualSize > 0 )
704             {
705                 wxChar *ptr = NULL ;
706 
707                 SetHandleSize( theText, (actualSize + 1) * sizeof(UniChar) ) ;
708                 HLock( theText ) ;
709                 (((UniChar*)*theText)[actualSize]) = 0 ;
710                 wxMBConvUTF16 converter ;
711                 size_t noChars = converter.MB2WC( NULL , (const char*)*theText , 0 ) ;
712                 wxASSERT_MSG( noChars != wxCONV_FAILED, wxT("Unable to count the number of characters in this string!") );
713                 ptr = new wxChar[noChars + 1] ;
714 
715                 noChars = converter.MB2WC( ptr , (const char*)*theText , noChars + 1 ) ;
716                 wxASSERT_MSG( noChars != wxCONV_FAILED, wxT("Conversion of string failed!") );
717                 ptr[noChars] = 0 ;
718                 HUnlock( theText ) ;
719 
720                 ptr[actualSize] = 0 ;
721                 result = wxString( ptr ) ;
722                 delete [] ptr ;
723             }
724 
725             DisposeHandle( theText ) ;
726         }
727 #else // !wxUSE_UNICODE
728         Handle theText ;
729         err = TXNGetDataEncoded( m_txn , kTXNStartOffset, kTXNEndOffset, &theText, kTXNTextData );
730 
731         // all done
732         if ( err != noErr )
733         {
734             actualSize = 0 ;
735         }
736         else
737         {
738             actualSize = GetHandleSize( theText ) ;
739             if ( actualSize > 0 )
740             {
741                 HLock( theText ) ;
742                 result = wxString( *theText , wxConvLocal , actualSize ) ;
743                 HUnlock( theText ) ;
744             }
745 
746             DisposeHandle( theText ) ;
747         }
748 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
749     }
750 
751 #if '\n' == 10
752     wxMacConvertNewlines13To10( &result ) ;
753 #else
754     wxMacConvertNewlines10To13( &result ) ;
755 #endif
756 
757     return result ;
758 }
759 
SetStringValue(const wxString & str)760 void wxMacMLTEControl::SetStringValue( const wxString &str )
761 {
762     wxString st = str;
763     wxMacConvertNewlines10To13( &st );
764 
765     {
766 #ifndef __LP64__
767         wxMacWindowClipper c( GetWXPeer() ) ;
768 #endif
769 
770         {
771             wxMacEditHelper help( m_txn );
772             SetTXNData( st, kTXNStartOffset, kTXNEndOffset );
773         }
774 
775         TXNSetSelection( m_txn, 0, 0 );
776         TXNShowSelection( m_txn, kTXNShowStart );
777     }
778 }
779 
FrameOptionsFromWXStyle(long wxStyle)780 TXNFrameOptions wxMacMLTEControl::FrameOptionsFromWXStyle( long wxStyle )
781 {
782     TXNFrameOptions frameOptions = kTXNDontDrawCaretWhenInactiveMask;
783 
784     frameOptions |= kTXNDoFontSubstitutionMask;
785 
786     if ( ! (wxStyle & wxTE_NOHIDESEL) )
787         frameOptions |= kTXNDontDrawSelectionWhenInactiveMask ;
788 
789     if ( wxStyle & (wxHSCROLL | wxTE_DONTWRAP) )
790         frameOptions |= kTXNWantHScrollBarMask ;
791 
792     if ( wxStyle & wxTE_MULTILINE )
793     {
794         if ( ! (wxStyle & wxTE_DONTWRAP ) )
795             frameOptions |= kTXNAlwaysWrapAtViewEdgeMask ;
796 
797         if ( !(wxStyle & wxTE_NO_VSCROLL) )
798         {
799             frameOptions |= kTXNWantVScrollBarMask ;
800 
801             // The following code causes drawing problems on 10.4. Perhaps it can be restored for
802             // older versions of the OS, but I'm not sure it's appropriate to put a grow icon here
803             // anyways, as AFAIK users can't actually use it to resize the text ctrl.
804 //            if ( frameOptions & kTXNWantHScrollBarMask )
805 //                frameOptions |= kTXNDrawGrowIconMask ;
806         }
807     }
808     else
809     {
810         frameOptions |= kTXNSingleLineOnlyMask ;
811     }
812 
813     return frameOptions ;
814 }
815 
AdjustCreationAttributes(const wxColour & background,bool WXUNUSED (visible))816 void wxMacMLTEControl::AdjustCreationAttributes(const wxColour &background,
817                                                 bool WXUNUSED(visible))
818 {
819     TXNControlTag iControlTags[] =
820         {
821             kTXNDoFontSubstitution,
822             kTXNWordWrapStateTag ,
823         };
824     TXNControlData iControlData[] =
825         {
826             { true },
827             { kTXNNoAutoWrap },
828         };
829 
830     int toptag = WXSIZEOF( iControlTags ) ;
831 
832     if ( m_windowStyle & wxTE_MULTILINE )
833     {
834         iControlData[1].uValue =
835             (m_windowStyle & wxTE_DONTWRAP)
836             ? kTXNNoAutoWrap
837             : kTXNAutoWrap;
838     }
839 
840     OSStatus err = TXNSetTXNObjectControls( m_txn, false, toptag, iControlTags, iControlData ) ;
841     verify_noerr( err );
842 
843     // setting the default font:
844     // under 10.2 this causes a visible caret, therefore we avoid it
845 
846     Str255 fontName ;
847     SInt16 fontSize ;
848     Style fontStyle ;
849 
850     GetThemeFont( kThemeSystemFont , GetApplicationScript() , fontName , &fontSize , &fontStyle ) ;
851 
852     TXNTypeAttributes typeAttr[] =
853     {
854         { kTXNQDFontNameAttribute , kTXNQDFontNameAttributeSize , { (void*) fontName } } ,
855         { kTXNQDFontSizeAttribute , kTXNFontSizeAttributeSize , { (void*) (fontSize << 16) } } ,
856         { kTXNQDFontStyleAttribute , kTXNQDFontStyleAttributeSize , { (void*) normal } } ,
857     } ;
858 
859     err = TXNSetTypeAttributes(
860         m_txn, WXSIZEOF(typeAttr),
861         typeAttr, kTXNStartOffset, kTXNEndOffset );
862     verify_noerr( err );
863 
864     if ( m_windowStyle & wxTE_PASSWORD )
865     {
866         UniChar c = 0x00A5 ;
867         err = TXNEchoMode( m_txn , c , 0 , true );
868         verify_noerr( err );
869     }
870 
871     TXNBackground tback;
872     tback.bgType = kTXNBackgroundTypeRGB;
873     background.GetRGBColor( &tback.bg.color );
874     TXNSetBackground( m_txn , &tback );
875 
876 
877     TXNCommandEventSupportOptions options ;
878     if ( TXNGetCommandEventSupport( m_txn, &options ) == noErr )
879     {
880         options |=
881             kTXNSupportEditCommandProcessing
882             | kTXNSupportEditCommandUpdating
883             | kTXNSupportFontCommandProcessing
884             | kTXNSupportFontCommandUpdating;
885 
886         // only spell check when not read-only
887         // use system options for the default
888         bool checkSpelling = false ;
889         if ( !(m_windowStyle & wxTE_READONLY) )
890         {
891 #if wxUSE_SYSTEM_OPTIONS
892             if ( wxSystemOptions::HasOption( wxMAC_TEXTCONTROL_USE_SPELL_CHECKER ) && (wxSystemOptions::GetOptionInt( wxMAC_TEXTCONTROL_USE_SPELL_CHECKER ) == 1) )
893             {
894                 checkSpelling = true ;
895             }
896 #endif
897         }
898 
899         if ( checkSpelling )
900             options |=
901                 kTXNSupportSpellCheckCommandProcessing
902                 | kTXNSupportSpellCheckCommandUpdating;
903 
904         TXNSetCommandEventSupport( m_txn , options ) ;
905     }
906 }
907 
SetBackgroundColour(const wxColour & col)908 void wxMacMLTEControl::SetBackgroundColour(const wxColour& col )
909 {
910     TXNBackground tback;
911     tback.bgType = kTXNBackgroundTypeRGB;
912     col.GetRGBColor(&tback.bg.color);
913     TXNSetBackground( m_txn , &tback );
914 }
915 
wxConvertToTXN(int x)916 static inline int wxConvertToTXN(int x)
917 {
918     return static_cast<int>(x / 254.0 * 72 + 0.5);
919 }
920 
TXNSetAttribute(const wxTextAttr & style,long from,long to)921 void wxMacMLTEControl::TXNSetAttribute( const wxTextAttr& style , long from , long to )
922 {
923     TXNTypeAttributes typeAttr[4] ;
924     RGBColor color ;
925     size_t typeAttrCount = 0 ;
926 
927     TXNMargins margins;
928     TXNControlTag    controlTags[4];
929     TXNControlData   controlData[4];
930     size_t controlAttrCount = 0;
931 
932     TXNTab* tabs = NULL;
933 
934     bool relayout = false;
935     wxFont font ;
936 
937     if ( style.HasFont() )
938     {
939         wxASSERT( typeAttrCount < WXSIZEOF(typeAttr) );
940         font = style.GetFont() ;
941         typeAttr[typeAttrCount].tag = kTXNATSUIStyle ;
942         typeAttr[typeAttrCount].size = kTXNATSUIStyleSize ;
943         typeAttr[typeAttrCount].data.dataPtr = font.MacGetATSUStyle() ;
944         typeAttrCount++ ;
945     }
946 
947     if ( style.HasTextColour() )
948     {
949         wxASSERT( typeAttrCount < WXSIZEOF(typeAttr) );
950         style.GetTextColour().GetRGBColor( &color );
951         typeAttr[typeAttrCount].tag = kTXNQDFontColorAttribute ;
952         typeAttr[typeAttrCount].size = kTXNQDFontColorAttributeSize ;
953         typeAttr[typeAttrCount].data.dataPtr = (void*) &color ;
954         typeAttrCount++ ;
955     }
956 
957     if ( style.HasAlignment() )
958     {
959         wxASSERT( controlAttrCount < WXSIZEOF(controlTags) );
960         SInt32 align;
961 
962         switch ( style.GetAlignment() )
963         {
964             case wxTEXT_ALIGNMENT_LEFT:
965                 align = kTXNFlushLeft;
966                 break;
967             case wxTEXT_ALIGNMENT_CENTRE:
968                 align = kTXNCenter;
969                 break;
970             case wxTEXT_ALIGNMENT_RIGHT:
971                 align = kTXNFlushRight;
972                 break;
973             case wxTEXT_ALIGNMENT_JUSTIFIED:
974                 align = kTXNFullJust;
975                 break;
976             default :
977             case wxTEXT_ALIGNMENT_DEFAULT:
978                 align = kTXNFlushDefault;
979                 break;
980         }
981 
982         controlTags[controlAttrCount] = kTXNJustificationTag ;
983         controlData[controlAttrCount].sValue = align ;
984         controlAttrCount++ ;
985     }
986 
987     if ( style.HasLeftIndent() || style.HasRightIndent() )
988     {
989         wxASSERT( controlAttrCount < WXSIZEOF(controlTags) );
990         controlTags[controlAttrCount] = kTXNMarginsTag;
991         controlData[controlAttrCount].marginsPtr = &margins;
992         verify_noerr( TXNGetTXNObjectControls (m_txn, 1 ,
993                                 &controlTags[controlAttrCount], &controlData[controlAttrCount]) );
994         if ( style.HasLeftIndent() )
995         {
996             margins.leftMargin = wxConvertToTXN(style.GetLeftIndent());
997         }
998         if ( style.HasRightIndent() )
999         {
1000             margins.rightMargin = wxConvertToTXN(style.GetRightIndent());
1001         }
1002         controlAttrCount++ ;
1003     }
1004 
1005     if ( style.HasTabs() )
1006     {
1007         const wxArrayInt& tabarray = style.GetTabs();
1008         // unfortunately Mac only applies a tab distance, not individually different tabs
1009         controlTags[controlAttrCount] = kTXNTabSettingsTag;
1010         if ( tabarray.size() > 0 )
1011             controlData[controlAttrCount].tabValue.value = wxConvertToTXN(tabarray[0]);
1012         else
1013             controlData[controlAttrCount].tabValue.value = 72 ;
1014 
1015         controlData[controlAttrCount].tabValue.tabType = kTXNLeftTab;
1016         controlAttrCount++ ;
1017     }
1018 
1019     // unfortunately the relayout is not automatic
1020     if ( controlAttrCount > 0 )
1021     {
1022         verify_noerr( TXNSetTXNObjectControls (m_txn, false /* don't clear all */, controlAttrCount,
1023                                 controlTags, controlData) );
1024         relayout = true;
1025     }
1026 
1027     if ( typeAttrCount > 0 )
1028     {
1029         verify_noerr( TXNSetTypeAttributes( m_txn , typeAttrCount, typeAttr, from , to ) );
1030         if (from != to)
1031             relayout = true;
1032     }
1033 
1034     if ( tabs != NULL )
1035     {
1036         delete[] tabs;
1037     }
1038 
1039     if ( relayout )
1040     {
1041         TXNRecalcTextLayout( m_txn );
1042     }
1043 }
1044 
SetFont(const wxFont & font,const wxColour & foreground,long WXUNUSED (windowStyle),bool WXUNUSED (ignoreBlack))1045 void wxMacMLTEControl::SetFont(const wxFont & font,
1046                                const wxColour& foreground,
1047                                long WXUNUSED(windowStyle),
1048                                bool WXUNUSED(ignoreBlack))
1049 {
1050     wxMacEditHelper help( m_txn ) ;
1051     TXNSetAttribute( wxTextAttr( foreground, wxNullColour, font ), kTXNStartOffset, kTXNEndOffset ) ;
1052 }
1053 
SetStyle(long start,long end,const wxTextAttr & style)1054 void wxMacMLTEControl::SetStyle( long start, long end, const wxTextAttr& style )
1055 {
1056     wxMacEditHelper help( m_txn ) ;
1057     TXNSetAttribute( style, start, end ) ;
1058 }
1059 
Copy()1060 void wxMacMLTEControl::Copy()
1061 {
1062     TXNCopy( m_txn );
1063 }
1064 
Cut()1065 void wxMacMLTEControl::Cut()
1066 {
1067     TXNCut( m_txn );
1068 }
1069 
Paste()1070 void wxMacMLTEControl::Paste()
1071 {
1072     TXNPaste( m_txn );
1073 }
1074 
CanPaste() const1075 bool wxMacMLTEControl::CanPaste() const
1076 {
1077     return TXNIsScrapPastable() ;
1078 }
1079 
SetEditable(bool editable)1080 void wxMacMLTEControl::SetEditable(bool editable)
1081 {
1082     TXNControlTag tag[] = { kTXNIOPrivilegesTag } ;
1083     TXNControlData data[] = { { editable ? kTXNReadWrite : kTXNReadOnly } } ;
1084     TXNSetTXNObjectControls( m_txn, false, WXSIZEOF(tag), tag, data ) ;
1085 }
1086 
GetLastPosition() const1087 long wxMacMLTEControl::GetLastPosition() const
1088 {
1089     wxTextPos actualsize = 0 ;
1090 
1091     Handle theText ;
1092 #if wxUSE_UNICODE
1093     OSErr err = TXNGetDataEncoded( m_txn, kTXNStartOffset, kTXNEndOffset, &theText, kTXNUnicodeTextData );
1094     // all done
1095     if ( err == noErr )
1096     {
1097         actualsize = GetHandleSize( theText )/sizeof(UniChar);
1098         DisposeHandle( theText ) ;
1099     }
1100 #else
1101     OSErr err = TXNGetDataEncoded( m_txn, kTXNStartOffset, kTXNEndOffset, &theText, kTXNTextData );
1102 
1103     // all done
1104     if ( err == noErr )
1105     {
1106         actualsize = GetHandleSize( theText ) ;
1107         DisposeHandle( theText ) ;
1108     }
1109 #endif
1110     else
1111     {
1112         actualsize = 0 ;
1113     }
1114 
1115     return actualsize ;
1116 }
1117 
Replace(long from,long to,const wxString & str)1118 void wxMacMLTEControl::Replace( long from , long to , const wxString &str )
1119 {
1120     wxString value = str ;
1121     wxMacConvertNewlines10To13( &value ) ;
1122 
1123     wxMacEditHelper help( m_txn ) ;
1124 #ifndef __LP64__
1125     wxMacWindowClipper c( GetWXPeer() ) ;
1126 #endif
1127 
1128     TXNSetSelection( m_txn, from, to == -1 ? kTXNEndOffset : to ) ;
1129     TXNClear( m_txn ) ;
1130     SetTXNData( value, kTXNUseCurrentSelection, kTXNUseCurrentSelection ) ;
1131 }
1132 
Remove(long from,long to)1133 void wxMacMLTEControl::Remove( long from , long to )
1134 {
1135 #ifndef __LP64__
1136     wxMacWindowClipper c( GetWXPeer() ) ;
1137 #endif
1138     wxMacEditHelper help( m_txn ) ;
1139     TXNSetSelection( m_txn , from , to ) ;
1140     TXNClear( m_txn ) ;
1141 }
1142 
GetSelection(long * from,long * to) const1143 void wxMacMLTEControl::GetSelection( long* from, long* to) const
1144 {
1145     TXNOffset f,t ;
1146     TXNGetSelection( m_txn , &f , &t ) ;
1147     *from = f;
1148     *to = t;
1149 }
1150 
SetSelection(long from,long to)1151 void wxMacMLTEControl::SetSelection( long from , long to )
1152 {
1153 #ifndef __LP64__
1154     wxMacWindowClipper c( GetWXPeer() ) ;
1155 #endif
1156 
1157     // change the selection
1158     if ((from == -1) && (to == -1))
1159         TXNSelectAll( m_txn );
1160     else
1161         TXNSetSelection( m_txn, from, to == -1 ? kTXNEndOffset : to );
1162 
1163     TXNShowSelection( m_txn, kTXNShowStart );
1164 }
1165 
WriteText(const wxString & str)1166 void wxMacMLTEControl::WriteText( const wxString& str )
1167 {
1168     // TODO: this MPRemoting will be moved into a remoting peer proxy for any command
1169     if ( !wxIsMainThread() )
1170     {
1171 #if wxOSX_USE_CARBON
1172         // unfortunately CW 8 is not able to correctly deduce the template types,
1173         // so we have to instantiate explicitly
1174         wxMacMPRemoteGUICall<wxTextCtrl,wxString>( (wxTextCtrl*) GetWXPeer() , &wxTextCtrl::WriteText , str ) ;
1175 #endif
1176         return ;
1177     }
1178 
1179     wxString st = str ;
1180     wxMacConvertNewlines10To13( &st ) ;
1181 
1182     long start , end , dummy ;
1183 
1184     GetSelection( &start , &dummy ) ;
1185 #ifndef __LP64__
1186     wxMacWindowClipper c( GetWXPeer() ) ;
1187 #endif
1188 
1189     {
1190         wxMacEditHelper helper( m_txn ) ;
1191         SetTXNData( st, kTXNUseCurrentSelection, kTXNUseCurrentSelection ) ;
1192     }
1193 
1194     GetSelection( &dummy, &end ) ;
1195 
1196     // TODO: SetStyle( start , end , GetDefaultStyle() ) ;
1197 }
1198 
Clear()1199 void wxMacMLTEControl::Clear()
1200 {
1201 #ifndef __LP64__
1202     wxMacWindowClipper c( GetWXPeer() ) ;
1203 #endif
1204     wxMacEditHelper st( m_txn ) ;
1205     TXNSetSelection( m_txn , kTXNStartOffset , kTXNEndOffset ) ;
1206     TXNClear( m_txn ) ;
1207 }
1208 
CanUndo() const1209 bool wxMacMLTEControl::CanUndo() const
1210 {
1211     return TXNCanUndo( m_txn , NULL ) ;
1212 }
1213 
Undo()1214 void wxMacMLTEControl::Undo()
1215 {
1216     TXNUndo( m_txn ) ;
1217 }
1218 
CanRedo() const1219 bool wxMacMLTEControl::CanRedo() const
1220 {
1221     return TXNCanRedo( m_txn , NULL ) ;
1222 }
1223 
Redo()1224 void wxMacMLTEControl::Redo()
1225 {
1226     TXNRedo( m_txn ) ;
1227 }
1228 
GetNumberOfLines() const1229 int wxMacMLTEControl::GetNumberOfLines() const
1230 {
1231     ItemCount lines = 0 ;
1232     TXNGetLineCount( m_txn, &lines ) ;
1233 
1234     return lines ;
1235 }
1236 
XYToPosition(long x,long y) const1237 long wxMacMLTEControl::XYToPosition(long x, long y) const
1238 {
1239     Point curpt ;
1240     wxTextPos lastpos ;
1241 
1242     // TODO: find a better implementation : while we can get the
1243     // line metrics of a certain line, we don't get its starting
1244     // position, so it would probably be rather a binary search
1245     // for the start position
1246     long xpos = 0, ypos = 0 ;
1247     int lastHeight = 0 ;
1248     ItemCount n ;
1249 
1250     lastpos = GetLastPosition() ;
1251     for ( n = 0 ; n <= (ItemCount) lastpos ; ++n )
1252     {
1253         if ( y == ypos && x == xpos )
1254             return n ;
1255 
1256         TXNOffsetToPoint( m_txn, n, &curpt ) ;
1257 
1258         if ( curpt.v > lastHeight )
1259         {
1260             xpos = 0 ;
1261             if ( n > 0 )
1262                 ++ypos ;
1263 
1264             lastHeight = curpt.v ;
1265         }
1266         else
1267             ++xpos ;
1268     }
1269 
1270     return 0 ;
1271 }
1272 
PositionToXY(long pos,long * x,long * y) const1273 bool wxMacMLTEControl::PositionToXY( long pos, long *x, long *y ) const
1274 {
1275     Point curpt ;
1276     wxTextPos lastpos ;
1277 
1278     if ( y )
1279         *y = 0 ;
1280     if ( x )
1281         *x = 0 ;
1282 
1283     lastpos = GetLastPosition() ;
1284     if ( pos <= lastpos )
1285     {
1286         // TODO: find a better implementation - while we can get the
1287         // line metrics of a certain line, we don't get its starting
1288         // position, so it would probably be rather a binary search
1289         // for the start position
1290         long xpos = 0, ypos = 0 ;
1291         int lastHeight = 0 ;
1292         ItemCount n ;
1293 
1294         for ( n = 0 ; n <= (ItemCount) pos ; ++n )
1295         {
1296             TXNOffsetToPoint( m_txn, n, &curpt ) ;
1297 
1298             if ( curpt.v > lastHeight )
1299             {
1300                 xpos = 0 ;
1301                 if ( n > 0 )
1302                     ++ypos ;
1303 
1304                 lastHeight = curpt.v ;
1305             }
1306             else
1307                 ++xpos ;
1308         }
1309 
1310         if ( y )
1311             *y = ypos ;
1312         if ( x )
1313             *x = xpos ;
1314     }
1315 
1316     return false ;
1317 }
1318 
ShowPosition(long pos)1319 void wxMacMLTEControl::ShowPosition( long pos )
1320 {
1321     Point current, desired ;
1322     TXNOffset selstart, selend;
1323 
1324     TXNGetSelection( m_txn, &selstart, &selend );
1325     TXNOffsetToPoint( m_txn, selstart, &current );
1326     TXNOffsetToPoint( m_txn, pos, &desired );
1327 
1328     // TODO: use HIPoints for 10.3 and above
1329 
1330     OSErr theErr = noErr;
1331     long dv = desired.v - current.v;
1332     long dh = desired.h - current.h;
1333     TXNShowSelection( m_txn, kTXNShowStart ) ; // NB: should this be kTXNShowStart or kTXNShowEnd ??
1334     theErr = TXNScroll( m_txn, kTXNScrollUnitsInPixels, kTXNScrollUnitsInPixels, &dv, &dh );
1335 
1336     // there will be an error returned for classic MLTE implementation when the control is
1337     // invisible, but HITextView works correctly, so we don't assert that one
1338     // wxASSERT_MSG( theErr == noErr, wxT("TXNScroll returned an error!") );
1339 }
1340 
SetTXNData(const wxString & st,TXNOffset start,TXNOffset end)1341 void wxMacMLTEControl::SetTXNData( const wxString& st, TXNOffset start, TXNOffset end )
1342 {
1343 #if wxUSE_UNICODE
1344     wxMBConvUTF16 converter ;
1345     ByteCount byteBufferLen = converter.WC2MB( NULL, st.wc_str(), 0 ) ;
1346     wxASSERT_MSG( byteBufferLen != wxCONV_FAILED,
1347                   wxT("Conversion to UTF-16 unexpectedly failed") );
1348     UniChar *unibuf = (UniChar*)malloc( byteBufferLen + 2 ) ; // 2 for NUL in UTF-16
1349     converter.WC2MB( (char*)unibuf, st.wc_str(), byteBufferLen + 2 ) ;
1350     TXNSetData( m_txn, kTXNUnicodeTextData, (void*)unibuf, byteBufferLen, start, end ) ;
1351     free( unibuf ) ;
1352 #else // !wxUSE_UNICODE
1353     wxCharBuffer text = st.mb_str( wxConvLocal ) ;
1354     TXNSetData( m_txn, kTXNTextData, (void*)text.data(), strlen( text ), start, end ) ;
1355 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
1356 }
1357 
GetLineText(long lineNo) const1358 wxString wxMacMLTEControl::GetLineText(long lineNo) const
1359 {
1360     wxString line ;
1361 
1362     if ( lineNo < GetNumberOfLines() )
1363     {
1364         Point firstPoint;
1365         Fixed lineWidth, lineHeight, currentHeight;
1366         long ypos ;
1367 
1368         // get the first possible position in the control
1369         TXNOffsetToPoint(m_txn, 0, &firstPoint);
1370 
1371         // Iterate through the lines until we reach the one we want,
1372         // adding to our current y pixel point position
1373         ypos = 0 ;
1374         currentHeight = 0;
1375         while (ypos < lineNo)
1376         {
1377             TXNGetLineMetrics(m_txn, ypos++, &lineWidth, &lineHeight);
1378             currentHeight += lineHeight;
1379         }
1380 
1381         Point thePoint = { firstPoint.v + (currentHeight >> 16), firstPoint.h + (0) };
1382         TXNOffset theOffset;
1383         TXNPointToOffset(m_txn, thePoint, &theOffset);
1384 
1385         wxString content = GetStringValue() ;
1386         Point currentPoint = thePoint;
1387         while (thePoint.v == currentPoint.v && theOffset < content.length())
1388         {
1389             line += content[theOffset];
1390             TXNOffsetToPoint(m_txn, ++theOffset, &currentPoint);
1391         }
1392     }
1393 
1394     return line ;
1395 }
1396 
GetLineLength(long lineNo) const1397 int wxMacMLTEControl::GetLineLength(long lineNo) const
1398 {
1399     int theLength = 0;
1400 
1401     if ( lineNo < GetNumberOfLines() )
1402     {
1403         Point firstPoint;
1404         Fixed lineWidth, lineHeight, currentHeight;
1405         long ypos;
1406 
1407         // get the first possible position in the control
1408         TXNOffsetToPoint(m_txn, 0, &firstPoint);
1409 
1410         // Iterate through the lines until we reach the one we want,
1411         // adding to our current y pixel point position
1412         ypos = 0;
1413         currentHeight = 0;
1414         while (ypos < lineNo)
1415         {
1416             TXNGetLineMetrics(m_txn, ypos++, &lineWidth, &lineHeight);
1417             currentHeight += lineHeight;
1418         }
1419 
1420         Point thePoint = { firstPoint.v + (currentHeight >> 16), firstPoint.h + (0) };
1421         TXNOffset theOffset;
1422         TXNPointToOffset(m_txn, thePoint, &theOffset);
1423 
1424         wxString content = GetStringValue() ;
1425         Point currentPoint = thePoint;
1426         while (thePoint.v == currentPoint.v && theOffset < content.length())
1427         {
1428             ++theLength;
1429             TXNOffsetToPoint(m_txn, ++theOffset, &currentPoint);
1430         }
1431     }
1432 
1433     return theLength ;
1434 }
1435 
1436 // ----------------------------------------------------------------------------
1437 // MLTE control implementation (OSX part)
1438 // ----------------------------------------------------------------------------
1439 
1440 // tiger multi-line textcontrols with no CR in the entire content
1441 // don't scroll automatically, so we need a hack.
1442 // This attempt only works 'before' the key (ie before CallNextEventHandler)
1443 // is processed, thus the scrolling always occurs one character too late, but
1444 // better than nothing ...
1445 
1446 static const EventTypeSpec eventList[] =
1447 {
1448     { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } ,
1449 } ;
1450 
wxMacUnicodeTextEventHandler(EventHandlerCallRef handler,EventRef event,void * data)1451 static pascal OSStatus wxMacUnicodeTextEventHandler( EventHandlerCallRef handler , EventRef event , void *data )
1452 {
1453     OSStatus result = eventNotHandledErr ;
1454     wxMacMLTEHIViewControl* focus = (wxMacMLTEHIViewControl*) data ;
1455 
1456     switch ( GetEventKind( event ) )
1457     {
1458         case kEventTextInputUnicodeForKeyEvent :
1459         {
1460             TXNOffset from , to ;
1461             TXNGetSelection( focus->GetTXNObject() , &from , &to ) ;
1462             if ( from == to )
1463                 TXNShowSelection( focus->GetTXNObject() , kTXNShowStart );
1464             result = CallNextEventHandler(handler,event);
1465             break;
1466         }
1467         default:
1468             break ;
1469     }
1470 
1471     return result ;
1472 }
1473 
wxMacTextControlEventHandler(EventHandlerCallRef handler,EventRef event,void * data)1474 static pascal OSStatus wxMacTextControlEventHandler( EventHandlerCallRef handler , EventRef event , void *data )
1475 {
1476     OSStatus result = eventNotHandledErr ;
1477 
1478     switch ( GetEventClass( event ) )
1479     {
1480         case kEventClassTextInput :
1481             result = wxMacUnicodeTextEventHandler( handler , event , data ) ;
1482             break ;
1483 
1484         default :
1485             break ;
1486     }
1487     return result ;
1488 }
1489 
DEFINE_ONE_SHOT_HANDLER_GETTER(wxMacTextControlEventHandler)1490 DEFINE_ONE_SHOT_HANDLER_GETTER( wxMacTextControlEventHandler )
1491 
1492 wxMacMLTEHIViewControl::wxMacMLTEHIViewControl( wxTextCtrl *wxPeer,
1493     const wxString& str,
1494     const wxPoint& pos,
1495     const wxSize& size, long style ) : wxMacMLTEControl( wxPeer )
1496 {
1497     m_font = wxPeer->GetFont() ;
1498     m_windowStyle = style ;
1499     Rect bounds = wxMacGetBoundsForControl( wxPeer , pos , size ) ;
1500     wxString st = str ;
1501     wxMacConvertNewlines10To13( &st ) ;
1502 
1503     HIRect hr = {
1504         { bounds.left , bounds.top },
1505         { bounds.right - bounds.left, bounds.bottom - bounds.top } } ;
1506 
1507     m_scrollView = NULL ;
1508     TXNFrameOptions frameOptions = FrameOptionsFromWXStyle( style ) ;
1509     if (( frameOptions & (kTXNWantVScrollBarMask | kTXNWantHScrollBarMask)) || (frameOptions &kTXNSingleLineOnlyMask))
1510     {
1511         if ( frameOptions & (kTXNWantVScrollBarMask | kTXNWantHScrollBarMask) )
1512         {
1513             HIScrollViewCreate(
1514                 (frameOptions & kTXNWantHScrollBarMask ? kHIScrollViewOptionsHorizScroll : 0)
1515                 | (frameOptions & kTXNWantVScrollBarMask ? kHIScrollViewOptionsVertScroll : 0) ,
1516                 &m_scrollView ) ;
1517         }
1518         else
1519         {
1520             HIScrollViewCreate(kHIScrollViewOptionsVertScroll,&m_scrollView);
1521             HIScrollViewSetScrollBarAutoHide(m_scrollView,true);
1522         }
1523 
1524         HIViewSetFrame( m_scrollView, &hr );
1525         HIViewSetVisible( m_scrollView, true );
1526     }
1527 
1528     m_textView = NULL ;
1529     HITextViewCreate( NULL , 0, frameOptions , &m_textView ) ;
1530     m_txn = HITextViewGetTXNObject( m_textView ) ;
1531     HIViewSetVisible( m_textView , true ) ;
1532     if ( m_scrollView )
1533     {
1534         HIViewAddSubview( m_scrollView , m_textView ) ;
1535         m_controlRef = m_scrollView ;
1536         InstallEventHandler( (WXWidget) m_textView ) ;
1537     }
1538     else
1539     {
1540         HIViewSetFrame( m_textView, &hr );
1541         m_controlRef = m_textView ;
1542     }
1543 
1544     AdjustCreationAttributes( *wxWHITE , true ) ;
1545 #ifndef __LP64__
1546     wxMacWindowClipper c( GetWXPeer() ) ;
1547 #endif
1548     SetTXNData( st , kTXNStartOffset, kTXNEndOffset ) ;
1549 
1550     TXNSetSelection( m_txn, 0, 0 );
1551     TXNShowSelection( m_txn, kTXNShowStart );
1552 
1553     ::InstallControlEventHandler( m_textView , GetwxMacTextControlEventHandlerUPP(),
1554                                 GetEventTypeCount(eventList), eventList, this,
1555                                 NULL);
1556 }
1557 
~wxMacMLTEHIViewControl()1558 wxMacMLTEHIViewControl::~wxMacMLTEHIViewControl()
1559 {
1560 }
1561 
SetFocus()1562 bool wxMacMLTEHIViewControl::SetFocus()
1563 {
1564     return SetKeyboardFocus( GetControlOwner( m_textView ), m_textView, kControlFocusNextPart ) == noErr ;
1565 }
1566 
HasFocus() const1567 bool wxMacMLTEHIViewControl::HasFocus() const
1568 {
1569     ControlRef control ;
1570     if ( GetUserFocusWindow() == NULL )
1571         return false;
1572 
1573     GetKeyboardFocus( GetUserFocusWindow() , &control ) ;
1574     return control == m_textView ;
1575 }
1576 
SetBackgroundColour(const wxColour & col)1577 void wxMacMLTEHIViewControl::SetBackgroundColour(const wxColour& col )
1578 {
1579     HITextViewSetBackgroundColor( m_textView, col.GetPixel() );
1580 }
1581 
1582 #endif // wxUSE_TEXTCTRL
1583