diff options
Diffstat (limited to 'macosx')
-rw-r--r-- | macosx/ExtInput.cxx | 608 | ||||
-rw-r--r-- | macosx/ExtInput.h | 64 | ||||
-rw-r--r-- | macosx/PlatMacOSX.cxx | 395 | ||||
-rw-r--r-- | macosx/PlatMacOSX.h | 6 | ||||
-rw-r--r-- | macosx/QuartzTextLayout.h | 57 | ||||
-rw-r--r-- | macosx/QuartzTextStyle.h | 27 | ||||
-rw-r--r-- | macosx/QuartzTextStyleAttribute.h | 24 | ||||
-rw-r--r-- | macosx/ScintillaMacOSX.cxx | 837 | ||||
-rw-r--r-- | macosx/ScintillaMacOSX.h | 54 | ||||
-rw-r--r-- | macosx/TView.cxx | 132 | ||||
-rw-r--r-- | macosx/makefile | 15 |
11 files changed, 1482 insertions, 737 deletions
diff --git a/macosx/ExtInput.cxx b/macosx/ExtInput.cxx new file mode 100644 index 000000000..677a82c3f --- /dev/null +++ b/macosx/ExtInput.cxx @@ -0,0 +1,608 @@ +/******************************************************************************* + +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; +} + diff --git a/macosx/ExtInput.h b/macosx/ExtInput.h new file mode 100644 index 000000000..0179e83b9 --- /dev/null +++ b/macosx/ExtInput.h @@ -0,0 +1,64 @@ +/******************************************************************************* + +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. + +********************************************************************************/ + +#ifndef _ExtInput_h +#define _ExtInput_h + +#include <Carbon/Carbon.h> +#include "Scintilla.h" + +namespace Scintilla +{ + +/** +The ExtInput class provides TSM input services to Scintilla. +It uses the indicators 0 and 1 (see SCI_INDICSETSTYLE) to apply +underlines to partially converted text. +*/ + +class ExtInput +{ +public: + /** + Attach extended input to a HIView with attached Scintilla. This installs the needed + event handlers etc. + */ + static void attach (HIViewRef ref); + /** + Detach extended input from a HIViewwith attached Scintilla. + */ + static void detach (HIViewRef ref); + /** + Activate or deactivate extended input. This method should be called whenever + the view gains or loses focus. + */ + static void activate (HIViewRef ref, bool on); + /** + Terminate extended input. + */ + static void stop (HIViewRef ref); +}; + +} // end namespace + +#endif diff --git a/macosx/PlatMacOSX.cxx b/macosx/PlatMacOSX.cxx index 631e0d95e..f8282c5f8 100644 --- a/macosx/PlatMacOSX.cxx +++ b/macosx/PlatMacOSX.cxx @@ -109,29 +109,24 @@ void Font::Release() { SurfaceImpl::SurfaceImpl() { bitmapData = NULL; // Release will try and delete bitmapData if != NULL -#ifdef SUPPORT_PORT - port = NULL; -#endif gc = NULL; + textLayout = new QuartzTextLayout(NULL); Release(); } SurfaceImpl::~SurfaceImpl() { Release(); + delete textLayout; } void SurfaceImpl::Release() { + textLayout->setContext (NULL); if ( bitmapData != NULL ) { delete[] bitmapData; // We only "own" the graphics context if we are a bitmap context if ( gc != NULL ) CGContextRelease( gc ); } -#ifdef SUPPORT_PORT - if ( port != NULL && gc != NULL) { - QDEndCGContext(port, &gc); - } -#endif bitmapData = NULL; gc = NULL; @@ -155,20 +150,13 @@ void SurfaceImpl::Init(WindowID /*wid*/) { // aquire/release of the context. Release(); -#ifdef SUPPORT_PORT - // Note, this seems to be unecessary, I have not seen any functionality loss by - // doing nothing in this method. - - WindowRef window = GetControlOwner(reinterpret_cast<HIViewRef>(wid)); - port = GetWindowPort(window); - QDBeginCGContext(port, &gc); -#endif } void SurfaceImpl::Init(SurfaceID sid, WindowID /*wid*/) { Release(); gc = reinterpret_cast<CGContextRef>( sid ); CGContextSetLineWidth( gc, 1.0 ); + textLayout->setContext (gc); } void SurfaceImpl::InitPixMap(int width, int height, Surface* /*surface_*/, WindowID /*wid*/) { @@ -182,13 +170,12 @@ void SurfaceImpl::InitPixMap(int width, int height, Surface* /*surface_*/, Windo // create an RGB color space CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - assert( colorSpace != NULL ); + if( colorSpace == NULL ) + return; // create the bitmap bitmapData = new uint8_t[ bitmapByteCount ]; - assert( bitmapData != NULL ); - if( bitmapData != NULL ) - { + if( bitmapData != NULL ) { // create the context gc = CGBitmapContextCreate( bitmapData, width, @@ -198,24 +185,25 @@ void SurfaceImpl::InitPixMap(int width, int height, Surface* /*surface_*/, Windo colorSpace, kCGImageAlphaPremultipliedLast); - if( gc == NULL ) - { + if( gc == NULL ) { // the context couldn't be created for some reason, // and we have no use for the bitmap without the context delete[] bitmapData; bitmapData = NULL; - } } + textLayout->setContext (gc); + } // the context retains the color space, so we can release it CGColorSpaceRelease( colorSpace ); - assert( gc != NULL && bitmapData != NULL ); - - // "Erase" to white - CGContextClearRect( gc, CGRectMake( 0, 0, width, height ) ); - CGContextSetRGBFillColor( gc, 1.0, 1.0, 1.0, 1.0 ); - CGContextFillRect( gc, CGRectMake( 0, 0, width, height ) ); + if ( gc != NULL && bitmapData != NULL ) + { + // "Erase" to white + CGContextClearRect( gc, CGRectMake( 0, 0, width, height ) ); + CGContextSetRGBFillColor( gc, 1.0, 1.0, 1.0, 1.0 ); + CGContextFillRect( gc, CGRectMake( 0, 0, width, height ) ); + } } void SurfaceImpl::PenColour(ColourAllocated fore) { @@ -244,28 +232,30 @@ CGImageRef SurfaceImpl::GetImage() { // create an RGB color space CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - assert( colorSpace != NULL ); + if( colorSpace == NULL ) + return NULL; const int bitmapBytesPerRow = ((int) bitmapWidth * BYTES_PER_PIXEL); const int bitmapByteCount = (bitmapBytesPerRow * (int) bitmapHeight); // Create a data provider CGDataProviderRef dataProvider = CGDataProviderCreateWithData( NULL, bitmapData, bitmapByteCount, NULL ); - assert( dataProvider != NULL ); - - // create the CGImage - CGImageRef image = CGImageCreate( bitmapWidth, - bitmapHeight, - BITS_PER_COMPONENT, - BITS_PER_PIXEL, - bitmapBytesPerRow, - colorSpace, - kCGImageAlphaPremultipliedLast, - dataProvider, - NULL, - 0, - kCGRenderingIntentDefault ); - assert( image != NULL ); + CGImageRef image = NULL; + if ( dataProvider != NULL ) + { + // create the CGImage + image = CGImageCreate( bitmapWidth, + bitmapHeight, + BITS_PER_COMPONENT, + BITS_PER_PIXEL, + bitmapBytesPerRow, + colorSpace, + kCGImageAlphaPremultipliedLast, + dataProvider, + NULL, + 0, + kCGRenderingIntentDefault ); + } // the image retains the color space, so we can release it CGColorSpaceRelease( colorSpace ); @@ -379,8 +369,6 @@ void SurfaceImpl::FillRectangle(PRectangle rc, Surface &surfacePattern) { return; } - assert( image != NULL ); - const CGPatternCallbacks drawImageCallbacks = { 0, reinterpret_cast<CGPatternDrawPatternCallback>( drawImageRefCallback ), NULL }; CGPatternRef pattern = CGPatternCreate( image, @@ -392,30 +380,30 @@ void SurfaceImpl::FillRectangle(PRectangle rc, Surface &surfacePattern) { true, &drawImageCallbacks ); - assert( pattern != NULL ); - - // Create a pattern color space - CGColorSpaceRef colorSpace = CGColorSpaceCreatePattern( NULL ); - assert( colorSpace != NULL ); - - CGContextSaveGState( gc ); - CGContextSetFillColorSpace( gc, colorSpace ); - - // Unlike the documentation, you MUST pass in a "components" parameter: - // For coloured patterns it is the alpha value. - const float alpha = 1.0; - CGContextSetFillPattern( gc, pattern, &alpha ); - CGContextFillRect( gc, PRectangleToCGRect( rc ) ); - - CGContextRestoreGState( gc ); - - // Free the color space, the pattern and image - CGColorSpaceRelease( colorSpace ); - colorSpace = NULL; - CGPatternRelease( pattern ); - pattern = NULL; - CGImageRelease( image ); - image = NULL; + if( pattern != NULL ) { + + // Create a pattern color space + CGColorSpaceRef colorSpace = CGColorSpaceCreatePattern( NULL ); + if( colorSpace != NULL ) { + + CGContextSaveGState( gc ); + CGContextSetFillColorSpace( gc, colorSpace ); + + // Unlike the documentation, you MUST pass in a "components" parameter: + // For coloured patterns it is the alpha value. + const float alpha = 1.0; + CGContextSetFillPattern( gc, pattern, &alpha ); + CGContextFillRect( gc, PRectangleToCGRect( rc ) ); + CGContextRestoreGState( gc ); + // Free the color space, the pattern and image + CGColorSpaceRelease( colorSpace ); + } /* colorSpace != NULL */ + colorSpace = NULL; + CGPatternRelease( pattern ); + pattern = NULL; + CGImageRelease( image ); + image = NULL; + } /* pattern != NULL */ } void SurfaceImpl::RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back) { @@ -480,20 +468,16 @@ void SurfaceImpl::RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAl CGContextDrawPath( gc, kCGPathFillStroke ); } -void SurfaceImpl::AlphaRectangle(PRectangle rc, int cornerSize, ColourAllocated fill, int alphaFill, - ColourAllocated outline, int alphaOutline, int flags) { - // XXX TODO +void Scintilla::SurfaceImpl::AlphaRectangle(PRectangle rc, int /*cornerSize*/, ColourAllocated fill, int alphaFill, + ColourAllocated /*outline*/, int /*alphaOutline*/, int /*flags*/) +{ if ( gc ) { - CGContextBeginPath( gc ); - //FillColour(fill); - PenColour(outline); + ColourDesired colour( fill.AsLong() ); - // Quartz integer -> float point conversion fun (see comment in SurfaceImpl::LineTo) - // We subtract 1 from the Width() and Height() so that all our drawing is within the area defined - // by the PRectangle. Otherwise, we draw one pixel too far to the right and bottom. - // TODO: Create some version of PRectangleToCGRect to do this conversion for us? - CGContextAddRect( gc, CGRectMake( rc.left + 0.5, rc.top + 0.5, rc.Width() - 1, rc.Height() - 1 ) ); - CGContextDrawPath( gc, kCGPathFill ); + // Set the Fill color to match + CGContextSetRGBFillColor( gc, colour.GetRed() / 255.0, colour.GetGreen() / 255.0, colour.GetBlue() / 255.0, alphaFill / 100.0 ); + CGRect rect = PRectangleToCGRect( rc ); + CGContextFillRect( gc, rect ); } } @@ -597,8 +581,6 @@ void SurfaceImpl::Copy(PRectangle rc, Scintilla::Point from, Surface &surfaceSou return; } - assert( image != NULL ); - // Now draw the image on the surface // Some fancy clipping work is required here: draw only inside of rc @@ -616,29 +598,6 @@ void SurfaceImpl::Copy(PRectangle rc, Scintilla::Point from, Surface &surfaceSou image = NULL; } -QuartzTextLayout* SurfaceImpl::GetTextLayout( Font &font_, const char *s, int len ) -{ - // create the text layout - QuartzTextLayout* textLayout = new QuartzTextLayout( gc ); - OSStatus err = textLayout->setText( reinterpret_cast<const UInt8*>( s ), len, ( unicodeMode ? kCFStringEncodingUTF8 : kCFStringEncodingASCII ) ); - if (err != noErr) { - fprintf(stderr, "SurfaceImpl::GetTextLayout error calling textLayout->setText %d %s\n", err, unicodeMode?"Invalid UTF8":"Unknown error"); - if (unicodeMode) - err = textLayout->setText( reinterpret_cast<const UInt8*>( s ), len, kCFStringEncodingASCII ); - if (err != noErr) - return NULL; - } - - textLayout->setStyle( *reinterpret_cast<QuartzTextStyle*>( font_.GetID() ) ); - - // TODO: If I could modify Scintilla to use ATSUHighlightText, this would not be required. - // However, using this setting makes Scintilla's rendering match TextEdit's (except for the antialiasing rules) - ATSLineLayoutOptions rendering = kATSLineUseDeviceMetrics; - textLayout->setControl( kATSULineLayoutOptionsTag, sizeof( rendering ), &rendering ); - - return textLayout; -} - void SurfaceImpl::DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back) { @@ -656,89 +615,80 @@ void SurfaceImpl::DrawTextClipped(PRectangle rc, Font &font_, int ybase, const c } void SurfaceImpl::DrawTextTransparent(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore) { - PLATFORM_ASSERT( gc != NULL ); - QuartzTextLayout* textLayout = GetTextLayout( font_, s, len ); - if (!textLayout) return; + textLayout->setText (reinterpret_cast<const UInt8*>(s), len, *reinterpret_cast<QuartzTextStyle*>(font_.GetID())); + // The Quartz RGB fill color influences the ATSUI color FillColour(fore); - // Draw the text, with the Y axis flipped textLayout->draw( rc.left, ybase, true ); - - // Get rid of the layout object - delete textLayout; - textLayout = NULL; } void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positions) { // sample at http://developer.apple.com/samplecode/ATSUICurveAccessDemo/listing1.html // sample includes use of ATSUGetGlyphInfo which would be better for older // OSX systems. We should expand to using that on older systems as well. - QuartzTextLayout* textLayout = GetTextLayout( font_, s, len ); - if (!textLayout) return; - - // Get the UNICODE character length - UniCharCount unicodeLength, unicodePosition; - OSStatus err; - err = ATSUGetTextLocation( textLayout->getLayout(), NULL, NULL, NULL, &unicodeLength, NULL ); - assert( err == noErr ); - - ATSLayoutRecord *layoutRecords; - ItemCount numRecords; - // Get the arrays of glyph information - verify_noerr( ATSUDirectGetLayoutDataArrayPtrFromTextLayout( - textLayout->getLayout(), 0, - kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, - (void **)&layoutRecords, &numRecords) ); - - int i, count; - long position; - unsigned char uch; - unsigned char mask; - FontID fontid = font_.GetID(); - - for ( unicodePosition = 0, i = 0; i < len && unicodePosition < numRecords; unicodePosition ++ ) { - if (fontid) { - - // note: an extra layoutRecord is provided (eg. numRecords will be one - // more than unicodeLength). Each record contains the left x - // coordinate, but we need the right x coordinate, so we skip - // the first layoutRecord, thus unicodePosition+1. - position = Fix2Long( layoutRecords[unicodePosition+1].realPos ); - uch = s[i]; - positions[i++] = position; - - // If we are using unicode (UTF8), map the Unicode position back to the UTF8 characters, - // as 1 unicode character can map to multiple UTF8 characters. - // See: http://www.tbray.org/ongoing/When/200x/2003/04/26/UTF - // Or: http://www.cl.cam.ac.uk/~mgk25/unicode.html - if ( unicodeMode ) { - mask = 0xc0; - count = 1; - // Add one additonal byte for each extra high order one in the byte - while ( uch >= mask && count < 8 ) { - assert( i < len ); + for (int i = 0; i < len; i++) + positions [i] = 0; + + // We need the right X coords, so we have to append a char to get the left coord of thast extra char + char* buf = (char*) malloc (len+1); + if (!buf) + return; + + memcpy (buf, s, len); + buf [len] = '.'; + + textLayout->setText (reinterpret_cast<const UInt8*>(buf), len+1, *reinterpret_cast<QuartzTextStyle*>(font_.GetID())); + ATSUGlyphInfoArray* theGlyphInfoArrayPtr; + ByteCount theArraySize; + + // Get the GlyphInfoArray + ATSUTextLayout layout = textLayout->getLayout(); + if ( noErr == ATSUGetGlyphInfo (layout, 0, textLayout->getLength(), &theArraySize, NULL)) + { + theGlyphInfoArrayPtr = (ATSUGlyphInfoArray *) malloc (theArraySize + sizeof(ItemCount) + sizeof(ATSUTextLayout)); + if (theGlyphInfoArrayPtr) + { + if (noErr == ATSUGetGlyphInfo (layout, 0, textLayout->getLength(), &theArraySize, theGlyphInfoArrayPtr)) + { + // do not count the first item, which is at the beginning of the line + for ( UniCharCount unicodePosition = 1, i = 0; i < len && unicodePosition < theGlyphInfoArrayPtr->numGlyphs; unicodePosition ++ ) + { + // The ideal position is the x coordinate of the glyph, relative to the beginning of the line + int position = (int)( theGlyphInfoArrayPtr->glyphs[unicodePosition].idealX + 0.5 ); // These older APIs return float values + unsigned char uch = s[i]; positions[i++] = position; - count ++; - mask = mask >> 1 | 0x80; // add an additional one in the highest order position + + // If we are using unicode (UTF8), map the Unicode position back to the UTF8 characters, + // as 1 unicode character can map to multiple UTF8 characters. + // See: http://www.tbray.org/ongoing/When/200x/2003/04/26/UTF + // Or: http://www.cl.cam.ac.uk/~mgk25/unicode.html + if ( unicodeMode ) + { + unsigned char mask = 0xc0; + int count = 1; + // Add one additonal byte for each extra high order one in the byte + while ( uch >= mask && count < 8 ) + { + positions[i++] = position; + count ++; + mask = mask >> 1 | 0x80; // add an additional one in the highest order position + } + } } - assert( count <= 8 ); } - } else { - positions[i++] = (i == 0) ? 1 : positions[i-1] + 1; + + // Free the GlyphInfoArray + free (theGlyphInfoArrayPtr); } } - verify_noerr( ATSUDirectReleaseLayoutDataArrayPtr(NULL, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void **)&layoutRecords) ); - // Get rid of the layout object - delete textLayout; - textLayout = NULL; + free (buf); } int SurfaceImpl::WidthText(Font &font_, const char *s, int len) { if (font_.GetID()) { - QuartzTextLayout* textLayout = GetTextLayout( font_, s, len ); - if (!textLayout) return 0; + textLayout->setText (reinterpret_cast<const UInt8*>(s), len, *reinterpret_cast<QuartzTextStyle*>(font_.GetID())); // TODO: Maybe I should add some sort of text measurement features to QuartzTextLayout? unsigned long actualNumberOfBounds = 0; @@ -748,17 +698,10 @@ int SurfaceImpl::WidthText(Font &font_, const char *s, int len) { if ( ATSUGetGlyphBounds( textLayout->getLayout(), 0, 0, kATSUFromTextBeginning, kATSUToTextEnd, kATSUseDeviceOrigins, 1, &glyphBounds, &actualNumberOfBounds ) != noErr || actualNumberOfBounds != 1 ) { Platform::DebugDisplay( "ATSUGetGlyphBounds failed in WidthText" ); - // Get rid of the layout object - delete textLayout; - textLayout = NULL; return 0; } //Platform::DebugPrintf( "WidthText: \"%*s\" = %ld\n", len, s, Fix2Long( glyphBounds.upperRight.x - glyphBounds.upperLeft.x ) ); - - // Get rid of the layout object - delete textLayout; - textLayout = NULL; return Fix2Long( glyphBounds.upperRight.x - glyphBounds.upperLeft.x ); } return 1; @@ -768,8 +711,7 @@ int SurfaceImpl::WidthChar(Font &font_, char ch) { char str[2] = { ch, '\0' }; if (font_.GetID()) { - QuartzTextLayout* textLayout = GetTextLayout( font_, str, 1 ); - if (!textLayout) return 0; + textLayout->setText (reinterpret_cast<const UInt8*>(str), 1, *reinterpret_cast<QuartzTextStyle*>(font_.GetID())); // TODO: Maybe I should add some sort of text measurement features to QuartzTextLayout? unsigned long actualNumberOfBounds = 0; @@ -779,15 +721,9 @@ int SurfaceImpl::WidthChar(Font &font_, char ch) { if ( ATSUGetGlyphBounds( textLayout->getLayout(), 0, 0, kATSUFromTextBeginning, kATSUToTextEnd, kATSUseDeviceOrigins, 1, &glyphBounds, &actualNumberOfBounds ) != noErr || actualNumberOfBounds != 1 ) { Platform::DebugDisplay( "ATSUGetGlyphBounds failed in WidthChar" ); - // Get rid of the layout object - delete textLayout; - textLayout = NULL; return 0; } - // Get rid of the layout object - delete textLayout; - textLayout = NULL; return Fix2Long( glyphBounds.upperRight.x - glyphBounds.upperLeft.x ); } else @@ -893,14 +829,15 @@ void SurfaceImpl::SetUnicodeMode(bool unicodeMode_) { } void SurfaceImpl::SetDBCSMode(int codePage) { - // TODO: Implement this + // TODO: Implement this for code pages != UTF-8 } Surface *Surface::Allocate() { return new SurfaceImpl( ); } -Window::~Window() {} +Window::~Window() { +} void Window::Destroy() { if (windowRef) { @@ -996,9 +933,7 @@ void Window::Show(bool show) { void Window::InvalidateAll() { if ( id ) { - OSStatus err; - err = HIViewSetNeedsDisplay( reinterpret_cast<HIViewRef>( id ), true ); - assert( err == noErr ); + HIViewSetNeedsDisplay( reinterpret_cast<HIViewRef>( id ), true ); } } @@ -1006,13 +941,10 @@ void Window::InvalidateRectangle(PRectangle rc) { if (id) { // Create a rectangular region RgnHandle region = NewRgn(); - assert( region != NULL ); SetRectRgn( region, rc.left, rc.top, rc.right, rc.bottom ); // Make that region invalid - OSStatus err; - err = HIViewSetNeedsDisplayInRegion( reinterpret_cast<HIViewRef>( id ), region, true ); - assert( err == noErr ); + HIViewSetNeedsDisplayInRegion( reinterpret_cast<HIViewRef>( id ), region, true ); DisposeRgn( region ); } } @@ -1041,6 +973,8 @@ void Window::SetCursor(Cursor curs) { cursor = kThemeResizeLeftRightCursor; break; case cursorVert: + cursor = kThemeResizeUpDownCursor; + break; case cursorReverseArrow: case cursorUp: default: @@ -1148,7 +1082,7 @@ private: int lineHeight; bool unicodeMode; int desiredVisibleRows; - unsigned int maxItemCharacters; + unsigned int maxItemWidth; unsigned int aveCharWidth; Font font; int maxWidth; @@ -1170,7 +1104,7 @@ public: void *doubleClickActionData; ListBoxImpl() : lb(NULL), lineHeight(10), unicodeMode(false), - desiredVisibleRows(5), maxItemCharacters(0), aveCharWidth(8), + desiredVisibleRows(5), maxItemWidth(0), aveCharWidth(8), doubleClickAction(NULL), doubleClickActionData(NULL) { if (kScrollBarWidth == 0) @@ -1223,7 +1157,7 @@ void ListBoxImpl::Create(Window &/*parent*/, int /*ctrlID*/, Scintilla::Point /* int lineHeight_, bool unicodeMode_) { lineHeight = lineHeight_; unicodeMode = unicodeMode_; - maxWidth = 1000; + maxWidth = 2000; WindowClass windowClass = kHelpWindowClass; WindowAttributes attributes = kWindowNoAttributes; @@ -1501,13 +1435,14 @@ void ListBoxImpl::SetFont(Font &font_) { #endif // !@&^#%$ we cant copy Font, but we need one for our custom drawing - Str255 fontName; - if (FMGetFontFamilyName(style.font, fontName) != kFMInvalidFontFamilyErr) { - // make sure we conver to a proper C string first. - char cFontName[255]; - CopyPascalStringToC(fontName, cFontName); - font.Create((const char *)cFontName, 0, style.size, false, false); - } + Str255 fontName255; + char fontName[256]; + FMGetFontFamilyName(style.font, fontName255); + + CFStringRef fontNameCF = ::CFStringCreateWithPascalString( kCFAllocatorDefault, fontName255, kCFStringEncodingMacRoman ); + ::CFStringGetCString( fontNameCF, fontName, (CFIndex)255, kCFStringEncodingMacRoman ); + + font.Create((const char *)fontName, 0, style.size, false, false); } void ListBoxImpl::SetAverageCharWidth(int width) { @@ -1545,17 +1480,14 @@ PRectangle ListBoxImpl::GetDesiredRect() { rows = desiredVisibleRows; rcDesired.bottom = itemHeight * rows; - int width = maxItemCharacters; - if (width < 12) - width = 12; - // XXX use the quartz text stuff to figure out the correct string width - rcDesired.right = rcDesired.left + width * (aveCharWidth+aveCharWidth/4); - if (rcDesired.right > maxWidth) { - rcDesired.right = maxWidth; - } - if (Length() > rows) + rcDesired.right = rcDesired.left + maxItemWidth + aveCharWidth; + + if (Length() > rows) rcDesired.right += kScrollBarWidth; rcDesired.right += IconWidth(); + + // Set the column width + ::SetDataBrowserTableViewColumnWidth (lb, UInt16 (rcDesired.right - rcDesired.left)); return rcDesired; } @@ -1578,7 +1510,7 @@ int ListBoxImpl::CaretFromEdge() { void ListBoxImpl::Clear() { // passing NULL to "items" arg 4 clears the list - maxItemCharacters = 0; + maxItemWidth = 0; ld.Clear(); AddDataBrowserItems (lb, kDataBrowserNoItem, 0, NULL, kDataBrowserItemNoProperty); } @@ -1588,15 +1520,15 @@ void ListBoxImpl::Append(char *s, int type) { CFStringRef r = CFStringCreateWithCString(NULL, s, kTextEncodingMacRoman); ld.Add(count, type, r); + Scintilla::SurfaceImpl surface; + unsigned int width = surface.WidthText (font, s, strlen (s)); + if (width > maxItemWidth) + maxItemWidth = width; + DataBrowserItemID items[1]; items[0] = count + 1; AddDataBrowserItems (lb, kDataBrowserNoItem, 1, items, kDataBrowserItemNoProperty); ShowHideScrollbar(); - - size_t len = strlen(s); - if (maxItemCharacters < len) - maxItemCharacters = len; - } void ListBoxImpl::SetList(const char* list, char separator, char typesep) { @@ -1721,7 +1653,6 @@ void Menu::CreatePopUp() { Destroy(); OSStatus err; err = CreateNewMenu( nextMenuID++, 0, reinterpret_cast<MenuRef*>( &id ) ); - assert( noErr == err && id != NULL ); } void Menu::Destroy() { @@ -1746,8 +1677,6 @@ void Menu::Show(Point pt, Window &) { &menuId, &menuItem ); - // The system should always handle the command for us, so we should get userCanceledErr - assert( /*noErr == err ||*/ userCanceledErr == err ); } // TODO: Consider if I should be using GetCurrentEventTime instead of gettimeoday @@ -1755,7 +1684,6 @@ ElapsedTime::ElapsedTime() { struct timeval curTime; int retVal; retVal = gettimeofday( &curTime, NULL ); - assert( retVal == 0 ); bigBit = curTime.tv_sec; littleBit = curTime.tv_usec; @@ -1765,7 +1693,6 @@ double ElapsedTime::Duration(bool reset) { struct timeval curTime; int retVal; retVal = gettimeofday( &curTime, NULL ); - assert( retVal == 0 ); long endBigBit = curTime.tv_sec; long endLittleBit = curTime.tv_usec; double result = 1000000.0 * (endBigBit - bigBit); @@ -1794,7 +1721,11 @@ static Str255 PlatformDefaultFontName; const char *Platform::DefaultFont() { long id = HighShortFromLong(GetScriptVariable(smCurrentScript, smScriptAppFondSize)); FMGetFontFamilyName(id, PlatformDefaultFontName); - return (const char *)PlatformDefaultFontName; + char* defaultFontName = (char*) PlatformDefaultFontName; + defaultFontName[defaultFontName[0]+1] = 0; + ++defaultFontName; + + return defaultFontName; } int Platform::DefaultFontSize() { @@ -1836,17 +1767,17 @@ long Platform::SendScintilla(WindowID w, unsigned int msg, unsigned long wParam, } bool Platform::IsDBCSLeadByte(int /*codePage*/, char /*ch*/) { - // TODO: Implement this + // TODO: Implement this for code pages != UTF-8 return false; } int Platform::DBCSCharLength(int /*codePage*/, const char* /*s*/) { - // TODO: Implement this + // TODO: Implement this for code pages != UTF-8 return 1; } int Platform::DBCSCharMaxLength() { - // TODO: Implement this + // TODO: Implement this for code pages != UTF-8 //return CFStringGetMaximumSizeForEncoding( 1, CFStringEncoding encoding ); return 2; } @@ -1906,11 +1837,13 @@ void Platform::Assert(const char *c, const char *file, int line) { sprintf(buffer, "Assertion [%s] failed at %s %d", c, file, line); strcat(buffer, "\r\n"); Platform::DebugDisplay(buffer); - abort(); +#ifdef DEBUG + // Jump into debugger in assert on Mac (CL269835) + ::Debugger(); +#endif } int Platform::Clamp(int val, int minVal, int maxVal) { - assert( minVal <= maxVal ); if (val > maxVal) val = maxVal; if (val < minVal) diff --git a/macosx/PlatMacOSX.h b/macosx/PlatMacOSX.h index 168ba74ee..58e1bc6d7 100644 --- a/macosx/PlatMacOSX.h +++ b/macosx/PlatMacOSX.h @@ -23,12 +23,11 @@ private: float x; float y; -#ifdef SUPPORT_PORT - CGrafPtr port; -#endif CGContextRef gc; + /** The text layout instance */ + QuartzTextLayout* textLayout; /** If the surface is a bitmap context, contains a reference to the bitmap data. */ uint8_t* bitmapData; /** If the surface is a bitmap context, stores the dimensions of the bitmap. */ @@ -74,7 +73,6 @@ public: void Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back); void Copy(PRectangle rc, Scintilla::Point from, Surface &surfaceSource); - QuartzTextLayout* GetTextLayout( Font &font_, const char *s, int len ); void DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back); void DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back); void DrawTextTransparent(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore); diff --git a/macosx/QuartzTextLayout.h b/macosx/QuartzTextLayout.h index 1461a078f..d65d45290 100644 --- a/macosx/QuartzTextLayout.h +++ b/macosx/QuartzTextLayout.h @@ -1,9 +1,10 @@ /* * QuartzTextLayout.h - * wtf * - * Created by Evan Jones on Wed Oct 02 2002. - * Copyright (c) 2002 __MyCompanyName__. All rights reserved. + * Original Code by Evan Jones on Wed Oct 02 2002. + * Contributors: + * Shane Caraveo, ActiveState + * Bernd Paradies, Adobe * */ @@ -20,26 +21,23 @@ public: /** Create a text layout for drawing on the specified context. */ QuartzTextLayout( CGContextRef context ) : layout( NULL ), unicode_string( NULL ), unicode_length( 0 ) { - gc = context; - - OSStatus err; - err = ATSUCreateTextLayout( &layout ); - assert( err == noErr && layout != NULL ); + OSStatus err = ATSUCreateTextLayout( &layout ); + if (0 != err) + layout = NULL; - setControl( kATSUCGContextTag, sizeof( gc ), &gc ); + setContext(context); - /*ATSUAttributeTag tag = kATSULineLayoutOptionsTag; + ATSUAttributeTag tag = kATSULineLayoutOptionsTag; ByteCount size = sizeof( ATSLineLayoutOptions ); - ATSLineLayoutOptions rendering = kATSLineHasNoOpticalAlignment; // kATSLineUseDeviceMetrics; | kATSLineFractDisable | kATSLineUseQDRendering + ATSLineLayoutOptions rendering = kATSLineUseDeviceMetrics; //| kATSLineFractDisable | kATSLineUseQDRendering ATSUAttributeValuePtr valuePtr = &rendering; err = ATSUSetLayoutControls( layout, 1, &tag, &size, &valuePtr ); - assert( err == noErr );*/ } ~QuartzTextLayout() { - assert( layout != NULL ); - ATSUDisposeTextLayout( layout ); + if (NULL != layout) + ATSUDisposeTextLayout( layout ); layout = NULL; if ( unicode_string != NULL ) @@ -55,13 +53,15 @@ public: // TODO: Optimise the ASCII version by not copying so much OSStatus setText( const UInt8* buffer, size_t byteLength, CFStringEncoding encoding ) { - assert( buffer != NULL && byteLength > 0 ); - + if (NULL == layout) + return -1; CFStringRef str = CFStringCreateWithBytes( NULL, buffer, byteLength, encoding, false ); if (!str) return -1; unicode_length = CFStringGetLength( str ); + if (unicode_string) + delete[] unicode_string; unicode_string = new UniChar[ unicode_length ]; CFStringGetCharacters( str, CFRangeMake( 0, unicode_length ), unicode_string ); @@ -76,12 +76,16 @@ public: return ATSUSetTransientFontMatching( layout, true ); } + inline void setText( const UInt8* buffer, size_t byteLength, const QuartzTextStyle& r ) + { + this->setText( buffer, byteLength, kCFStringEncodingUTF8 ); + ATSUSetRunStyle( layout, r.getATSUStyle(), 0, unicode_length ); + } + /** Apply the specified text style on the entire range of text. */ void setStyle( const QuartzTextStyle& style ) { - OSStatus err; - err = ATSUSetRunStyle( layout, style.getATSUStyle(), kATSUFromTextBeginning, kATSUToTextEnd ); - assert( err == noErr ); + ATSUSetRunStyle( layout, style.getATSUStyle(), kATSUFromTextBeginning, kATSUToTextEnd ); } /** Draw the text layout into the current CGContext at the specified position, flipping the CGContext's Y axis if required. @@ -90,6 +94,8 @@ public: * @param flipTextYAxis If true, the CGContext's Y axis will be flipped before drawing the text, and restored afterwards. Use this when drawing in an HIView's CGContext, where the origin is the top left corner. */ void draw( float x, float y, bool flipTextYAxis = false ) { + if (NULL == layout || 0 == unicode_length) + return; if ( flipTextYAxis ) { CGContextSaveGState( gc ); @@ -99,7 +105,6 @@ public: OSStatus err; err = ATSUDrawText( layout, kATSUFromTextBeginning, kATSUToTextEnd, X2Fix( x ), X2Fix( y ) ); - assert( err == noErr ); if ( flipTextYAxis ) CGContextRestoreGState( gc ); } @@ -111,15 +116,21 @@ public: */ void setControl( ATSUAttributeTag tag, ByteCount size, ATSUAttributeValuePtr value ) { - OSStatus err; - err = ATSUSetLayoutControls( layout, 1, &tag, &size, &value ); - assert( noErr == err ); + ATSUSetLayoutControls( layout, 1, &tag, &size, &value ); } ATSUTextLayout getLayout() { return layout; } + inline CFIndex getLength() const { return unicode_length; } + inline void setContext (CGContextRef context) + { + gc = context; + if (NULL != layout) + setControl( kATSUCGContextTag, sizeof( gc ), &gc ); + } + private: ATSUTextLayout layout; UniChar* unicode_string; diff --git a/macosx/QuartzTextStyle.h b/macosx/QuartzTextStyle.h index d3a5db5dc..80c06b76e 100644 --- a/macosx/QuartzTextStyle.h +++ b/macosx/QuartzTextStyle.h @@ -19,23 +19,19 @@ class QuartzTextStyle public: QuartzTextStyle() { - OSStatus err; - err = ATSUCreateStyle( &style ); - assert( err == noErr ); + ATSUCreateStyle( &style ); } ~QuartzTextStyle() { - assert( style != NULL ); - ATSUDisposeStyle( style ); + if ( style != NULL ) + ATSUDisposeStyle( style ); style = NULL; } void setAttribute( ATSUAttributeTag tag, ByteCount size, ATSUAttributeValuePtr value ) { - OSStatus err; - err = ATSUSetAttributes( style, 1, &tag, &size, &value ); - assert( err == noErr ); + ATSUSetAttributes( style, 1, &tag, &size, &value ); } void setAttribute( QuartzTextStyleAttribute& attribute ) @@ -45,8 +41,7 @@ public: void getAttribute( ATSUAttributeTag tag, ByteCount size, ATSUAttributeValuePtr value, ByteCount* actualSize ) { - OSStatus err; err = ATSUGetAttribute( style, tag, size, value, actualSize ); - assert( err == noErr ); + ATSUGetAttribute( style, tag, size, value, actualSize ); } template <class T> @@ -54,9 +49,7 @@ public: { T value; ByteCount actualSize; - OSStatus err; - err = ATSUGetAttribute( style, tag, sizeof( T ), &value, &actualSize ); - assert( (err == noErr || err == kATSUNotSetErr) && actualSize == sizeof( T ) ); + ATSUGetAttribute( style, tag, sizeof( T ), &value, &actualSize ); return value; } @@ -75,9 +68,7 @@ public: values[i] = attributes[i]->getValuePtr(); } - OSStatus err; - err = ATSUSetAttributes( style, number, tags, sizes, values ); - //assert( err == noErr ); + ATSUSetAttributes( style, number, tags, sizes, values ); // Free the arrays that were allocated delete[] tags; @@ -87,9 +78,7 @@ public: void setFontFeature( ATSUFontFeatureType featureType, ATSUFontFeatureSelector selector ) { - OSStatus err; - err = ATSUSetFontFeatures( style, 1, &featureType, &selector ); - assert( err == noErr ); + ATSUSetFontFeatures( style, 1, &featureType, &selector ); } const ATSUStyle& getATSUStyle() const diff --git a/macosx/QuartzTextStyleAttribute.h b/macosx/QuartzTextStyleAttribute.h index 490ef7d02..89b43721e 100644 --- a/macosx/QuartzTextStyleAttribute.h +++ b/macosx/QuartzTextStyleAttribute.h @@ -1,9 +1,10 @@ /* * QuartzTextStyleAttribute.h - * wtf * - * Created by Evan Jones on Wed Oct 02 2002. - * Copyright (c) 2002 __MyCompanyName__. All rights reserved. + * Original Code by Evan Jones on Wed Oct 02 2002. + * Contributors: + * Shane Caraveo, ActiveState + * Bernd Paradies, Adobe * */ @@ -16,6 +17,8 @@ class QuartzTextStyleAttribute { public: + QuartzTextStyleAttribute() {} + virtual ~QuartzTextStyleAttribute() {} virtual ByteCount getSize() const = 0; virtual ATSUAttributeValuePtr getValuePtr() = 0; virtual ATSUAttributeTag getTag() const = 0; @@ -104,17 +107,12 @@ public: QuartzFont( const char* name, int length ) { assert( name != NULL && length > 0 && name[length] == '\0' ); - /*CFStringRef fontName = CFStringCreateWithCString( NULL, name, kCFStringEncodingMacRoman ); - - ATSFontRef fontRef = ATSFontFindFromName( fontName, kATSOptionFlagsDefault ); - assert( fontRef != NULL ); - fontid = fontRef; + // try to create font + OSStatus err = ATSUFindFontFromName( const_cast<char*>( name ), length, kFontFullName, (unsigned) kFontNoPlatform, kFontRomanScript, (unsigned) kFontNoLanguage, &fontid ); - CFRelease( fontName );*/ - - OSStatus err; - err = ATSUFindFontFromName( const_cast<char*>( name ), length, kFontFullName, (unsigned) kFontNoPlatform, kFontRomanScript, (unsigned) kFontNoLanguage, &fontid ); - //assert( err == noErr && fontid != kATSUInvalidFontID ); + // need a fallback if font isn't installed + if( err != noErr || fontid == kATSUInvalidFontID ) + ::ATSUFindFontFromName( "Lucida Grande", 13, kFontFullName, (unsigned) kFontNoPlatform, kFontRomanScript, (unsigned) kFontNoLanguage, &fontid ); } ByteCount getSize() const diff --git a/macosx/ScintillaMacOSX.cxx b/macosx/ScintillaMacOSX.cxx index 9ac746f4b..410d105d9 100644 --- a/macosx/ScintillaMacOSX.cxx +++ b/macosx/ScintillaMacOSX.cxx @@ -6,7 +6,12 @@ #include "ScintillaMacOSX.h" +#ifdef EXT_INPUT +// External Input Editor +#include "ExtInput.h" +#else #include "UniConversion.h" +#endif using namespace Scintilla; @@ -59,9 +64,103 @@ static int BOMlen(unsigned char *cstr) { return 0; } +#ifdef EXT_INPUT +#define SCI_CMD ( SCI_ALT | SCI_CTRL | SCI_SHIFT) + +static const KeyToCommand macMapDefault[] = { + {SCK_DOWN, SCI_CMD, SCI_DOCUMENTEND}, + {SCK_UP, SCI_CMD, SCI_DOCUMENTSTART}, + {SCK_LEFT, SCI_CMD, SCI_VCHOME}, + {SCK_RIGHT, SCI_CMD, SCI_LINEEND}, + {SCK_DOWN, SCI_NORM, SCI_LINEDOWN}, + {SCK_DOWN, SCI_SHIFT, SCI_LINEDOWNEXTEND}, + {SCK_DOWN, SCI_CTRL, SCI_LINESCROLLDOWN}, + {SCK_DOWN, SCI_ASHIFT, SCI_LINEDOWNRECTEXTEND}, + {SCK_UP, SCI_NORM, SCI_LINEUP}, + {SCK_UP, SCI_SHIFT, SCI_LINEUPEXTEND}, + {SCK_UP, SCI_CTRL, SCI_LINESCROLLUP}, + {SCK_UP, SCI_ASHIFT, SCI_LINEUPRECTEXTEND}, + {'[', SCI_CTRL, SCI_PARAUP}, + {'[', SCI_CSHIFT, SCI_PARAUPEXTEND}, + {']', SCI_CTRL, SCI_PARADOWN}, + {']', SCI_CSHIFT, SCI_PARADOWNEXTEND}, + {SCK_LEFT, SCI_NORM, SCI_CHARLEFT}, + {SCK_LEFT, SCI_SHIFT, SCI_CHARLEFTEXTEND}, + {SCK_LEFT, SCI_ALT, SCI_WORDLEFT}, + {SCK_LEFT, SCI_CSHIFT, SCI_WORDLEFTEXTEND}, + {SCK_LEFT, SCI_ASHIFT, SCI_CHARLEFTRECTEXTEND}, + {SCK_RIGHT, SCI_NORM, SCI_CHARRIGHT}, + {SCK_RIGHT, SCI_SHIFT, SCI_CHARRIGHTEXTEND}, + {SCK_RIGHT, SCI_ALT, SCI_WORDRIGHT}, + {SCK_RIGHT, SCI_CSHIFT, SCI_WORDRIGHTEXTEND}, + {SCK_RIGHT, SCI_ASHIFT, SCI_CHARRIGHTRECTEXTEND}, + {'/', SCI_CTRL, SCI_WORDPARTLEFT}, + {'/', SCI_CSHIFT, SCI_WORDPARTLEFTEXTEND}, + {'\\', SCI_CTRL, SCI_WORDPARTRIGHT}, + {'\\', SCI_CSHIFT, SCI_WORDPARTRIGHTEXTEND}, + {SCK_HOME, SCI_NORM, SCI_VCHOME}, + {SCK_HOME, SCI_SHIFT, SCI_VCHOMEEXTEND}, + {SCK_HOME, SCI_CTRL, SCI_DOCUMENTSTART}, + {SCK_HOME, SCI_CSHIFT, SCI_DOCUMENTSTARTEXTEND}, + {SCK_HOME, SCI_ALT, SCI_HOMEDISPLAY}, +// {SCK_HOME, SCI_ASHIFT, SCI_HOMEDISPLAYEXTEND}, + {SCK_HOME, SCI_ASHIFT, SCI_VCHOMERECTEXTEND}, + {SCK_END, SCI_NORM, SCI_LINEEND}, + {SCK_END, SCI_SHIFT, SCI_LINEENDEXTEND}, + {SCK_END, SCI_CTRL, SCI_DOCUMENTEND}, + {SCK_END, SCI_CSHIFT, SCI_DOCUMENTENDEXTEND}, + {SCK_END, SCI_ALT, SCI_LINEENDDISPLAY}, +// {SCK_END, SCI_ASHIFT, SCI_LINEENDDISPLAYEXTEND}, + {SCK_END, SCI_ASHIFT, SCI_LINEENDRECTEXTEND}, + {SCK_PRIOR, SCI_NORM, SCI_PAGEUP}, + {SCK_PRIOR, SCI_SHIFT, SCI_PAGEUPEXTEND}, + {SCK_PRIOR, SCI_ASHIFT, SCI_PAGEUPRECTEXTEND}, + {SCK_NEXT, SCI_NORM, SCI_PAGEDOWN}, + {SCK_NEXT, SCI_SHIFT, SCI_PAGEDOWNEXTEND}, + {SCK_NEXT, SCI_ASHIFT, SCI_PAGEDOWNRECTEXTEND}, + {SCK_DELETE, SCI_NORM, SCI_CLEAR}, + {SCK_DELETE, SCI_SHIFT, SCI_CUT}, + {SCK_DELETE, SCI_CTRL, SCI_DELWORDRIGHT}, + {SCK_DELETE, SCI_CSHIFT, SCI_DELLINERIGHT}, + {SCK_INSERT, SCI_NORM, SCI_EDITTOGGLEOVERTYPE}, + {SCK_INSERT, SCI_SHIFT, SCI_PASTE}, + {SCK_INSERT, SCI_CTRL, SCI_COPY}, + {SCK_ESCAPE, SCI_NORM, SCI_CANCEL}, + {SCK_BACK, SCI_NORM, SCI_DELETEBACK}, + {SCK_BACK, SCI_SHIFT, SCI_DELETEBACK}, + {SCK_BACK, SCI_CTRL, SCI_DELWORDLEFT}, + {SCK_BACK, SCI_ALT, SCI_UNDO}, + {SCK_BACK, SCI_CSHIFT, SCI_DELLINELEFT}, + {'Z', SCI_CTRL, SCI_UNDO}, + {'Y', SCI_CTRL, SCI_REDO}, + {'X', SCI_CTRL, SCI_CUT}, + {'C', SCI_CTRL, SCI_COPY}, + {'V', SCI_CTRL, SCI_PASTE}, + {'A', SCI_CTRL, SCI_SELECTALL}, + {SCK_TAB, SCI_NORM, SCI_TAB}, + {SCK_TAB, SCI_SHIFT, SCI_BACKTAB}, + {SCK_RETURN, SCI_NORM, SCI_NEWLINE}, + {SCK_RETURN, SCI_SHIFT, SCI_NEWLINE}, + {SCK_ADD, SCI_CTRL, SCI_ZOOMIN}, + {SCK_SUBTRACT, SCI_CTRL, SCI_ZOOMOUT}, + {SCK_DIVIDE, SCI_CTRL, SCI_SETZOOM}, + //'L', SCI_CTRL, SCI_FORMFEED, + {'L', SCI_CTRL, SCI_LINECUT}, + {'L', SCI_CSHIFT, SCI_LINEDELETE}, + {'T', SCI_CSHIFT, SCI_LINECOPY}, + {'T', SCI_CTRL, SCI_LINETRANSPOSE}, + {'D', SCI_CTRL, SCI_SELECTIONDUPLICATE}, + {'U', SCI_CTRL, SCI_LOWERCASE}, + {'U', SCI_CSHIFT, SCI_UPPERCASE}, + {0,0,0}, +}; +#endif + ScintillaMacOSX::ScintillaMacOSX( void* windowid ) : TView( reinterpret_cast<HIViewRef>( windowid ) ) { + notifyObj = NULL; + notifyProc = NULL; wMain = windowid; OSStatus err; err = GetThemeMetric( kThemeMetricScrollBarWidth, &scrollBarFixedSize ); @@ -123,7 +222,13 @@ ScintillaMacOSX::ScintillaMacOSX( void* windowid ) : GetEventTypeCount( commandEventInfo ), commandEventInfo, this, NULL); - assert( err == noErr ); +#ifdef EXT_INPUT + ExtInput::attach (GetViewRef()); + for (int i = 0; macMapDefault[i].key; i++) + { + this->kmap.AssignCmdKey(macMapDefault[i].key, macMapDefault[i].modifiers, macMapDefault[i].msg); + } +#endif } ScintillaMacOSX::~ScintillaMacOSX() { @@ -134,6 +239,9 @@ ScintillaMacOSX::~ScintillaMacOSX() { } mouseTrackingRef = NULL; SetTicking(false); +#ifdef EXT_INPUT + ExtInput::detach (GetViewRef()); +#endif } void ScintillaMacOSX::Initialise() { @@ -226,43 +334,12 @@ HIPoint ScintillaMacOSX::GetLocalPoint(::Point pt) } void ScintillaMacOSX::StartDrag() { -#define DRAG_DROP_PASTEBOARD if (currentPos == anchor) return; - SelectionText selectedText; - CopySelectionRange(&selectedText); - - // some of this taken from copytoclipboard - if (selectedText.len == 0) - return; - - CFStringEncoding encoding = ( IsUnicodeMode() ? kCFStringEncodingUTF8 : kCFStringEncodingASCII); - - // Create a CFString from the ASCII/UTF8 data, convert it to UTF16 - CFStringRef string = CFStringCreateWithBytes( NULL, reinterpret_cast<UInt8*>( selectedText.s ), selectedText.len - 1, encoding, false ); - assert( string != NULL ); - -#ifndef DRAG_DROP_PASTEBOARD - CFIndex numUniChars = CFStringGetLength( string ); - UniChar* buffer = new UniChar[ numUniChars ]; - CFStringGetCharacters( string, CFRangeMake( 0, numUniChars ), buffer ); - - // Create an c string byte buffer - CFIndex maximumByteLength = CFStringGetMaximumSizeForEncoding( numUniChars, encoding ) + 1; - char* cstring = new char[maximumByteLength]; - CFIndex usedBufferLength = 0; - CFIndex numCharsConverted; - numCharsConverted = CFStringGetBytes( string, CFRangeMake( 0, numUniChars ), encoding, - '?', false, reinterpret_cast<UInt8*>( cstring ), - maximumByteLength, &usedBufferLength ); - cstring[usedBufferLength] = '\0'; // null terminate the ASCII/UTF8 string - assert( numCharsConverted == numUniChars ); -#endif - // calculate the bounds of the selection PRectangle client = GetTextRectangle(); - int selStart = Platform::Minimum(anchor, currentPos); - int selEnd = Platform::Maximum(anchor, currentPos); + int selStart = SelectionStart(); + int selEnd = SelectionEnd(); int startLine = pdoc->LineFromPosition(selStart); int endLine = pdoc->LineFromPosition(selEnd); Point pt; @@ -318,7 +395,7 @@ void ScintillaMacOSX::StartDrag() { client = GetClientRectangle(); paintState = painting; sw->InitPixMap( client.Width(), client.Height(), NULL, NULL ); - + paintingAllText = true; Paint(sw, imageRect); paintState = notPainting; @@ -354,28 +431,12 @@ void ScintillaMacOSX::StartDrag() { RectRgn(dragRegion, &rcSel); -#ifdef DRAG_DROP_PASTEBOARD + SelectionText selectedText; + CopySelectionRange(&selectedText); PasteboardRef theClipboard; - PasteboardCreate( kPasteboardClipboard, &theClipboard ); - PasteboardClear( theClipboard ); - - CFDataRef data = NULL; - data = CFStringCreateExternalRepresentation ( kCFAllocatorDefault, string, kCFStringEncodingMacRoman, 0 ); - PasteboardPutItemFlavor( theClipboard, (PasteboardItemID)1, - CFSTR("com.apple.traditional-mac-plain-text"), - data, 0 ); - CFRelease(data); - data = CFStringCreateExternalRepresentation ( kCFAllocatorDefault, string, kCFStringEncodingUnicode, 0 ); - PasteboardPutItemFlavor( theClipboard, (PasteboardItemID)1, - CFSTR("public.utf16-plain-text"), - data, 0 ); - CFRelease(data); + SetPasteboardData(theClipboard, selectedText); NewDragWithPasteboard( theClipboard, &inDrag); -#else - NewDrag(&inDrag); - AddDragItemFlavor(inDrag, 1, 'utxt', buffer, sizeof( UniChar ) * numUniChars, 0); - AddDragItemFlavor(inDrag, 1, 'txt', cstring, sizeof( char ) * usedBufferLength, 0); -#endif + CFRelease( theClipboard ); // Set the item's bounding rectangle in global coordinates. SetDragItemBounds(inDrag, 1, &rcSel); @@ -394,9 +455,9 @@ void ScintillaMacOSX::StartDrag() { } // Drag the text. TrackDrag will return userCanceledErr if the drop whooshed back for any reason. - inDragSession = true; + inDragDrop = ddDragging; OSErr error = TrackDrag(inDrag, &mouseDownEvent, dragRegion); - inDragSession = false; + inDragDrop = ddNone; // Check to see if the drop occurred in the Finder's Trash. If the drop occurred // in the Finder's Trash and a copy operation wasn't specified, delete the @@ -424,23 +485,12 @@ void ScintillaMacOSX::StartDrag() { // Dispose of this drag, 'cause we're done. DisposeDrag(inDrag); DisposeRgn(dragRegion); - CFRelease( string ); if (pixmap) { CGImageRelease(image); pixmap->Release(); delete pixmap; } - - // Done with the UniChar* buffer -#ifdef DRAG_DROP_PASTEBOARD - CFRelease( theClipboard ); -#else - delete[] buffer; - buffer = NULL; - delete[] cstring; - cstring = NULL; -#endif } void ScintillaMacOSX::SetDragCursor(DragRef inDrag) @@ -504,8 +554,7 @@ Scintilla::Point ScintillaMacOSX::GetDragPoint(DragRef inDrag) { ::Point mouse, globalMouse; GetDragMouse(inDrag, &mouse, &globalMouse); - QDGlobalToLocalPoint(GetWindowPort(GetOwner()), &globalMouse); - HIPoint hiPoint = {globalMouse.h, globalMouse.v}; + HIPoint hiPoint = GetLocalPoint (globalMouse); return Point(static_cast<int>(hiPoint.x), static_cast<int>(hiPoint.y)); } @@ -548,22 +597,13 @@ void ScintillaMacOSX::DragScroll() bool ScintillaMacOSX::DragWithin(DragRef inDrag ) { PasteboardRef pasteBoard; - OSStatus status = GetDragData(inDrag, pasteBoard, NULL); - if (status != noErr) { - return false; - } - - ::Point mouse, globalMouse; - GetDragMouse(inDrag, &mouse, &globalMouse); - QDGlobalToLocalPoint(GetWindowPort(GetOwner()), &globalMouse); - HIPoint globalHit = {globalMouse.h, globalMouse.v}; - // HIPoint localHit = {mouse.h, mouse.v}; - - if (!CGRectContainsPoint( Bounds(), globalHit )) { + bool isFileURL = false; + if (!GetDragData(inDrag, pasteBoard, NULL, &isFileURL)) { return false; } - SetDragPosition(PositionFromLocation(Point(static_cast<int>(globalHit.x),static_cast<int>(globalHit.y)))); + Point pt = GetDragPoint (inDrag); + SetDragPosition(PositionFromLocation(pt)); SetDragCursor(inDrag); return true; @@ -577,21 +617,84 @@ bool ScintillaMacOSX::DragLeave(DragRef inDrag ) return true; } -OSStatus ScintillaMacOSX::GetDragData(DragRef inDrag, PasteboardRef &pasteBoard, CFStringRef *textString) +enum +{ + kFormatBad, + kFormatText, + kFormatUnicode, + kFormatUTF8, + kFormatFile +}; + +bool ScintillaMacOSX::GetDragData(DragRef inDrag, PasteboardRef &pasteBoard, + SelectionText *selectedText, bool *isFileURL) { // TODO: add support for special flavors: flavorTypeHFS and flavorTypePromiseHFS so we // can handle files being dropped on the editor OSStatus status; status = GetDragPasteboard(inDrag, &pasteBoard); if (status != noErr) { - return dragNotAcceptedErr; + return false; + } + return GetPasteboardData(pasteBoard, selectedText, isFileURL); +} + +void ScintillaMacOSX::SetPasteboardData(PasteboardRef &theClipboard, const SelectionText &selectedText) +{ + if (selectedText.len == 0) + return; + + CFStringEncoding encoding = ( IsUnicodeMode() ? kCFStringEncodingUTF8 : kCFStringEncodingMacRoman); + + // Create a CFString from the ASCII/UTF8 data, convert it to UTF16 + CFStringRef string = CFStringCreateWithBytes( NULL, reinterpret_cast<UInt8*>( selectedText.s ), selectedText.len - 1, encoding, false ); + + PasteboardCreate( kPasteboardClipboard, &theClipboard ); + PasteboardClear( theClipboard ); + + CFDataRef data = NULL; + if (selectedText.rectangular) { + // This is specific to scintilla, allows us to drag rectangular selections + // around the document + data = CFStringCreateExternalRepresentation ( kCFAllocatorDefault, string, kCFStringEncodingUnicode, 0 ); + if (data) { + PasteboardPutItemFlavor( theClipboard, (PasteboardItemID)1, + CFSTR("com.scintilla.utf16-plain-text.rectangular"), + data, 0 ); + CFRelease(data); + } } + data = CFStringCreateExternalRepresentation ( kCFAllocatorDefault, string, kCFStringEncodingUnicode, 0 ); + if (data) { + PasteboardPutItemFlavor( theClipboard, (PasteboardItemID)1, + CFSTR("public.utf16-plain-text"), + data, 0 ); + CFRelease(data); + data = NULL; + } + data = CFStringCreateExternalRepresentation ( kCFAllocatorDefault, string, kCFStringEncodingMacRoman, 0 ); + if (data) { + PasteboardPutItemFlavor( theClipboard, (PasteboardItemID)1, + CFSTR("com.apple.traditional-mac-plain-text"), + data, 0 ); + CFRelease(data); + data = NULL; + } + CFRelease(string); +} +bool ScintillaMacOSX::GetPasteboardData(PasteboardRef &pasteBoard, + SelectionText *selectedText, + bool *isFileURL) +{ // how many items in the pasteboard? + CFDataRef data; + CFStringRef textString = NULL; + bool isRectangular = selectedText ? selectedText->rectangular : false; ItemCount i, itemCount; - status = PasteboardGetItemCount(pasteBoard, &itemCount); + OSStatus status = PasteboardGetItemCount(pasteBoard, &itemCount); if (status != noErr) { - return dragNotAcceptedErr; + return false; } // as long as we didn't get our text, let's loop on the items. We stop as soon as we get it @@ -604,13 +707,13 @@ OSStatus ScintillaMacOSX::GetDragData(DragRef inDrag, PasteboardRef &pasteBoard, status = PasteboardGetItemIdentifier(pasteBoard, i, &itemID); if (status != noErr) { - return dragNotAcceptedErr; + return false; } // how many flavors in this item? status = PasteboardCopyItemFlavors(pasteBoard, itemID, &flavorTypeArray); if (status != noErr) { - return dragNotAcceptedErr; + return false; } if (flavorTypeArray != NULL) @@ -621,57 +724,83 @@ OSStatus ScintillaMacOSX::GetDragData(DragRef inDrag, PasteboardRef &pasteBoard, { CFDataRef flavorData; CFStringRef flavorType = (CFStringRef)CFArrayGetValueAtIndex(flavorTypeArray, j); - if (flavorType != NULL) { - if (UTTypeConformsTo(flavorType, CFSTR("public.utf16-plain-text"))) // this is 'utxt' + if (flavorType != NULL) + { + int format = kFormatBad; + if (UTTypeConformsTo(flavorType, CFSTR("public.file-url"))) { + format = kFormatFile; + *isFileURL = true; + } + else if (UTTypeConformsTo(flavorType, CFSTR("com.scintilla.utf16-plain-text.rectangular"))) { + format = kFormatUnicode; + isRectangular = true; + } + else if (UTTypeConformsTo(flavorType, CFSTR("public.utf16-plain-text"))) { // this is 'utxt' + format = kFormatUnicode; + } + else if (UTTypeConformsTo(flavorType, CFSTR("public.utf8-plain-text"))) { + format = kFormatUTF8; + } + else if (UTTypeConformsTo(flavorType, CFSTR("com.apple.traditional-mac-plain-text"))) { // this is 'TEXT' + format = kFormatText; + } + if (format == kFormatBad) + continue; + + // if we got a flavor match, and we have no textString, we just want + // to know that we can accept this data, so jump out now + if (selectedText == NULL) { + haveMatch = true; + goto PasteboardDataRetrieved; + } + if (PasteboardCopyItemFlavorData(pasteBoard, itemID, flavorType, &flavorData) == noErr) { - // if we got a flavor match, and we have no textString, we just want - // to know that we can accept this drag data, so jump out now - if (textString == NULL) { - haveMatch = true; - goto DragDataRetrieved; - } - if (PasteboardCopyItemFlavorData(pasteBoard, itemID, flavorType, &flavorData) == noErr) + CFIndex dataSize = CFDataGetLength (flavorData); + const UInt8* dataBytes = CFDataGetBytePtr (flavorData); + switch (format) { - CFIndex flavorDataSize = CFDataGetLength(flavorData); - - // getting the text - *textString = CFStringCreateWithCharacters(NULL, - (UniChar *)CFDataGetBytePtr(flavorData), - flavorDataSize >> 1); - CFRelease(flavorData); - goto DragDataRetrieved; + case kFormatFile: + case kFormatText: + data = CFDataCreate (NULL, dataBytes, dataSize); + textString = CFStringCreateFromExternalRepresentation (NULL, data, kCFStringEncodingMacRoman); + break; + case kFormatUnicode: + data = CFDataCreate (NULL, dataBytes, dataSize); + textString = CFStringCreateFromExternalRepresentation (NULL, data, kCFStringEncodingUnicode); + break; + case kFormatUTF8: + data = CFDataCreate (NULL, dataBytes, dataSize); + textString = CFStringCreateFromExternalRepresentation (NULL, data, kCFStringEncodingUTF8); + break; } + CFRelease (flavorData); + goto PasteboardDataRetrieved; } } } } -DragDataRetrieved: +PasteboardDataRetrieved: if (flavorTypeArray != NULL) CFRelease(flavorTypeArray); - if (haveMatch || textString != NULL && *textString != NULL) - return noErr; - return dragNotAcceptedErr; -} - -OSStatus ScintillaMacOSX::DragReceive(DragRef inDrag ) -{ - OSStatus status; - PasteboardRef pasteBoard; - CFStringRef textString = NULL; - status = GetDragData(inDrag, pasteBoard, &textString); - if (status != noErr) { - return dragNotAcceptedErr; + int newlen = 0; + if (textString != NULL) { + selectedText->s = GetStringFromCFString(textString, &selectedText->len); + selectedText->rectangular = isRectangular; + // Default allocator releases both the CFString and the UniChar buffer (text) + CFRelease( textString ); + textString = NULL; } - - // getting the length of the text and setting the value - if (textString == NULL) { - return noErr; + if (haveMatch || selectedText != NULL && selectedText->s != NULL) { + return true; } + return false; +} - // XXX the following is identical (ALMOST) to code in Paste +char *ScintillaMacOSX::GetStringFromCFString(CFStringRef &textString, int *textLen) +{ // Allocate a buffer, plus the null byte CFIndex numUniChars = CFStringGetLength( textString ); - CFStringEncoding encoding = ( IsUnicodeMode() ? kCFStringEncodingUTF8 : kCFStringEncodingASCII); + CFStringEncoding encoding = ( IsUnicodeMode() ? kCFStringEncodingUTF8 : kCFStringEncodingMacRoman); CFIndex maximumByteLength = CFStringGetMaximumSizeForEncoding( numUniChars, encoding ) + 1; char* cstring = new char[maximumByteLength]; CFIndex usedBufferLength = 0; @@ -680,84 +809,84 @@ OSStatus ScintillaMacOSX::DragReceive(DragRef inDrag ) '?', false, reinterpret_cast<UInt8*>( cstring ), maximumByteLength, &usedBufferLength ); cstring[usedBufferLength] = '\0'; // null terminate the ASCII/UTF8 string - assert( numCharsConverted == numUniChars ); - - // Default allocator releases both the CFString and the UniChar buffer (text) - CFRelease( textString ); - textString = NULL; // determine whether a BOM is in the string. Apps like Emacs prepends a BOM // to the string, CFStrinGetBytes reflects that (though it may change in the conversion) // so we need to remove it before pasting into our buffer. TextWrangler has no // problem dealing with BOM when pasting into it. int bomLen = BOMlen((unsigned char *)cstring); - + // convert line endings to the document line ending - int droppedLen = 0; - char *droppedText = Document::TransformLineEnds(&droppedLen, - cstring + bomLen, - usedBufferLength - bomLen, - pdoc->eolMode); - - pdoc->BeginUndoAction(); + *textLen = 0; + char *result = Document::TransformLineEnds(textLen, + cstring + bomLen, + usedBufferLength - bomLen, + pdoc->eolMode); + delete[] cstring; + return result; +} - // figure out if this is a move or a paste - DragAttributes attributes; - SInt16 modifiers = 0; - GetDragAttributes( inDrag, &attributes ); +OSStatus ScintillaMacOSX::DragReceive(DragRef inDrag ) +{ + // dragleave IS called, but for some reason (probably to do with inDrag) + // the hide hilite does not happen unless we do it here + HideDragHilite( inDrag ); - int position = PositionFromLocation(GetDragPoint(inDrag)); - int selStart = Platform::Minimum(anchor, currentPos); - int selEnd = Platform::Maximum(anchor, currentPos); - if ( attributes & kDragInsideSenderWindow ) { - if (position >= selStart && position <= selEnd) { - // droping on top of what we dragged, we should ignore this - goto endDrag; - } - GetDragModifiers(inDrag, NULL, NULL, &modifiers); - switch (modifiers & ~btnState) // Filter out btnState (on for drop) - { - case optionKey: - // default is copy text - break; - - case cmdKey: - case cmdKey | optionKey: - default: - // what to do with these? rectangular drag? - position = selStart; - ClearSelection(); - break; - } + PasteboardRef pasteBoard; + SelectionText selectedText; + CFStringRef textString = NULL; + bool isFileURL = false; + if (!GetDragData(inDrag, pasteBoard, &selectedText, &isFileURL)) { + return dragNotAcceptedErr; + } + + if (isFileURL) { + NotifyURIDropped(selectedText.s); } else { - if (position >= selStart && position <= selEnd) { - // droping on top of a selection from another app or control, clear it - position = selStart; - ClearSelection(); + // figure out if this is a move or a paste + DragAttributes attributes; + SInt16 modifiers = 0; + GetDragAttributes( inDrag, &attributes ); + bool moving = true; + + int position = PositionFromLocation(GetDragPoint(inDrag)); + if ( attributes & kDragInsideSenderWindow ) { + GetDragModifiers(inDrag, NULL, NULL, &modifiers); + switch (modifiers & ~btnState) // Filter out btnState (on for drop) + { + case optionKey: + // default is copy text + moving = false; + break; + case cmdKey: + case cmdKey | optionKey: + default: + // what to do with these? rectangular drag? + break; + } } - } - // lets put the text in our document now - if ( pdoc->InsertString( position, droppedText, droppedLen ) ) - { - SetEmptySelection( currentPos + droppedLen ); + DropAt(position, selectedText.s, moving, selectedText.rectangular); } -endDrag: - delete[] droppedText; - delete[] cstring; - cstring = NULL; - - pdoc->EndUndoAction(); - NotifyChange(); - - // dragleave IS called, but for some reason (probably to do with inDrag) - // the hide hilite does not happen unless we do it here - HideDragHilite( inDrag ); - return noErr; } +// Extended UTF8-UTF6-conversion to handle surrogate pairs correctly (CL265070) +void ScintillaMacOSX::InsertCharacters (const UniChar* buf, int len) +{ + CFStringRef str = CFStringCreateWithCharactersNoCopy (NULL, buf, (UInt32) len, kCFAllocatorNull); + CFStringEncoding encoding = ( IsUnicodeMode() ? kCFStringEncodingUTF8 : kCFStringEncodingMacRoman); + CFRange range = { 0, len }; + CFIndex bufLen; + CFStringGetBytes (str, range, encoding, '?', false, NULL, 0, &bufLen); + UInt8* utf8buf = new UInt8 [bufLen]; + CFStringGetBytes (str, range, encoding, '?', false, utf8buf, bufLen, NULL); + AddCharUTF ((char*) utf8buf, bufLen, false); + delete [] utf8buf; + CFRelease (str); +} + /** The simulated message loop. */ sptr_t ScintillaMacOSX::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { switch (iMessage) { @@ -774,11 +903,9 @@ sptr_t ScintillaMacOSX::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lPa break; case WM_UNICHAR: if (IsUnicodeMode()) { - char utfval[4]; - wchar_t wcs[2] = {wParam, 0}; - unsigned int len = UTF8Length(wcs, 1); - UTF8FromUTF16(wcs, 1, utfval, len); - AddCharUTF(utfval, len); + // Extended UTF8-UTF6-conversion to handle surrogate pairs correctly (CL265070) + UniChar wcs[1] = { (UniChar) wParam}; + InsertCharacters(wcs, 1); return 1; } else { return 0; @@ -892,11 +1019,11 @@ void ScintillaMacOSX::SyncPaint(void* gc, PRectangle rc) { sw->Init( gc, wMain.GetID() ); Paint(sw, rc); if (paintState == paintAbandoned) { - // XXX a bit of a hack to avoid excesive flashing when typing. + // do a FULL paint. + rcPaint = GetClientRectangle(); paintState = painting; paintingAllText = true; - Paint(sw, rc); - // TODO: There is a chance that this causes an infinite drawing loop... + Paint(sw, rcPaint); wMain.InvalidateAll(); } sw->Release(); @@ -1004,18 +1131,39 @@ void ScintillaMacOSX::Resize(int width, int height) { } ChangeSize(); -} -void ScintillaMacOSX::NotifyChange() { - // TODO: How should this be implemented on OS X? Should it be? + // fixup mouse tracking regions, this causes mouseenter/exit to work + if (HIViewGetSuperview(GetViewRef()) != NULL) { + RgnHandle rgn = NewRgn(); + HIRect r; + HIViewGetFrame( reinterpret_cast<HIViewRef>( GetViewRef() ), &r ); + SetRectRgn(rgn, short (r.origin.x), short (r.origin.y), + short (r.origin.x + r.size.width - (verticalScrollBarVisible ? scrollBarFixedSize : 0)), + short (r.origin.y + r.size.height - (showSBHorizontal ? scrollBarFixedSize : 0))); + if (mouseTrackingRef == NULL) { + CreateMouseTrackingRegion(GetOwner(), rgn, NULL, + kMouseTrackingOptionsLocalClip, + mouseTrackingID, NULL, + GetControlEventTarget( GetViewRef() ), + &mouseTrackingRef); + } else { + ChangeMouseTrackingRegion(mouseTrackingRef, rgn, NULL); + } + DisposeRgn(rgn); + } else { + if (mouseTrackingRef != NULL) { + ReleaseMouseTrackingRegion(mouseTrackingRef); + } + mouseTrackingRef = NULL; + } } pascal void ScintillaMacOSX::LiveScrollHandler( HIViewRef control, SInt16 part ) { - SInt16 currentValue = GetControl32BitValue( control ); - SInt16 min = GetControl32BitMinimum( control ); - SInt16 max = GetControl32BitMaximum( control ); - SInt16 page = GetControlViewSize( control ); + int currentValue = GetControl32BitValue( control ); + int min = GetControl32BitMinimum( control ); + int max = GetControl32BitMaximum( control ); + int page = GetControlViewSize( control ); // Get a reference to the Scintilla C++ object ScintillaMacOSX* scintilla = NULL; @@ -1066,6 +1214,7 @@ pascal void ScintillaMacOSX::LiveScrollHandler( HIViewRef control, SInt16 part ) break; case kControlIndicatorPart: + case kControlNoPart: newValue = currentValue; break; @@ -1139,30 +1288,34 @@ bool ScintillaMacOSX::ScrollBarHit(HIPoint location) { return false; } - -void ScintillaMacOSX::NotifyFocus(bool /*focus*/) { - // TODO: How should this be implemented on OS X? Should it be? +void ScintillaMacOSX::NotifyFocus(bool focus) { +#ifdef EXT_INPUT + ExtInput::activate (GetViewRef(), focus); +#endif + if (NULL != notifyProc) + notifyProc (notifyObj, WM_COMMAND, + (uintptr_t) ((focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS) << 16), + (uintptr_t) GetViewRef()); } -typedef void (*SciNotifyFunc)(sptr_t *, long); -void ScintillaMacOSX::NotifyParent(SCNotification scn) { - OSStatus err; - sptr_t *ptr = NULL; - SciNotifyFunc fn = NULL; +void ScintillaMacOSX::NotifyChange() { + if (NULL != notifyProc) + notifyProc (notifyObj, WM_COMMAND, + (uintptr_t) (SCEN_CHANGE << 16), + (uintptr_t) GetViewRef()); +} - // XXX do this at some other point, or otherwise cache the results - err = GetControlProperty(GetViewRef(), - scintillaNotifyObject, 0, - sizeof( sptr_t * ), NULL, &ptr ); - if (err != noErr) return; - err = GetControlProperty(GetViewRef(), - scintillaNotifyFN, 0, - sizeof( SciNotifyFunc ), NULL, &fn ); - if (err != noErr || !fn) return; +void ScintillaMacOSX::registerNotifyCallback(intptr_t windowid, SciNotifyFunc callback) { + notifyObj = windowid; + notifyProc = callback; +} - scn.nmhdr.hwndFrom = GetViewRef(); - scn.nmhdr.idFrom = (unsigned int)wMain.GetID(); - fn(ptr, (long int)&scn); +void ScintillaMacOSX::NotifyParent(SCNotification scn) { + if (NULL != notifyProc) { + scn.nmhdr.hwndFrom = (void*) this; + scn.nmhdr.idFrom = (unsigned int)wMain.GetID(); + notifyProc (notifyObj, WM_NOTIFY, (uintptr_t) 0, (uintptr_t) &scn); + } } void ScintillaMacOSX::NotifyKey(int key, int modifiers) { @@ -1182,6 +1335,8 @@ void ScintillaMacOSX::NotifyURIDropped(const char *list) { NotifyParent(scn); } +#ifndef EXT_INPUT +// Extended UTF8-UTF6-conversion to handle surrogate pairs correctly (CL265070) int ScintillaMacOSX::KeyDefault(int key, int modifiers) { if (!(modifiers & SCI_CTRL) && !(modifiers & SCI_ALT) && (key < 256)) { AddChar(key); @@ -1193,6 +1348,7 @@ int ScintillaMacOSX::KeyDefault(int key, int modifiers) { } //Platform::DebugPrintf("SK-key: %d %x %x\n",key, modifiers); } +#endif template <class T, class U> struct StupidMap @@ -1254,6 +1410,10 @@ pascal OSStatus ScintillaMacOSX::CommandEventHandler( EventHandlerCallRef /*inCa if ( kind == kEventProcessCommand ) { +#ifdef EXT_INPUT + // We are getting a HI command, so stop extended input + ExtInput::stop (scintilla->GetViewRef()); +#endif // Find the method pointer that matches this command void (ScintillaMacOSX::*methodPtr)() = StupidMapFind( processCommands, command.commandID ); @@ -1316,60 +1476,37 @@ bool ScintillaMacOSX::AlwaysTrue() } void ScintillaMacOSX::CopyToClipboard(const SelectionText &selectedText) { - if (selectedText.len == 0) - return; - - CFStringEncoding encoding = ( IsUnicodeMode() ? kCFStringEncodingUTF8 : kCFStringEncodingASCII); - - // Create a CFString from the ASCII/UTF8 data, convert it to UTF16 - CFStringRef string = CFStringCreateWithBytes( NULL, reinterpret_cast<UInt8*>( selectedText.s ), selectedText.len - 1, encoding, false ); - assert( string != NULL ); - - CFIndex numUniChars = CFStringGetLength( string ); - UniChar* buffer = new UniChar[ numUniChars ]; - CFStringGetCharacters( string, CFRangeMake( 0, numUniChars ), buffer ); - + PasteboardRef theClipboard; + SetPasteboardData(theClipboard, selectedText); // Done with the CFString - CFRelease( string ); - string = NULL; - - OSStatus err; - err = ClearCurrentScrap(); - assert( err == noErr ); - - ScrapRef scrap = NULL; - err = GetCurrentScrap( &scrap ); - assert( err == noErr && scrap != NULL ); - - err = PutScrapFlavor( scrap, kScrapFlavorTypeUnicode, 0, sizeof( UniChar ) * numUniChars, buffer ); - assert( err == noErr ); - err = PutScrapFlavor( scrap, kScrapFlavorTypeText, 0, sizeof( char ) * selectedText.len, reinterpret_cast<UInt8*>( selectedText.s ) ); - assert( err == noErr ); - - // Done with the UniChar* buffer - delete[] buffer; - buffer = NULL; + CFRelease( theClipboard ); } void ScintillaMacOSX::Copy() { if (currentPos != anchor) { +#ifdef EXT_INPUT + ExtInput::stop (GetViewRef()); +#endif SelectionText selectedText; CopySelectionRange(&selectedText); + fprintf(stderr, "copied text is rectangular? %d\n", selectedText.rectangular); CopyToClipboard(selectedText); } } bool ScintillaMacOSX::CanPaste() { - ScrapRef scrap = NULL; - OSStatus err; - err = GetCurrentScrap( &scrap ); - assert( err == noErr && scrap != NULL ); + if (!Editor::CanPaste()) + return false; + + PasteboardRef theClipboard; + bool isFileURL = false; - ScrapFlavorFlags flavorFlags; - return GetScrapFlavorFlags ( scrap, kScrapFlavorTypeUnicode, &flavorFlags ) == noErr || - GetScrapFlavorFlags ( scrap, kScrapFlavorTypeText, &flavorFlags ) == noErr; + PasteboardCreate( kPasteboardClipboard, &theClipboard ); + bool ok = GetPasteboardData(theClipboard, NULL, &isFileURL); + CFRelease( theClipboard ); + return ok; } void ScintillaMacOSX::Paste() @@ -1380,112 +1517,34 @@ void ScintillaMacOSX::Paste() // XXX there is no system flag (I can find) to tell us that a paste is rectangular, so // applications must implement an additional command (eg. option-V like BBEdit) // in order to provide rectangular paste -void ScintillaMacOSX::Paste(bool isRectangular) +void ScintillaMacOSX::Paste(bool forceRectangular) { - // Make sure that we CAN paste - if ( ! CanPaste() ) return; - - // Get the clipboard reference - ScrapRef scrap = NULL; - OSStatus err; - err = GetCurrentScrap( &scrap ); - assert( err == noErr && scrap != NULL ); - - ScrapFlavorFlags flavorFlags; - Size bytes = 0; - CFStringRef string = NULL; - if (GetScrapFlavorFlags ( scrap, kScrapFlavorTypeUnicode, &flavorFlags ) == noErr) - { - // No error, we have unicode data in a Scrap. Find out how many bytes of data it is. - err = GetScrapFlavorSize( scrap, kScrapFlavorTypeUnicode, &bytes ); - assert( err == noErr && bytes != 0 ); - Size numUniChars = bytes / sizeof( UniChar ); - - // Allocate a buffer for the text using Core Foundation - UniChar* buffer = reinterpret_cast<UniChar*>( CFAllocatorAllocate( NULL, bytes, 0 ) ); - assert( buffer != NULL ); - - // Get a copy of the text - Size nextBytes = bytes; - err = GetScrapFlavorData( scrap, kScrapFlavorTypeUnicode, &nextBytes, buffer ); - assert( err == noErr && nextBytes == bytes ); - - // Create a CFString which wraps and takes ownership of the buffer - string = CFStringCreateWithCharactersNoCopy( NULL, buffer, numUniChars, NULL ); - assert( string != NULL ); - buffer = NULL; // string now owns this buffer - } else if (GetScrapFlavorFlags ( scrap, kScrapFlavorTypeText, &flavorFlags ) == noErr) { - // No error, we have unicode data in a Scrap. Find out how many bytes of data it is. - err = GetScrapFlavorSize( scrap, kScrapFlavorTypeText, &bytes ); - assert( err == noErr && bytes != 0 ); - - // Allocate a buffer for the text using Core Foundation - char* buffer = reinterpret_cast<char*>( CFAllocatorAllocate( NULL, bytes + 1, 0 ) ); - assert( buffer != NULL ); - - // Get a copy of the text - Size nextBytes = bytes; - err = GetScrapFlavorData( scrap, kScrapFlavorTypeText, &nextBytes, buffer ); - assert( err == noErr && nextBytes == bytes ); - buffer[bytes]=0; - // Create a CFString which wraps and takes ownership of the buffer - string = CFStringCreateWithCStringNoCopy( NULL, buffer, kCFStringEncodingMacRoman, NULL ); - assert( string != NULL ); - buffer = NULL; // string now owns this buffer - } else { - // a flavor we do not understand + PasteboardRef theClipboard; + SelectionText selectedText; + selectedText.rectangular = forceRectangular; + bool isFileURL = false; + PasteboardCreate( kPasteboardClipboard, &theClipboard ); + bool ok = GetPasteboardData(theClipboard, &selectedText, &isFileURL); + CFRelease( theClipboard ); + fprintf(stderr, "paste is rectangular? %d\n", selectedText.rectangular); + if (!ok || !selectedText.s) + // no data or no flavor we support return; - } - - - // Allocate a buffer, plus the null byte - CFIndex numUniChars = CFStringGetLength( string ); - CFStringEncoding encoding = ( IsUnicodeMode() ? kCFStringEncodingUTF8 : kCFStringEncodingASCII); - CFIndex maximumByteLength = CFStringGetMaximumSizeForEncoding( numUniChars, encoding ) + 1; - char* cstring = new char[maximumByteLength]; - CFIndex usedBufferLength = 0; - CFIndex numCharsConverted; - numCharsConverted = CFStringGetBytes( string, CFRangeMake( 0, numUniChars ), encoding, - '?', false, reinterpret_cast<UInt8*>( cstring ), - maximumByteLength, &usedBufferLength ); - cstring[usedBufferLength] = '\0'; // null terminate the ASCII/UTF8 string - assert( numCharsConverted == numUniChars ); - - // Default allocator releases both the CFString and the UniChar buffer (text) - CFRelease( string ); - string = NULL; - - // determine whether a BOM is in the string. Apps like Emacs prepends a BOM - // to the string, CFStrinGetBytes reflects that (though it may change in the conversion) - // so we need to remove it before pasting into our buffer. TextWrangler has no - // problem dealing with BOM when pasting into it. - int bomLen = BOMlen((unsigned char *)cstring); - // convert line endings to the document line ending - int newlen = 0; - char *pasted = Document::TransformLineEnds(&newlen, - cstring + bomLen, - usedBufferLength - bomLen, - pdoc->eolMode); - pdoc->BeginUndoAction(); ClearSelection(); - - if (isRectangular) { + if (selectedText.rectangular) { int selStart = SelectionStart(); - PasteRectangular(selStart, pasted, newlen); + PasteRectangular(selStart, selectedText.s, selectedText.len); } else - if ( pdoc->InsertString( currentPos, pasted, newlen ) ) { - SetEmptySelection( currentPos + newlen ); + if ( pdoc->InsertString( currentPos, selectedText.s, selectedText.len ) ) { + SetEmptySelection( currentPos + selectedText.len ); } - delete[] pasted; - delete[] cstring; - cstring = NULL; - pdoc->EndUndoAction(); - NotifyChange(); + Redraw(); + EnsureCaretVisible(); } void ScintillaMacOSX::CreateCallTipWindow(PRectangle rc) { @@ -1602,7 +1661,7 @@ void ScintillaMacOSX::AddToPopUp( const char *label, int cmd, bool enabled ) return; } - CFStringRef string = CFStringCreateWithCString( NULL, label, kTextEncodingMacRoman ); + CFStringRef string = CFStringCreateWithCString( NULL, label, kCFStringEncodingUTF8 ); OSStatus err; err = AppendMenuItemTextWithCFString( reinterpret_cast<MenuRef>( popup.GetID() ), string, attributes, macCommand, NULL ); @@ -1702,10 +1761,12 @@ static inline int KeyTranslate( UniChar unicodeChar ) return SCK_HOME; case kEndCharCode: return SCK_END; +#ifndef EXT_INPUT case kPageUpCharCode: return SCK_PRIOR; case kPageDownCharCode: return SCK_NEXT; +#endif case kDeleteCharCode: return SCK_DELETE; // TODO: Is there an insert key in the mac world? My insert key is the "help" key @@ -1714,6 +1775,30 @@ static inline int KeyTranslate( UniChar unicodeChar ) case kEnterCharCode: case kReturnCharCode: return SCK_RETURN; +#ifdef EXT_INPUT + // BP 2006-08-22: These codes below should not be translated. Otherwise TextInput() will fail for keys like SCK_ADD, which is '+'. + case kBackspaceCharCode: + return SCK_BACK; + case kFunctionKeyCharCode: + case kBellCharCode: + case kVerticalTabCharCode: + case kFormFeedCharCode: + case 14: + case 15: + case kCommandCharCode: + case kCheckCharCode: + case kAppleLogoCharCode: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case kEscapeCharCode: + return 0; // ignore + default: + return unicodeChar; +#else case kEscapeCharCode: return SCK_ESCAPE; case kBackspaceCharCode: @@ -1730,6 +1815,7 @@ static inline int KeyTranslate( UniChar unicodeChar ) return kFunctionKeyCharCode; default: return 0; +#endif } } @@ -1775,6 +1861,39 @@ OSStatus ScintillaMacOSX::TextInput( TCarbonEvent& event ) const int numUniChars = actualSize / sizeof( UniChar ); +#ifdef EXT_INPUT + UniChar* text = new UniChar [numUniChars]; + err = event.GetParameter( kEventParamTextInputSendText, typeUnicodeText, actualSize, text ); + PLATFORM_ASSERT( err == noErr ); + + int modifiers = GetCurrentEventKeyModifiers(); + + // Loop over all characters in sequence + for (int i = 0; i < numUniChars; i++) + { + UniChar key = KeyTranslate( text[i] ); + if (!key) + continue; + + bool consumed = false; + + // need to go here first so e.g. Tab indentation works + KeyDown ((int) key, (modifiers & shiftKey) != 0 || (modifiers & cmdKey) != 0, (modifiers & controlKey) != 0 || (modifiers & cmdKey) != 0, + (modifiers & optionKey) != 0 || (modifiers & cmdKey) != 0, &consumed); + + // BP 2007-01-08: 1452623 Second Cmd+s to save doc inserts an "s" into the text on Mac. + // At this point we need to ignore all cmd/option keys with char value smaller than 32 + if( !consumed ) + consumed = ( modifiers & ( cmdKey | optionKey ) ) != 0 && text[i] < 32; + + // If not consumed, insert the original key + if (!consumed) + InsertCharacters (text+i, 1); + } + + delete[] text; + return noErr; +#else // Allocate a buffer for the text using Core Foundation UniChar* text = reinterpret_cast<UniChar*>( CFAllocatorAllocate( CFAllocatorGetDefault(), actualSize, 0 ) ); assert( text != NULL ); @@ -1873,6 +1992,7 @@ OSStatus ScintillaMacOSX::TextInput( TCarbonEvent& event ) string = NULL; return err; +#endif } UInt32 ScintillaMacOSX::GetBehaviors() @@ -1891,6 +2011,7 @@ OSStatus ScintillaMacOSX::MouseEntered(HIPoint& location, UInt32 /*inKeyModifier } else { // reset to normal, buttonmove will change for other area's in the editor WndProc(SCI_SETCURSOR, (long int)SC_CURSORNORMAL, 0); + ButtonMove( Point( static_cast<int>( location.x ), static_cast<int>( location.y ) ) ); } return noErr; } @@ -1945,7 +2066,7 @@ OSStatus ScintillaMacOSX::MouseDown( HIPoint& location, UInt32 modifiers, EventM #if !defined(CONTAINER_HANDLES_EVENTS) OSStatus err; err = SetKeyboardFocus( this->GetOwner(), this->GetViewRef(), 1 ); - assert( err == noErr ); + ::SetUserFocusWindow(::HIViewGetWindow( this->GetViewRef() )); return noErr; #else return eventNotHandledErr; // allow event to go to container diff --git a/macosx/ScintillaMacOSX.h b/macosx/ScintillaMacOSX.h index 1b386b48b..66d4f0f68 100644 --- a/macosx/ScintillaMacOSX.h +++ b/macosx/ScintillaMacOSX.h @@ -2,8 +2,10 @@ * ScintillaMacOSX.h * tutorial * - * Created by Evan Jones on Sun Sep 01 2002. - * Copyright (c) 2002 __MyCompanyName__. All rights reserved. + * Original code by Evan Jones on Sun Sep 01 2002. + * Contributors: + * Shane Caraveo, ActiveState + * Bernd Paradies, Adobe * */ #include "TView.h" @@ -50,20 +52,41 @@ #include "ScintillaCallTip.h" static const OSType scintillaMacOSType = 'Scin'; -static const OSType scintillaNotifyObject = 'Snob'; -static const OSType scintillaNotifyFN = 'Snfn'; namespace Scintilla { +/** +On the Mac, there is no WM_COMMAND or WM_NOTIFY message that can be sent +back to the parent. Therefore, there must be a callback handler that acts +like a Windows WndProc, where Scintilla can send notifications to. Use +ScintillaMacOSX::registerNotifyHandler() to register such a handler. +Messgae format is: +<br> +WM_COMMAND: HIWORD (wParam) = notification code, LOWORD (wParam) = 0 (no control ID), lParam = ScintillaMacOSX* +<br> +WM_NOTIFY: wParam = 0 (no control ID), lParam = ptr to SCNotification structure, with hwndFrom set to ScintillaMacOSX* +*/ +typedef void(*SciNotifyFunc) (intptr_t windowid, unsigned int iMessage, uintptr_t wParam, uintptr_t lParam); + +/** +Scintilla sends these two messages to the nofity handler. Please refer +to the Windows API doc for details about the message format. +*/ +#define WM_COMMAND 1001 +#define WM_NOTIFY 1002 + class ScintillaMacOSX : public ScintillaBase, public TView { public: HIViewRef vScrollBar; HIViewRef hScrollBar; SInt32 scrollBarFixedSize; + SciNotifyFunc notifyProc; + intptr_t notifyObj; bool capturedMouse; - bool inDragSession; // true if scintilla initiated the drag session + // true if scintilla initiated the drag session + bool inDragSession() { return inDragDrop == ddDragging; }; bool isTracking; // Private so ScintillaMacOSX objects can not be copied @@ -82,15 +105,26 @@ public: /** Returns the HIView object kind, needed to subclass TView. */ virtual ControlKind GetKind() { return kScintillaKind; } + /// Register the notify callback + void registerNotifyCallback(intptr_t windowid, SciNotifyFunc callback); private: virtual void Initialise(); virtual void Finalise(); + + // pasteboard support + bool GetPasteboardData(PasteboardRef &pasteBoard, + SelectionText *selectedText, bool *isFileURL); + void SetPasteboardData(PasteboardRef &pasteBoard, + const SelectionText &selectedText); + char *GetStringFromCFString(CFStringRef &textString, int *textLen); + + // Drag and drop virtual void StartDrag(); Scintilla::Point GetDragPoint(DragRef inDrag); - OSStatus GetDragData(DragRef inDrag, PasteboardRef &pasteBoard, CFStringRef *textString); + bool GetDragData(DragRef inDrag, PasteboardRef &pasteBoard, + SelectionText *selectedText, + bool *isFileURL); void SetDragCursor(DragRef inDrag); - - // Drag and drop virtual bool DragEnter(DragRef inDrag ); virtual bool DragWithin(DragRef inDrag ); virtual bool DragLeave(DragRef inDrag ); @@ -104,6 +138,7 @@ private: MouseTrackingRegionID mouseTrackingID; HIPoint GetLocalPoint(::Point pt); + void InsertCharacters (const UniChar* buf, int len); static pascal void IdleTimerEventHandler(EventLoopTimerRef inTimer, EventLoopIdleTimerMessage inState, void *scintilla ); @@ -136,7 +171,10 @@ public: // Public for scintilla_send_message virtual void NotifyParent(SCNotification scn); void NotifyKey(int key, int modifiers); void NotifyURIDropped(const char *list); +#ifndef EXT_INPUT + // Extended UTF8-UTF6-conversion to handle surrogate pairs correctly (CL265070) virtual int KeyDefault(int key, int modifiers); +#endif static pascal OSStatus CommandEventHandler( EventHandlerCallRef inCallRef, EventRef inEvent, void* data ); bool HasSelection(); diff --git a/macosx/TView.cxx b/macosx/TView.cxx index 9ed02b110..00a354ae7 100644 --- a/macosx/TView.cxx +++ b/macosx/TView.cxx @@ -111,12 +111,8 @@ TView::~TView() // If we have installed our custom mouse events handler on the window, // go forth and remove it. Note: -1 is used to indicate that no handler has // been installed yet, but we want to once we get a window. - if ( mouseEventHandler != NULL && mouseEventHandler != reinterpret_cast<void*>( -1 ) ) - { - OSStatus err; - err = RemoveEventHandler( mouseEventHandler ); - assert( err == noErr ); - } + if ( mouseEventHandler != NULL && mouseEventHandler != reinterpret_cast<void*>( -1 ) ) + RemoveEventHandler( mouseEventHandler ); mouseEventHandler = NULL; } @@ -549,7 +545,6 @@ OSStatus TView::ActivateInterface( result = AddEventTypesToHandler( fHandler, GetEventTypeCount( kControlMouseEvents ), kControlMouseEvents ); - assert( result == noErr ); } if ( this->GetOwner() != NULL ) @@ -558,7 +553,6 @@ OSStatus TView::ActivateInterface( if ( mouseEventHandler != NULL && mouseEventHandler != reinterpret_cast<void*>( -1 ) ) { result = RemoveEventHandler( mouseEventHandler ); - assert( result != NULL ); } mouseEventHandler = NULL; @@ -570,11 +564,9 @@ OSStatus TView::ActivateInterface( { kEventClassMouse, kEventMouseDragged }, }; - assert( mouseEventHandler == NULL ); result = InstallEventHandler( GetWindowEventTarget( this->GetOwner() ), WindowEventHandler, GetEventTypeCount( kWindowMouseEvents ), kWindowMouseEvents, this, &mouseEventHandler ); - assert( result == noErr && mouseEventHandler != NULL ); } // If we have no window yet. Set the mouseEventHandler to -1 so when we get one we // will install the event handler @@ -595,7 +587,6 @@ OSStatus TView::ActivateInterface( result = AddEventTypesToHandler( fHandler, GetEventTypeCount( kControlMouseEvents ), kControlMouseEvents ); - assert( result == noErr ); } } @@ -697,8 +688,8 @@ pascal OSStatus TView::ViewEventHandler( OSStatus result; TView* view = (TView*) inUserData; TCarbonEvent event( inEvent ); - if (view->debugPrint) - fprintf(stderr,"TView::ViewEventHandler\n"); + //if (view->debugPrint) + // fprintf(stderr,"TView::ViewEventHandler\n"); result = view->HandleEvent( inCallRef, event ); return result; @@ -714,7 +705,8 @@ pascal OSStatus TView::WindowEventHandler( TCarbonEvent event( inEvent ); const WindowRef window = view->GetOwner(); - assert( window != NULL ); + if (NULL == window) + return eventNotHandledErr; // If the window is not active, let the standard window handler execute. if ( ! IsWindowActive( window ) ) return eventNotHandledErr; @@ -722,7 +714,8 @@ pascal OSStatus TView::WindowEventHandler( fprintf(stderr,"TView::WindowEventHandler\n"); const HIViewRef rootView = HIViewGetRoot( window ); - assert( rootView != NULL ); + if (NULL == rootView) + return eventNotHandledErr; // TODO: On OS X 10.3, test if this bug still exists // This is a hack to work around a bug in the OS. See: @@ -739,19 +732,18 @@ pascal OSStatus TView::WindowEventHandler( // convert screen coords to window relative Rect bounds; err = GetWindowBounds( window, kWindowStructureRgn, &bounds ); - assert( err == noErr ); - - ptMouse.x -= bounds.left; - ptMouse.y -= bounds.top; - - event.SetParameter( kEventParamWindowMouseLocation, ptMouse ); + if( err == noErr ) + { + ptMouse.x -= bounds.left; + ptMouse.y -= bounds.top; + event.SetParameter( kEventParamWindowMouseLocation, ptMouse ); + } } HIViewRef targetView = NULL; err = HIViewGetViewForMouseEvent( rootView, inEvent, &targetView ); - assert( err == noErr && targetView != NULL ); - if (view->debugPrint) - fprintf(stderr,"TView::WindowEventHandler root[%08X] viewRef[%08X] targetView[%08X]\n", rootView, view->GetViewRef(), targetView); + //if (view->debugPrint) + // fprintf(stderr,"TView::WindowEventHandler root[%08X] viewRef[%08X] targetView[%08X]\n", rootView, view->GetViewRef(), targetView); if ( targetView == view->GetViewRef() || event.GetKind() == kEventMouseDragged ) { return view->HandleEvent( inCallRef, event ); @@ -1076,7 +1068,6 @@ OSStatus TView::HandleEvent( // some other kind of Control event default: - assert( false ); break; } break; @@ -1086,70 +1077,59 @@ OSStatus TView::HandleEvent( break; case kEventClassMouse: + { result = inEvent.GetParameter<HIPoint>( kEventParamWindowMouseLocation, typeHIPoint, &where ); HIViewConvertPoint( &where, NULL, fViewRef ); - assert( result == noErr ); UInt32 inKeyModifiers; result = inEvent.GetParameter( kEventParamKeyModifiers, &inKeyModifiers ); - assert( result == noErr ); - + if( result != noErr ) + inKeyModifiers = 0; + EventMouseButton inMouseButton = 0; + result = inEvent.GetParameter<EventMouseButton>( kEventParamMouseButton, typeMouseButton, &inMouseButton ); + if (noErr != result) + // assume no button is pressed if there is no button info + inMouseButton = 0; + UInt32 inClickCount; + result = inEvent.GetParameter( kEventParamClickCount, &inClickCount ); + if( result != noErr ) + inClickCount = 0; + switch ( inEvent.GetKind() ) - { + { case kEventMouseWheelMoved: - { - EventMouseWheelAxis inAxis; - result = inEvent.GetParameter<EventMouseWheelAxis>( kEventParamMouseWheelAxis, typeMouseWheelAxis, &inAxis ); - assert( noErr == result ); + { + EventMouseWheelAxis inAxis; + result = inEvent.GetParameter<EventMouseWheelAxis>( kEventParamMouseWheelAxis, typeMouseWheelAxis, &inAxis ); - SInt32 inDelta; - result = inEvent.GetParameter<SInt32>( kEventParamMouseWheelDelta, typeSInt32, &inDelta ); - assert( noErr == result ); + SInt32 inDelta; + result = inEvent.GetParameter<SInt32>( kEventParamMouseWheelDelta, typeSInt32, &inDelta ); - result = MouseWheelMoved( inAxis, inDelta, inKeyModifiers ); - } + result = MouseWheelMoved( inAxis, inDelta, inKeyModifiers ); + break; + } + case kEventMouseDown: + result = MouseDown( where, inKeyModifiers, inMouseButton, inClickCount, inEvent ); break; - - - // some other kind of Mouse event: This is (in my opinion) an error - // TODO: There is also a MouseMoved event that we do not handle - default: - { - EventMouseButton inMouseButton; - result = inEvent.GetParameter<EventMouseButton>( kEventParamMouseButton, typeMouseButton, &inMouseButton ); - assert( result == noErr ); - - UInt32 inClickCount; - result = inEvent.GetParameter( kEventParamClickCount, &inClickCount ); - assert( result == noErr ); - - switch ( inEvent.GetKind() ) - { - case kEventMouseDown: - result = MouseDown( where, inKeyModifiers, inMouseButton, inClickCount, inEvent ); - break; - case kEventMouseUp: - result = MouseUp( where, inKeyModifiers, inMouseButton, inClickCount ); - break; - case kEventMouseExited: - result = MouseExited( where, inKeyModifiers, inMouseButton, inClickCount ); - break; - case kEventMouseEntered: - result = MouseEntered( where, inKeyModifiers, inMouseButton, inClickCount ); - break; - case kEventMouseMoved: - case kEventMouseDragged: - result = MouseDragged( where, inKeyModifiers, inMouseButton, inClickCount ); - break; - } - } + case kEventMouseUp: + result = MouseUp( where, inKeyModifiers, inMouseButton, inClickCount ); break; - } - break; - + case kEventMouseExited: + result = MouseExited( where, inKeyModifiers, inMouseButton, inClickCount ); + break; + case kEventMouseEntered: + result = MouseEntered( where, inKeyModifiers, inMouseButton, inClickCount ); + break; + case kEventMouseMoved: + case kEventMouseDragged: + result = MouseDragged( where, inKeyModifiers, inMouseButton, inClickCount ); + break; + default:; + } + break; + } // some other event class default: - assert( false ); break; } diff --git a/macosx/makefile b/macosx/makefile index 02ff7998a..73342ce3e 100644 --- a/macosx/makefile +++ b/macosx/makefile @@ -50,10 +50,15 @@ ifdef CONTAINER_HANDLES_EVENTS CONTAINER=-DCONTAINER_HANDLES_EVENTS=1 endif +ifdef EXT_INPUT +EXT_INPUT=-DEXT_INPUT +EXTOBS=ExtInput.o +endif + .cxx.o: - $(CC) $(CXXFLAGS) $(OPTIONS) $(DFLAGS) $(CONTAINER) $(ARCHFLAGS) $(INCLUDEDIRS) -c $< + $(CC) $(CXXFLAGS) $(OPTIONS) $(DFLAGS) $(CONTAINER) $(ARCHFLAGS) $(EXT_INPUT) $(INCLUDEDIRS) -c $< .c.o: - $(CCOMP) $(CXXFLAGS) $(OPTIONS) $(DFLAGS) $(CONTAINER) $(ARCHFLAGS) $(INCLUDEDIRS) -w -c $< + $(CCOMP) $(CXXFLAGS) $(OPTIONS) $(DFLAGS) $(CONTAINER) $(ARCHFLAGS) $(EXT_INPUT) $(INCLUDEDIRS) -w -c $< #++Autogenerated -- run src/LexGen.py to regenerate #**LEXOBJS=\\\n\(\*.o \) @@ -87,13 +92,13 @@ COMPLIB=DocumentAccessor.o WindowAccessor.o KeyWords.o StyleContext.o \ ScintillaBase.o ContractionState.o Editor.o ExternalLexer.o PropSet.o PlatMacOSX.o \ KeyMap.o LineMarker.o ScintillaMacOSX.o CellBuffer.o ViewStyle.o \ RESearch.o RunStyles.o Style.o Indicator.o AutoComplete.o UniConversion.o XPM.o \ - TCarbonEvent.o TView.o ScintillaCallTip.o \ + TCarbonEvent.o TView.o ScintillaCallTip.o $(EXTOBS) \ $(LEXOBJS) -$(STATICLIB): $(COMPLIB) +$(STATICLIB): $(COMPLIB) $(LIBTOOL) -static -o $@ $^ -$(DYNAMICLIB): $(COMPLIB) +$(DYNAMICLIB): $(COMPLIB) $(CC) -dynamic -o $@ $(DYN_FLAGS) $^ # Generate header files from Scintilla.iface |