1// TSComposingBuffer.cpp 2 3#include <Carbon/Carbon.h> 4#include "ATSComponent.h" 5#include "TSComposingBuffer.h" 6 7// we have to define these consts, because they're gone in Tiger... :( 8#define tsbkConvertedText 4 9#define tsbkCaretPosition 1 10#define tsbkSelectedConvertedText 5 11 12TSComposingBuffer::TSComposingBuffer(ComponentInstance i) 13{ 14 inst=i; 15 lastupdate=0; 16 str=[NSMutableString new]; 17} 18 19TSComposingBuffer::~TSComposingBuffer() 20{ 21 [str release]; 22} 23 24TSComposingBuffer* TSComposingBuffer::clear() 25{ 26 NSRange r; 27 r.length=[str length]; 28 r.location=0; 29 [str deleteCharactersInRange: r]; 30 31 // lastupdate=0; 32 return this; 33} 34 35TSComposingBuffer* TSComposingBuffer::send() 36{ 37 if (!isEmpty()) 38 { 39 update(FALSE); 40 lastupdate=[str length]; 41 update(TRUE); 42 lastupdate=0; 43 44 45 [str setString:@""]; 46 // update(FALSE); 47 } 48 return clear(); 49} 50 51Boolean TSComposingBuffer::isEmpty() 52{ 53 return [str length] ? FALSE : TRUE; 54} 55 56NSMutableString* TSComposingBuffer::getContent() 57{ 58 return str; 59} 60 61TSComposingBuffer* TSComposingBuffer::append(NSString *s) 62{ 63 if (s) [str appendString: s]; 64 return this; 65} 66 67int TSComposingBuffer::realPos(int p) 68{ 69 int rp=0; 70 unsigned int i; 71 72 for (i=0; i<[str length]; i++) 73 { 74 if (rp==p) break; 75 76 // see if encounters a surrogate pair 77 UniChar c=[str characterAtIndex: i]; 78 if (c >= 0xd800 && c < 0xdc00) 79 { 80 c=[str characterAtIndex: ++i]; 81 if (i<[str length] && c >= 0xdc00 && c < 0xe000) rp++; 82 } 83 else rp++; 84 } 85 86 return i; 87} 88 89Point TSComposingBuffer::getAppCursorPosition() 90{ 91 Point pnt; 92 long refcon=0, offset=0; 93 Boolean edge=0; 94 EventRef event=NULL; 95 OSStatus status; 96 97 // define an ASSERT macro here for getAppCursorPosition (GACP) 98 #define GACPASSERT(x) if ((status=x) != noErr) return pnt; 99 100 GACPASSERT(CreateEvent(NULL, kEventClassTextInput, 101 kEventTextInputOffsetToPos, GetCurrentEventTime(), 102 kEventAttributeUserEvent, &event)); 103 104 ScriptLanguageRecord record; 105 bzero(&record, sizeof(record)); 106 pnt.h = pnt.v = 0; 107 108 #define EVENTPARAM(a, b, c, d) \ 109 GACPASSERT(SetEventParameter(event, a, b, sizeof(c), d)); 110 111 EVENTPARAM(kEventParamTextInputSendComponentInstance, 112 typeComponentInstance, ComponentInstance, &inst); 113 EVENTPARAM(kEventParamTextInputSendRefCon, typeLongInteger, long, &refcon); 114 EVENTPARAM(kEventParamTextInputSendTextOffset, typeLongInteger, long, &offset); 115 EVENTPARAM(kEventParamTextInputSendSLRec, typeIntlWritingCode, 116 ScriptLanguageRecord, &record); 117 EVENTPARAM(kEventParamTextInputSendLeadingEdge, typeBoolean, Boolean, &edge); 118 GACPASSERT(SendTextInputEvent(event)); 119 GACPASSERT(GetEventParameter(event, 120 kEventParamTextInputReplyPoint, typeQDPoint, NULL, sizeof(Point), 121 NULL, &pnt)); 122 123 if (event) ReleaseEvent(event); 124 return pnt; 125 126 #undef EVENTPARAM 127 #undef GACPASSERT 128} 129 130// Apple's terminolgy calls it "session fix" 131TSComposingBuffer* TSComposingBuffer::update(Boolean send, int cursor, 132 int markFrom, int markTo) 133{ 134 OSErr error=noErr; 135 UniChar *buf=NULL; 136 long reallen=[str length]*sizeof(UniChar); 137 long fixlen=0; 138 EventRef event=NULL; 139 ScriptLanguageRecord script; 140 TextRange pinrange; 141 TextRangeArrayPtr markrange=nil, updaterange=nil; 142 143 if (send) fixlen=reallen; 144 145 // define an ASSERT macro here (U=update) 146 #define UASSERT(x) if ((error=x) != noErr) { clear(); \ 147 if (buf) { delete[] buf; } \ 148 return this; } 149 150 UASSERT(CreateEvent(NULL, kEventClassTextInput, 151 kEventTextInputUpdateActiveInputArea, 152 GetCurrentEventTime(), kEventAttributeUserEvent, &event)); 153 154 // first parameter: reference to our text service component's instance 155 UASSERT(SetEventParameter(event, kEventParamTextInputSendComponentInstance, 156 typeComponentInstance, sizeof(ComponentInstance), 157 &inst)); 158 159 // second parameter: script information 160 script.fScript=ATSCSCRIPT; 161 script.fLanguage=ATSCLANGUAGE; 162 163 UASSERT(SetEventParameter(event, kEventParamTextInputSendSLRec, 164 typeIntlWritingCode, sizeof(ScriptLanguageRecord), &script)); 165 166 buf=new UniChar[[str length]+1]; 167 NSRange bufrange; 168 bufrange.location=0; 169 bufrange.length=[str length]; 170 if (!buf) { clear(); return this; } 171 [str getCharacters:buf range: bufrange]; 172 // 3rd parameter: what we have in the buffer 173 UASSERT(SetEventParameter(event, kEventParamTextInputSendText, 174 typeUnicodeText, reallen, buf)); 175 176 // 4th paramter: "fix" length, >0 if we're dumping ("sending") the buffer 177 UASSERT(SetEventParameter(event, kEventParamTextInputSendFixLen, 178 typeLongInteger, sizeof(long), &fixlen)); 179 180 // set update region 181 updaterange=(TextRangeArrayPtr)NewPtrClear(sizeof(short)+ 182 sizeof(TextRange)*2); 183 184 if (!updaterange) return this; // memory full (unlikely) 185 186 updaterange->fNumOfRanges=2; 187 updaterange->fRange[0].fStart=0; 188 updaterange->fRange[0].fEnd=lastupdate*sizeof(UniChar); 189 updaterange->fRange[0].fHiliteStyle=0; 190 updaterange->fRange[1].fStart=0; 191 updaterange->fRange[1].fEnd=reallen; 192 updaterange->fRange[1].fHiliteStyle=0; 193 194 lastupdate=[str length]; 195 UASSERT(SetEventParameter(event, kEventParamTextInputSendUpdateRng, 196 typeTextRangeArray, sizeof(short)+sizeof(TextRange)*2, updaterange)); 197 198 markrange=(TextRangeArrayPtr)NewPtrClear(sizeof(short)+ 199 sizeof(TextRange)*3); 200 201 // among the four TSM update messages: k(Selected)RawText and 202 // k(Selected)ConvertedText, it seems only the latter pair works, 203 // i.e both kSelectedRawText and kRawText seem to be defunct 204 205 #define SETRANGE(r, f, e, s) markrange->fRange[r].fStart=f; \ 206 markrange->fRange[r].fEnd=e; \ 207 markrange->fRange[r].fHiliteStyle=s 208 209 if(!markrange) return this; // memory full (unlikely) 210 211 markrange->fNumOfRanges=2; 212 int realcur=reallen; 213 214 // if cursor is set (!= -1), we set its position accordingly 215 if (cursor>=0 && realPos(cursor)<=(int)[str length]) 216 realcur=realPos(cursor)*sizeof(UniChar); 217 218 // requests app to draw a light gray underline to our text area 219 SETRANGE(0, 0, reallen, tsbkConvertedText); 220 SETRANGE(1, realcur, realcur, tsbkCaretPosition); 221 222 // if markFrom & markTo are set, draw a darker line underneath the marked area 223 if ((markFrom>=0 && realPos(markFrom)<=(int)[str length]) && 224 (markTo>markFrom && realPos(markTo)<=(int)[str length])) 225 { 226 markrange->fNumOfRanges=3; // send one more range block 227 SETRANGE(2, realPos(markFrom)*sizeof(UniChar), 228 realPos(markTo)*sizeof(UniChar), tsbkSelectedConvertedText); 229 } 230 231 if (!send) { 232 UASSERT(SetEventParameter(event, kEventParamTextInputSendHiliteRng, 233 typeTextRangeArray, 234 sizeof(short)+sizeof(TextRange)*markrange->fNumOfRanges, markrange)); 235 } 236 237 // we don't any "clause" information 238 pinrange.fStart=0; 239 pinrange.fEnd=reallen; 240 UASSERT(SetEventParameter(event,kEventParamTextInputSendPinRng, 241 typeTextRange, sizeof(TextRange), &pinrange)); 242 243 UASSERT(SendTextInputEvent(event)); // send event, whew! 244 245 // delete the allocated range objects 246 DisposePtr((Ptr)markrange); 247 DisposePtr((Ptr)updaterange); 248 delete[] buf; 249 return this; 250} 251