609 lines
19 KiB
C++
609 lines
19 KiB
C++
|
/*******************************************************************************
|
||
|
|
||
|
Copyright (c) 2007 Adobe Systems Incorporated
|
||
|
|
||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||
|
this software and associated documentation files (the "Software"), to deal in
|
||
|
the Software without restriction, including without limitation the rights to
|
||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||
|
subject to the following conditions:
|
||
|
|
||
|
The above copyright notice and this permission notice shall be included in all
|
||
|
copies or substantial portions of the Software.
|
||
|
|
||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
|
|
||
|
********************************************************************************/
|
||
|
|
||
|
#include "ScintillaMacOSX.h"
|
||
|
#include "ExtInput.h"
|
||
|
|
||
|
using namespace Scintilla;
|
||
|
|
||
|
// uncomment this for a log to /dev/console
|
||
|
// #define LOG_TSM 1
|
||
|
|
||
|
#if LOG_TSM
|
||
|
FILE* logFile = NULL;
|
||
|
#endif
|
||
|
|
||
|
static EventHandlerUPP tsmHandler;
|
||
|
|
||
|
static EventTypeSpec tsmSpecs[] = {
|
||
|
{ kEventClassTextInput, kEventTextInputUpdateActiveInputArea },
|
||
|
// { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
|
||
|
{ kEventClassTextInput, kEventTextInputOffsetToPos },
|
||
|
{ kEventClassTextInput, kEventTextInputPosToOffset },
|
||
|
{ kEventClassTextInput, kEventTextInputGetSelectedText }
|
||
|
};
|
||
|
|
||
|
#define kScintillaTSM 'ScTs'
|
||
|
|
||
|
// The following structure is attached to the HIViewRef as property kScintillaTSM
|
||
|
|
||
|
struct TSMData
|
||
|
{
|
||
|
HIViewRef view; // this view
|
||
|
TSMDocumentID docid; // the TSM document ID
|
||
|
EventHandlerRef handler; // the event handler
|
||
|
ScintillaMacOSX* scintilla; // the Scintilla pointer
|
||
|
int styleMask; // the document style mask
|
||
|
int indicStyle [3]; // indicator styles save
|
||
|
int indicColor [3]; // indicator colors save
|
||
|
int selStart; // starting position of selection (Scintilla offset)
|
||
|
int selLength; // UTF-8 number of characters
|
||
|
int selCur; // current position (Scintilla offset)
|
||
|
int inhibitRecursion; // true to stop recursion
|
||
|
bool active; // true if this is active
|
||
|
};
|
||
|
|
||
|
static const int numSpecs = 5;
|
||
|
|
||
|
|
||
|
// Fetch a range of text as UTF-16; delete the buffer after use
|
||
|
|
||
|
static char* getTextPortion (TSMData* data, UInt32 start, UInt32 size)
|
||
|
{
|
||
|
Scintilla::TextRange range;
|
||
|
range.chrg.cpMin = start;
|
||
|
range.chrg.cpMax = start + size;
|
||
|
range.lpstrText = new char [size + 1];
|
||
|
range.lpstrText [size] = 0;
|
||
|
data->scintilla->WndProc (SCI_GETTEXTRANGE, 0, (uptr_t) &range);
|
||
|
return range.lpstrText;
|
||
|
}
|
||
|
|
||
|
static pascal OSStatus doHandleTSM (EventHandlerCallRef, EventRef inEvent, void* userData);
|
||
|
|
||
|
void ExtInput::attach (HIViewRef viewRef)
|
||
|
{
|
||
|
if (NULL == tsmHandler)
|
||
|
tsmHandler = NewEventHandlerUPP (doHandleTSM);
|
||
|
::UseInputWindow (NULL, FALSE);
|
||
|
|
||
|
#ifdef LOG_TSM
|
||
|
if (NULL == logFile)
|
||
|
logFile = fopen ("/dev/console", "a");
|
||
|
#endif
|
||
|
|
||
|
// create and attach the TSM data
|
||
|
TSMData* data = new TSMData;
|
||
|
|
||
|
data->view = viewRef;
|
||
|
data->active = false;
|
||
|
data->inhibitRecursion = 0;
|
||
|
|
||
|
::GetControlProperty (viewRef, scintillaMacOSType, 0, sizeof( data->scintilla ), NULL, &data->scintilla);
|
||
|
|
||
|
if (NULL != data->scintilla)
|
||
|
{
|
||
|
// create the TSM document ref
|
||
|
InterfaceTypeList interfaceTypes;
|
||
|
interfaceTypes[0] = kUnicodeDocumentInterfaceType;
|
||
|
::NewTSMDocument (1, interfaceTypes, &data->docid, (long) viewRef);
|
||
|
// install my event handler
|
||
|
::InstallControlEventHandler (viewRef, tsmHandler, numSpecs, tsmSpecs, data, &data->handler);
|
||
|
|
||
|
::SetControlProperty (viewRef, kScintillaTSM, 0, sizeof (data), &data);
|
||
|
}
|
||
|
else
|
||
|
delete data;
|
||
|
}
|
||
|
|
||
|
static TSMData* getTSMData (HIViewRef viewRef)
|
||
|
{
|
||
|
TSMData* data = NULL;
|
||
|
UInt32 n;
|
||
|
::GetControlProperty (viewRef, kScintillaTSM, 0, sizeof (data), &n, (UInt32*) &data);
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
void ExtInput::detach (HIViewRef viewRef)
|
||
|
{
|
||
|
TSMData* data = getTSMData (viewRef);
|
||
|
if (NULL != data)
|
||
|
{
|
||
|
::DeleteTSMDocument (data->docid);
|
||
|
::RemoveEventHandler (data->handler);
|
||
|
delete data;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ExtInput::activate (HIViewRef viewRef, bool on)
|
||
|
{
|
||
|
TSMData* data = getTSMData (viewRef);
|
||
|
if (NULL == data)
|
||
|
return;
|
||
|
|
||
|
if (on)
|
||
|
{
|
||
|
::ActivateTSMDocument (data->docid);
|
||
|
HIRect bounds;
|
||
|
::HIViewGetBounds (viewRef, &bounds);
|
||
|
::HIViewConvertRect (&bounds, viewRef, NULL);
|
||
|
RgnHandle hRgn = ::NewRgn();
|
||
|
::SetRectRgn (hRgn, (short) bounds.origin.x, (short) bounds.origin.y,
|
||
|
(short) (bounds.origin.x + bounds.size.width),
|
||
|
(short) (bounds.origin.y + bounds.size.height));
|
||
|
#if LOG_TSM
|
||
|
fprintf (logFile, "TSMSetInlineInputRegion (%08lX, %ld:%ld-%ld:%ld)\n",
|
||
|
(long) data->docid, (long) bounds.origin.x, (long) bounds.origin.y,
|
||
|
(long) (bounds.origin.x + bounds.size.width), (long) (bounds.origin.y + bounds.size.height));
|
||
|
fflush (logFile);
|
||
|
#endif
|
||
|
::TSMSetInlineInputRegion (data->docid, HIViewGetWindow (data->view), hRgn);
|
||
|
::DisposeRgn (hRgn);
|
||
|
::UseInputWindow (NULL, FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
#if LOG_TSM
|
||
|
fprintf (logFile, "DeactivateTSMDocument (%08lX)\n", (long) data->docid);
|
||
|
fflush (logFile);
|
||
|
#endif
|
||
|
::DeactivateTSMDocument (data->docid);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void startInput (TSMData* data, bool delSelection = true)
|
||
|
{
|
||
|
if (!data->active && 0 == data->inhibitRecursion)
|
||
|
{
|
||
|
data->active = true;
|
||
|
|
||
|
// Delete any selection
|
||
|
if( delSelection )
|
||
|
data->scintilla->WndProc (SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(""));
|
||
|
|
||
|
// need all style bits because we do indicators
|
||
|
data->styleMask = data->scintilla->WndProc (SCI_GETSTYLEBITS, 0, 0);
|
||
|
data->scintilla->WndProc (SCI_SETSTYLEBITS, 5, 0);
|
||
|
|
||
|
// Set the target range for successive replacements
|
||
|
data->selStart =
|
||
|
data->selCur = data->scintilla->WndProc (SCI_GETCURRENTPOS, 0, 0);
|
||
|
data->selLength = 0;
|
||
|
|
||
|
// save needed styles
|
||
|
for (int i = 0; i < 2; i++)
|
||
|
{
|
||
|
data->indicStyle [i] = data->scintilla->WndProc (SCI_INDICGETSTYLE, i, 0);
|
||
|
data->indicColor [i] = data->scintilla->WndProc (SCI_INDICGETFORE, i, 0);
|
||
|
}
|
||
|
// set styles and colors
|
||
|
data->scintilla->WndProc (SCI_INDICSETSTYLE, 0, INDIC_SQUIGGLE);
|
||
|
data->scintilla->WndProc (SCI_INDICSETFORE, 0, 0x808080);
|
||
|
data->scintilla->WndProc (SCI_INDICSETSTYLE, 1, INDIC_PLAIN); // selected converted
|
||
|
data->scintilla->WndProc (SCI_INDICSETFORE, 1, 0x808080);
|
||
|
data->scintilla->WndProc (SCI_INDICSETSTYLE, 2, INDIC_PLAIN); // selected raw
|
||
|
data->scintilla->WndProc (SCI_INDICSETFORE, 2, 0x0000FF);
|
||
|
// stop Undo
|
||
|
data->scintilla->WndProc (SCI_BEGINUNDOACTION, 0, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void stopInput (TSMData* data, int pos)
|
||
|
{
|
||
|
if (data->active && 0 == data->inhibitRecursion)
|
||
|
{
|
||
|
// First fix the doc - this may cause more messages
|
||
|
// but do not fall into recursion
|
||
|
data->inhibitRecursion++;
|
||
|
::FixTSMDocument (data->docid);
|
||
|
data->inhibitRecursion--;
|
||
|
data->active = false;
|
||
|
|
||
|
// Remove indicator styles
|
||
|
data->scintilla->WndProc (SCI_STARTSTYLING, data->selStart, INDICS_MASK);
|
||
|
data->scintilla->WndProc (SCI_SETSTYLING, pos - data->selStart, 0);
|
||
|
// Restore old indicator styles and colors
|
||
|
data->scintilla->WndProc (SCI_SETSTYLEBITS, data->styleMask, 0);
|
||
|
for (int i = 0; i < 2; i++)
|
||
|
{
|
||
|
data->scintilla->WndProc (SCI_INDICSETSTYLE, i, data->indicStyle [i]);
|
||
|
data->scintilla->WndProc (SCI_INDICSETFORE, i, data->indicColor [i]);
|
||
|
}
|
||
|
|
||
|
// remove selection and re-allow selections to display
|
||
|
data->scintilla->WndProc (SCI_SETSEL, pos, pos);
|
||
|
data->scintilla->WndProc (SCI_TARGETFROMSELECTION, 0, 0);
|
||
|
data->scintilla->WndProc (SCI_HIDESELECTION, 0, 0);
|
||
|
|
||
|
// move the caret behind the current area
|
||
|
data->scintilla->WndProc (SCI_SETCURRENTPOS, pos, 0);
|
||
|
// re-enable Undo
|
||
|
data->scintilla->WndProc (SCI_ENDUNDOACTION, 0, 0);
|
||
|
// re-colorize
|
||
|
int32_t startLine = data->scintilla->WndProc (SCI_LINEFROMPOSITION, data->selStart, 0);
|
||
|
int32_t startPos = data->scintilla->WndProc (SCI_POSITIONFROMLINE, startLine, 0);
|
||
|
int32_t endLine = data->scintilla->WndProc (SCI_LINEFROMPOSITION, pos, 0);
|
||
|
if (endLine == startLine)
|
||
|
endLine++;
|
||
|
int32_t endPos = data->scintilla->WndProc (SCI_POSITIONFROMLINE, endLine, 0);
|
||
|
|
||
|
data->scintilla->WndProc (SCI_COLOURISE, startPos, endPos);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ExtInput::stop (HIViewRef viewRef)
|
||
|
{
|
||
|
TSMData* data = getTSMData (viewRef);
|
||
|
if (NULL != data)
|
||
|
stopInput (data, data->selStart + data->selLength);
|
||
|
}
|
||
|
|
||
|
static char* UTF16toUTF8 (const UniChar* buf, int len, int& utf8len)
|
||
|
{
|
||
|
CFStringRef str = CFStringCreateWithCharactersNoCopy (NULL, buf, (UInt32) len, kCFAllocatorNull);
|
||
|
CFRange range = { 0, len };
|
||
|
CFIndex bufLen;
|
||
|
CFStringGetBytes (str, range, kCFStringEncodingUTF8, '?', false, NULL, 0, &bufLen);
|
||
|
UInt8* utf8buf = new UInt8 [bufLen+1];
|
||
|
CFStringGetBytes (str, range, kCFStringEncodingUTF8, '?', false, utf8buf, bufLen, NULL);
|
||
|
utf8buf [bufLen] = 0;
|
||
|
CFRelease (str);
|
||
|
utf8len = (int) bufLen;
|
||
|
return (char*) utf8buf;
|
||
|
}
|
||
|
|
||
|
static int UCS2Length (const char* buf, int len)
|
||
|
{
|
||
|
int n = 0;
|
||
|
while (len > 0)
|
||
|
{
|
||
|
int bytes = 0;
|
||
|
char ch = *buf;
|
||
|
while (ch & 0x80)
|
||
|
bytes++, ch <<= 1;
|
||
|
len -= bytes;
|
||
|
n += bytes;
|
||
|
}
|
||
|
return n;
|
||
|
}
|
||
|
|
||
|
static int UTF8Length (const UniChar* buf, int len)
|
||
|
{
|
||
|
int n = 0;
|
||
|
while (len > 0)
|
||
|
{
|
||
|
UInt32 uch = *buf++;
|
||
|
len--;
|
||
|
if (uch >= 0xD800 && uch <= 0xDBFF && len > 0)
|
||
|
{
|
||
|
UInt32 uch2 = *buf;
|
||
|
if (uch2 >= 0xDC00 && uch2 <= 0xDFFF)
|
||
|
{
|
||
|
buf++;
|
||
|
len--;
|
||
|
uch = ((uch & 0x3FF) << 10) + (uch2 & 0x3FF);
|
||
|
}
|
||
|
}
|
||
|
n++;
|
||
|
if (uch > 0x7F)
|
||
|
n++;
|
||
|
if (uch > 0x7FF)
|
||
|
n++;
|
||
|
if (uch > 0xFFFF)
|
||
|
n++;
|
||
|
if (uch > 0x1FFFFF)
|
||
|
n++;
|
||
|
if (uch > 0x3FFFFFF)
|
||
|
n++;
|
||
|
}
|
||
|
return n;
|
||
|
}
|
||
|
|
||
|
static OSStatus handleTSMUpdateActiveInputArea (TSMData* data, EventRef inEvent)
|
||
|
{
|
||
|
UInt32 fixLength;
|
||
|
int caretPos = -1;
|
||
|
UInt32 actualSize;
|
||
|
::TextRangeArray* hiliteRanges = NULL;
|
||
|
char* hiliteBuffer = NULL;
|
||
|
bool done;
|
||
|
|
||
|
// extract the text
|
||
|
UniChar* buffer = NULL;
|
||
|
UniChar temp [128];
|
||
|
UniChar* text = temp;
|
||
|
|
||
|
// get the fix length (in bytes)
|
||
|
OSStatus err = ::GetEventParameter (inEvent, kEventParamTextInputSendFixLen,
|
||
|
typeLongInteger, NULL, sizeof (long), NULL, &fixLength);
|
||
|
// need the size (in bytes)
|
||
|
if (noErr == err)
|
||
|
err = ::GetEventParameter (inEvent, kEventParamTextInputSendText,
|
||
|
typeUnicodeText, NULL, 256, &actualSize, temp);
|
||
|
|
||
|
// then allocate and fetch if necessary
|
||
|
UInt32 textLength = actualSize / sizeof (UniChar);
|
||
|
fixLength /= sizeof (UniChar);
|
||
|
|
||
|
if (noErr == err)
|
||
|
{
|
||
|
// this indicates that we are completely done
|
||
|
done = (fixLength == textLength || fixLength < 0);
|
||
|
if (textLength >= 128)
|
||
|
{
|
||
|
buffer = text = new UniChar [textLength];
|
||
|
err = ::GetEventParameter (inEvent, kEventParamTextInputSendText,
|
||
|
typeUnicodeText, NULL, actualSize, NULL, (void*) text);
|
||
|
}
|
||
|
|
||
|
// set the text now, but convert it to UTF-8 first
|
||
|
int utf8len;
|
||
|
char* utf8 = UTF16toUTF8 (text, textLength, utf8len);
|
||
|
data->scintilla->WndProc (SCI_SETTARGETSTART, data->selStart, 0);
|
||
|
data->scintilla->WndProc (SCI_SETTARGETEND, data->selStart + data->selLength, 0);
|
||
|
data->scintilla->WndProc (SCI_HIDESELECTION, 1, 0);
|
||
|
data->scintilla->WndProc (SCI_REPLACETARGET, utf8len, (sptr_t) utf8);
|
||
|
data->selLength = utf8len;
|
||
|
delete [] utf8;
|
||
|
}
|
||
|
|
||
|
// attempt to extract the array of hilite ranges
|
||
|
if (noErr == err)
|
||
|
{
|
||
|
::TextRangeArray tempTextRangeArray;
|
||
|
OSStatus tempErr = ::GetEventParameter (inEvent, kEventParamTextInputSendHiliteRng,
|
||
|
typeTextRangeArray, NULL, sizeof (::TextRangeArray), &actualSize, &tempTextRangeArray);
|
||
|
if (noErr == tempErr)
|
||
|
{
|
||
|
// allocate memory and get the stuff!
|
||
|
hiliteBuffer = new char [actualSize];
|
||
|
hiliteRanges = (::TextRangeArray*) hiliteBuffer;
|
||
|
err = ::GetEventParameter (inEvent, kEventParamTextInputSendHiliteRng,
|
||
|
typeTextRangeArray, NULL, actualSize, NULL, hiliteRanges);
|
||
|
if (noErr != err)
|
||
|
{
|
||
|
delete [] hiliteBuffer;
|
||
|
hiliteBuffer = NULL;
|
||
|
hiliteRanges = NULL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#if LOG_TSM
|
||
|
fprintf (logFile, "kEventTextInputUpdateActiveInputArea:\n"
|
||
|
" TextLength = %ld\n"
|
||
|
" FixLength = %ld\n",
|
||
|
(long) textLength, (long) fixLength);
|
||
|
fflush (logFile);
|
||
|
#endif
|
||
|
|
||
|
if (NULL != hiliteRanges)
|
||
|
{
|
||
|
for (int i = 0; i < hiliteRanges->fNumOfRanges; i++)
|
||
|
{
|
||
|
#if LOG_TSM
|
||
|
fprintf (logFile, " Range #%d: %ld-%ld (%d)\n",
|
||
|
i+1,
|
||
|
hiliteRanges->fRange[i].fStart,
|
||
|
hiliteRanges->fRange[i].fEnd,
|
||
|
hiliteRanges->fRange[i].fHiliteStyle);
|
||
|
fflush (logFile);
|
||
|
#endif
|
||
|
// start and end of range, zero based
|
||
|
long bgn = long (hiliteRanges->fRange[i].fStart) / sizeof (UniChar);
|
||
|
long end = long (hiliteRanges->fRange[i].fEnd) / sizeof (UniChar);
|
||
|
if (bgn >= 0 && end >= 0)
|
||
|
{
|
||
|
// move the caret if this is requested
|
||
|
if (hiliteRanges->fRange[i].fHiliteStyle == kTSMHiliteCaretPosition)
|
||
|
caretPos = bgn;
|
||
|
else
|
||
|
{
|
||
|
// determine which style to use
|
||
|
int style;
|
||
|
switch (hiliteRanges->fRange[i].fHiliteStyle)
|
||
|
{
|
||
|
case kTSMHiliteRawText: style = INDIC0_MASK; break;
|
||
|
case kTSMHiliteSelectedRawText: style = INDIC0_MASK; break;
|
||
|
case kTSMHiliteConvertedText: style = INDIC1_MASK; break;
|
||
|
case kTSMHiliteSelectedConvertedText: style = INDIC2_MASK; break;
|
||
|
default: style = INDIC0_MASK;
|
||
|
}
|
||
|
// bgn and end are Unicode offsets from the starting pos
|
||
|
// use the text buffer to determine the UTF-8 offsets
|
||
|
long utf8bgn = data->selStart + UTF8Length (text, bgn);
|
||
|
long utf8size = UTF8Length (text + bgn, end - bgn);
|
||
|
// set indicators
|
||
|
int oldEnd = data->scintilla->WndProc (SCI_GETENDSTYLED, 0, 0);
|
||
|
data->scintilla->WndProc (SCI_STARTSTYLING, utf8bgn, INDICS_MASK);
|
||
|
data->scintilla->WndProc (SCI_SETSTYLING, utf8size, style & ~1);
|
||
|
data->scintilla->WndProc (SCI_STARTSTYLING, oldEnd, 31);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (noErr == err)
|
||
|
{
|
||
|
// if the fixed length is == to the new text, we are done
|
||
|
if (done)
|
||
|
stopInput (data, data->selStart + UTF8Length (text, textLength));
|
||
|
else if (caretPos >= 0)
|
||
|
{
|
||
|
data->selCur = data->selStart + UTF8Length (text, caretPos);
|
||
|
data->scintilla->WndProc (SCI_SETCURRENTPOS, data->selCur, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
delete [] hiliteBuffer;
|
||
|
delete [] buffer;
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
struct MacPoint {
|
||
|
short v;
|
||
|
short h;
|
||
|
};
|
||
|
|
||
|
static OSErr handleTSMOffset2Pos (TSMData* data, EventRef inEvent)
|
||
|
{
|
||
|
long offset;
|
||
|
|
||
|
// get the offfset to convert
|
||
|
OSStatus err = ::GetEventParameter (inEvent, kEventParamTextInputSendTextOffset,
|
||
|
typeLongInteger, NULL, sizeof (long), NULL, &offset);
|
||
|
if (noErr == err)
|
||
|
{
|
||
|
// where is the caret now?
|
||
|
HIPoint where;
|
||
|
|
||
|
int line = (int) data->scintilla->WndProc (SCI_LINEFROMPOSITION, data->selCur, 0);
|
||
|
where.x = data->scintilla->WndProc (SCI_POINTXFROMPOSITION, 0, data->selCur);
|
||
|
where.y = data->scintilla->WndProc (SCI_POINTYFROMPOSITION, 0, data->selCur)
|
||
|
+ data->scintilla->WndProc (SCI_TEXTHEIGHT, line, 0);
|
||
|
// convert to window coords
|
||
|
::HIViewConvertPoint (&where, data->view, NULL);
|
||
|
// convert to screen coords
|
||
|
Rect global;
|
||
|
GetWindowBounds (HIViewGetWindow (data->view), kWindowStructureRgn, &global);
|
||
|
MacPoint pt;
|
||
|
pt.h = (short) where.x + global.left;
|
||
|
pt.v = (short) where.y + global.top;
|
||
|
|
||
|
// set the result
|
||
|
err = ::SetEventParameter (inEvent, kEventParamTextInputReplyPoint, typeQDPoint, sizeof (MacPoint), &pt);
|
||
|
#if LOG_TSM
|
||
|
fprintf (logFile, "kEventTextInputOffsetToPos:\n"
|
||
|
" Offset: %ld\n"
|
||
|
" Pos: %ld:%ld (orig = %ld:%ld)\n", offset,
|
||
|
(long) pt.h, (long) pt.v,
|
||
|
(long) where.x, (long) where.y);
|
||
|
fflush (logFile);
|
||
|
#endif
|
||
|
}
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
static OSErr handleTSMPos2Offset (TSMData* data, EventRef inEvent)
|
||
|
{
|
||
|
MacPoint qdPosition;
|
||
|
long offset;
|
||
|
short regionClass;
|
||
|
|
||
|
// retrieve the global point to convert
|
||
|
OSStatus err = ::GetEventParameter (inEvent, kEventParamTextInputSendCurrentPoint,
|
||
|
typeQDPoint, NULL, sizeof (MacPoint), NULL, &qdPosition);
|
||
|
if (noErr == err)
|
||
|
{
|
||
|
#if LOG_TSM
|
||
|
fprintf (logFile, "kEventTextInputPosToOffset:\n"
|
||
|
" Pos: %ld:%ld\n", (long) qdPosition.v, (long) qdPosition.h);
|
||
|
fflush (logFile);
|
||
|
#endif
|
||
|
// convert to local coordinates
|
||
|
HIRect rect;
|
||
|
rect.origin.x = qdPosition.h;
|
||
|
rect.origin.y = qdPosition.v;
|
||
|
rect.size.width =
|
||
|
rect.size.height = 1;
|
||
|
::HIViewConvertRect (&rect, NULL, data->view);
|
||
|
|
||
|
// we always report the position to be within the composition;
|
||
|
// coords inside the same pane are clipped to the composition,
|
||
|
// and if the position is outside, then we deactivate this instance
|
||
|
// this leaves the edit open and active so we can edit multiple panes
|
||
|
regionClass = kTSMInsideOfActiveInputArea;
|
||
|
|
||
|
// compute the offset (relative value)
|
||
|
offset = data->scintilla->WndProc (SCI_POSITIONFROMPOINTCLOSE, (uptr_t) rect.origin.x, (sptr_t) rect.origin.y);
|
||
|
if (offset >= 0)
|
||
|
{
|
||
|
// convert to a UTF-16 offset (Brute Force)
|
||
|
char* buf = getTextPortion (data, 0, offset);
|
||
|
offset = UCS2Length (buf, offset);
|
||
|
delete [] buf;
|
||
|
|
||
|
#if LOG_TSM
|
||
|
fprintf (logFile, " Offset: %ld (class %ld)\n", offset, (long) regionClass);
|
||
|
fflush (logFile);
|
||
|
#endif
|
||
|
// store the offset
|
||
|
err = ::SetEventParameter (inEvent, kEventParamTextInputReplyTextOffset, typeLongInteger, sizeof (long), &offset);
|
||
|
if (noErr == err)
|
||
|
err = ::SetEventParameter (inEvent, kEventParamTextInputReplyRegionClass, typeShortInteger, sizeof (short), ®ionClass);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// not this pane!
|
||
|
err = eventNotHandledErr;
|
||
|
ExtInput::activate (data->view, false);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
static OSErr handleTSMGetText (TSMData* data, EventRef inEvent)
|
||
|
{
|
||
|
char* buf = getTextPortion (data, data->selStart, data->selLength);
|
||
|
|
||
|
#if LOG_TSM
|
||
|
fprintf (logFile, "kEventTextInputGetSelectedText:\n"
|
||
|
" Text: \"%s\"\n", buf);
|
||
|
fflush (logFile);
|
||
|
#endif
|
||
|
OSStatus status = ::SetEventParameter (inEvent, kEventParamTextInputReplyText, typeUTF8Text, data->selLength, buf);
|
||
|
delete [] buf;
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
static pascal OSStatus doHandleTSM (EventHandlerCallRef, EventRef inEvent, void* userData)
|
||
|
{
|
||
|
TSMData* data = (TSMData*) userData;
|
||
|
|
||
|
OSStatus err = eventNotHandledErr;
|
||
|
|
||
|
switch (::GetEventKind (inEvent))
|
||
|
{
|
||
|
case kEventTextInputUpdateActiveInputArea:
|
||
|
// Make sure that input has been started
|
||
|
startInput (data);
|
||
|
err = handleTSMUpdateActiveInputArea (data, inEvent);
|
||
|
break;
|
||
|
// case kEventTextInputUnicodeForKeyEvent:
|
||
|
// err = handleTSMUnicodeInput (inEvent);
|
||
|
// break;
|
||
|
case kEventTextInputOffsetToPos:
|
||
|
err = handleTSMOffset2Pos (data, inEvent);
|
||
|
break;
|
||
|
case kEventTextInputPosToOffset:
|
||
|
err = handleTSMPos2Offset (data, inEvent);
|
||
|
break;
|
||
|
case kEventTextInputGetSelectedText:
|
||
|
// Make sure that input has been started
|
||
|
startInput (data, false);
|
||
|
err = handleTSMGetText (data, inEvent);
|
||
|
break;
|
||
|
}
|
||
|
return err;
|
||
|
}
|
||
|
|