diff options
Diffstat (limited to 'macosx/ScintillaMacOSX.cxx')
-rw-r--r-- | macosx/ScintillaMacOSX.cxx | 2244 |
1 files changed, 0 insertions, 2244 deletions
diff --git a/macosx/ScintillaMacOSX.cxx b/macosx/ScintillaMacOSX.cxx deleted file mode 100644 index d1bee757f..000000000 --- a/macosx/ScintillaMacOSX.cxx +++ /dev/null @@ -1,2244 +0,0 @@ -// Scintilla source code edit control -// ScintillaMacOSX.cxx - Mac OS X subclass of ScintillaBase -// Copyright 2003 by Evan Jones <ejones@uwaterloo.ca> -// Based on ScintillaGTK.cxx Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org> -// The License.txt file describes the conditions under which this software may be distributed. - - -#include "ScintillaMacOSX.h" -#ifdef EXT_INPUT -// External Input Editor -#include "ExtInput.h" -#else -#include "UniConversion.h" -#endif - -using namespace Scintilla; - -const CFStringRef ScintillaMacOSX::kScintillaClassID = CFSTR( "org.scintilla.scintilla" ); -const ControlKind ScintillaMacOSX::kScintillaKind = { 'ejon', 'Scin' }; - -extern "C" HIViewRef scintilla_calltip_new(void); - -#ifndef WM_UNICHAR -#define WM_UNICHAR 0x0109 -#endif - -// required for paste/dragdrop, see comment in paste function below -static int BOMlen(unsigned char *cstr) { - switch(cstr[0]) { - case 0xEF: // BOM_UTF8 - if (cstr[1] == 0xBB && cstr[2] == 0xBF) { - return 3; - } - break; - case 0xFE: - if (cstr[1] == 0xFF) { - if (cstr[2] == 0x00 && cstr[3] == 0x00) { - return 4; - } - return 2; - } - break; - case 0xFF: - if (cstr[1] == 0xFE) { - if (cstr[2] == 0x00 && cstr[3] == 0x00) { - return 4; - } - return 2; - } - break; - case 0x00: - if (cstr[1] == 0x00) { - if (cstr[2] == 0xFE && cstr[3] == 0xFF) { - return 4; - } - if (cstr[2] == 0xFF && cstr[3] == 0xFE) { - return 4; - } - return 2; - } - break; - } - - 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 ); - assert( err == noErr ); - - mouseTrackingRef = NULL; - mouseTrackingID.signature = scintillaMacOSType; - mouseTrackingID.id = (SInt32)this; - capturedMouse = false; - - // Enable keyboard events and mouse events -#if !defined(CONTAINER_HANDLES_EVENTS) - ActivateInterface( kKeyboardFocus ); - ActivateInterface( kMouse ); - ActivateInterface( kDragAndDrop ); -#endif - ActivateInterface( kMouseTracking ); - - Initialise(); - - // Create some bounds rectangle which will just get reset to the correct rectangle later - Rect tempScrollRect; - tempScrollRect.top = -1; - tempScrollRect.left = 400; - tempScrollRect.bottom = 300; - tempScrollRect.right = 450; - - // Create the scroll bar with fake values that will get set correctly later - err = CreateScrollBarControl( this->GetOwner(), &tempScrollRect, 0, 0, 100, 100, true, LiveScrollHandler, &vScrollBar ); - assert( vScrollBar != NULL && err == noErr ); - err = CreateScrollBarControl( this->GetOwner(), &tempScrollRect, 0, 0, 100, 100, true, LiveScrollHandler, &hScrollBar ); - assert( hScrollBar != NULL && err == noErr ); - - // Set a property on the scrollbars to store a pointer to the Scintilla object - ScintillaMacOSX* objectPtr = this; - err = SetControlProperty( vScrollBar, scintillaMacOSType, 0, sizeof( this ), &objectPtr ); - assert( err == noErr ); - err = SetControlProperty( hScrollBar, scintillaMacOSType, 0, sizeof( this ), &objectPtr ); - assert( err == noErr ); - - // set this into our parent control so we can be retrieved easily at a later time - // (see scintilla_send below) - err = SetControlProperty( reinterpret_cast<HIViewRef>( windowid ), scintillaMacOSType, 0, sizeof( this ), &objectPtr ); - assert( err == noErr ); - - // Tell Scintilla not to buffer: Quartz buffers drawing for us - // TODO: Can we disable this option on Mac OS X? - WndProc( SCI_SETBUFFEREDDRAW, 0, 0 ); - // Turn on UniCode mode - WndProc( SCI_SETCODEPAGE, SC_CP_UTF8, 0 ); - - const EventTypeSpec commandEventInfo[] = { - { kEventClassCommand, kEventProcessCommand }, - { kEventClassCommand, kEventCommandUpdateStatus }, - }; - - err = InstallEventHandler( GetControlEventTarget( reinterpret_cast<HIViewRef>( windowid ) ), - CommandEventHandler, - GetEventTypeCount( commandEventInfo ), - commandEventInfo, - this, NULL); -#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() { - // If the window is closed and the timer is not removed, - // A segment violation will occur when it attempts to fire the timer next. - if ( mouseTrackingRef != NULL ) { - ReleaseMouseTrackingRegion(mouseTrackingRef); - } - mouseTrackingRef = NULL; - SetTicking(false); -#ifdef EXT_INPUT - ExtInput::detach (GetViewRef()); -#endif -} - -void ScintillaMacOSX::Initialise() { - // TODO: Do anything here? Maybe this stuff should be here instead of the constructor? -} - -void ScintillaMacOSX::Finalise() { - SetTicking(false); - ScintillaBase::Finalise(); -} - -// -------------------------------------------------------------------------------------------------------------- -// -// IsDropInFinderTrash - Returns true if the given dropLocation AEDesc is a descriptor of the Finder's Trash. -// -#pragma segment Drag - -Boolean IsDropInFinderTrash(AEDesc *dropLocation) -{ - OSErr result; - AEDesc dropSpec; - FSSpec *theSpec; - CInfoPBRec thePB; - short trashVRefNum; - long trashDirID; - - // Coerce the dropLocation descriptor into an FSSpec. If there's no dropLocation or - // it can't be coerced into an FSSpec, then it couldn't have been the Trash. - - if ((dropLocation->descriptorType != typeNull) && - (AECoerceDesc(dropLocation, typeFSS, &dropSpec) == noErr)) - { - unsigned char flags = HGetState((Handle)dropSpec.dataHandle); - - HLock((Handle)dropSpec.dataHandle); - theSpec = (FSSpec *) *dropSpec.dataHandle; - - // Get the directory ID of the given dropLocation object. - - thePB.dirInfo.ioCompletion = 0L; - thePB.dirInfo.ioNamePtr = (StringPtr) &theSpec->name; - thePB.dirInfo.ioVRefNum = theSpec->vRefNum; - thePB.dirInfo.ioFDirIndex = 0; - thePB.dirInfo.ioDrDirID = theSpec->parID; - - result = PBGetCatInfoSync(&thePB); - - HSetState((Handle)dropSpec.dataHandle, flags); - AEDisposeDesc(&dropSpec); - - if (result != noErr) - return false; - - // If the result is not a directory, it must not be the Trash. - - if (!(thePB.dirInfo.ioFlAttrib & (1 << 4))) - return false; - - // Get information about the Trash folder. - - FindFolder(theSpec->vRefNum, kTrashFolderType, kCreateFolder, &trashVRefNum, &trashDirID); - - // If the directory ID of the dropLocation object is the same as the directory ID - // returned by FindFolder, then the drop must have occurred into the Trash. - - if (thePB.dirInfo.ioDrDirID == trashDirID) - return true; - } - - return false; - -} // IsDropInFinderTrash - -HIPoint ScintillaMacOSX::GetLocalPoint(::Point pt) -{ - // get the mouse position so we can offset it - Rect bounds; - GetWindowBounds( GetOwner(), kWindowStructureRgn, &bounds ); - - PRectangle hbounds = wMain.GetPosition(); - HIViewRef parent = HIViewGetSuperview(GetViewRef()); - Rect pbounds; - GetControlBounds(parent, &pbounds); - - bounds.left += pbounds.left + hbounds.left; - bounds.top += pbounds.top + hbounds.top; - - HIPoint offset = { pt.h - bounds.left, pt.v - bounds.top }; - return offset; -} - -void ScintillaMacOSX::StartDrag() { - if (sel.Empty()) return; - - // calculate the bounds of the selection - PRectangle client = GetTextRectangle(); - int selStart = sel.RangeMain().Start().Position(); - int selEnd = sel.RangeMain().End().Position(); - int startLine = pdoc->LineFromPosition(selStart); - int endLine = pdoc->LineFromPosition(selEnd); - Point pt; - int startPos, endPos, ep; - Rect rcSel; - rcSel.top = rcSel.bottom = rcSel.right = rcSel.left = -1; - for (int l = startLine; l <= endLine; l++) { - startPos = WndProc(SCI_GETLINESELSTARTPOSITION, l, 0); - endPos = WndProc(SCI_GETLINESELENDPOSITION, l, 0); - if (endPos == startPos) continue; - // step back a position if we're counting the newline - ep = WndProc(SCI_GETLINEENDPOSITION, l, 0); - if (endPos > ep) endPos = ep; - - pt = LocationFromPosition(startPos); // top left of line selection - if (pt.x < rcSel.left || rcSel.left < 0) rcSel.left = pt.x; - if (pt.y < rcSel.top || rcSel.top < 0) rcSel.top = pt.y; - - pt = LocationFromPosition(endPos); // top right of line selection - pt.y += vs.lineHeight; // get to the bottom of the line - if (pt.x > rcSel.right || rcSel.right < 0) { - if (pt.x > client.right) - rcSel.right = client.right; - else - rcSel.right = pt.x; - } - if (pt.y > rcSel.bottom || rcSel.bottom < 0) { - if (pt.y > client.bottom) - rcSel.bottom = client.bottom; - else - rcSel.bottom = pt.y; - } - } - - // must convert to global coordinates for drag regions, but also save the - // image rectangle for further calculations and copy operations - PRectangle imageRect = PRectangle(rcSel.left, rcSel.top, rcSel.right, rcSel.bottom); - QDLocalToGlobalRect(GetWindowPort(GetOwner()), &rcSel); - - // get the mouse position so we can offset it - HIPoint offset = GetLocalPoint(mouseDownEvent.where); - offset.y = (imageRect.top * 1.0) - offset.y; - offset.x = (imageRect.left * 1.0) - offset.x; - - // to get a bitmap of the text we're dragging, we just use Paint on a - // pixmap surface. - SurfaceImpl *sw = new SurfaceImpl(); - SurfaceImpl *pixmap = NULL; - - if (sw) { - pixmap = new SurfaceImpl(); - if (pixmap) { - client = GetClientRectangle(); - paintState = painting; - sw->InitPixMap( client.Width(), client.Height(), NULL, NULL ); - paintingAllText = true; - Paint(sw, imageRect); - paintState = notPainting; - - pixmap->InitPixMap( imageRect.Width(), imageRect.Height(), NULL, NULL ); - - CGContextRef gc = pixmap->GetContext(); - - // to make Paint() work on a bitmap, we have to flip our coordinates - // and translate the origin - //fprintf(stderr, "translate to %d\n", client.Height() ); - CGContextTranslateCTM(gc, 0, imageRect.Height()); - CGContextScaleCTM(gc, 1.0, -1.0); - - pixmap->CopyImageRectangle( *sw, imageRect, PRectangle( 0, 0, imageRect.Width(), imageRect.Height() )); - // XXX TODO: overwrite any part of the image that is not part of the - // selection to make it transparent. right now we just use - // the full rectangle which may include non-selected text. - } - sw->Release(); - delete sw; - } - - // now we initiate the drag session - - RgnHandle dragRegion = NewRgn(); - RgnHandle tempRegion; - DragRef inDrag; - DragAttributes attributes; - AEDesc dropLocation; - SInt16 mouseDownModifiers, mouseUpModifiers; - bool copyText; - CGImageRef image = NULL; - - RectRgn(dragRegion, &rcSel); - - SelectionText selectedText; - CopySelectionRange(&selectedText); - PasteboardRef theClipboard; - SetPasteboardData(theClipboard, selectedText, true); - NewDragWithPasteboard( theClipboard, &inDrag); - CFRelease( theClipboard ); - - // Set the item's bounding rectangle in global coordinates. - SetDragItemBounds(inDrag, 1, &rcSel); - - // Prepare the drag region. - tempRegion = NewRgn(); - CopyRgn(dragRegion, tempRegion); - InsetRgn(tempRegion, 1, 1); - DiffRgn(dragRegion, tempRegion, dragRegion); - DisposeRgn(tempRegion); - - // if we have a pixmap, lets use that - if (pixmap) { - image = pixmap->GetImage(); - SetDragImageWithCGImage (inDrag, image, &offset, kDragStandardTranslucency); - } - - // Drag the text. TrackDrag will return userCanceledErr if the drop whooshed back for any reason. - inDragDrop = ddDragging; - OSErr error = TrackDrag(inDrag, &mouseDownEvent, dragRegion); - 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 - // source selection. Note that we can continute to get the attributes, drop location - // modifiers, etc. of the drag until we dispose of it using DisposeDrag. - if (error == noErr) { - GetDragAttributes(inDrag, &attributes); - if (!(attributes & kDragInsideSenderApplication)) - { - GetDropLocation(inDrag, &dropLocation); - - GetDragModifiers(inDrag, 0L, &mouseDownModifiers, &mouseUpModifiers); - copyText = (mouseDownModifiers | mouseUpModifiers) & optionKey; - - if ((!copyText) && (IsDropInFinderTrash(&dropLocation))) - { - // delete the selected text from the buffer - ClearSelection(); - } - - AEDisposeDesc(&dropLocation); - } - } - - // Dispose of this drag, 'cause we're done. - DisposeDrag(inDrag); - DisposeRgn(dragRegion); - - if (pixmap) { - CGImageRelease(image); - pixmap->Release(); - delete pixmap; - } -} - -void ScintillaMacOSX::SetDragCursor(DragRef inDrag) -{ - DragAttributes attributes; - SInt16 modifiers = 0; - ThemeCursor cursor = kThemeCopyArrowCursor; - GetDragAttributes( inDrag, &attributes ); - - if ( attributes & kDragInsideSenderWindow ) { - GetDragModifiers(inDrag, &modifiers, NULL, NULL); - switch (modifiers & ~btnState) // Filter out btnState (on for drop) - { - case optionKey: - // it's a copy, leave it as a copy arrow - break; - - case cmdKey: - case cmdKey | optionKey: - default: - // what to do with these? rectangular drag? - cursor = kThemeArrowCursor; - break; - } - } - SetThemeCursor(cursor); -} - -bool ScintillaMacOSX::DragEnter(DragRef inDrag ) -{ - if (!DragWithin(inDrag)) - return false; - - DragAttributes attributes; - GetDragAttributes( inDrag, &attributes ); - - // only show the drag hilight if the drag has left the sender window per HI spec - if( attributes & kDragHasLeftSenderWindow ) - { - HIRect textFrame; - RgnHandle hiliteRgn = NewRgn(); - - // get the text view's frame ... - HIViewGetFrame( GetViewRef(), &textFrame ); - - // ... and convert it into a region for ShowDragHilite - HIShapeRef textShape = HIShapeCreateWithRect( &textFrame ); - HIShapeGetAsQDRgn( textShape, hiliteRgn ); - CFRelease( textShape ); - - // add the drag hilight to the inside of the text view - ShowDragHilite( inDrag, hiliteRgn, true ); - - DisposeRgn( hiliteRgn ); - } - SetDragCursor(inDrag); - return true; -} - -Scintilla::Point ScintillaMacOSX::GetDragPoint(DragRef inDrag) -{ - ::Point mouse, globalMouse; - GetDragMouse(inDrag, &mouse, &globalMouse); - HIPoint hiPoint = GetLocalPoint (globalMouse); - return Point(static_cast<int>(hiPoint.x), static_cast<int>(hiPoint.y)); -} - - -void ScintillaMacOSX::DragScroll() -{ -#define RESET_SCROLL_TIMER(lines) \ - scrollSpeed = (lines); \ - scrollTicks = 2000; - - if (!posDrag.IsValid()) { - RESET_SCROLL_TIMER(1); - return; - } - Point dragMouse = LocationFromPosition(posDrag); - int line = pdoc->LineFromPosition(posDrag.Position()); - int currentVisibleLine = cs.DisplayFromDoc(line); - int lastVisibleLine = Platform::Minimum(topLine + LinesOnScreen() - 1, pdoc->LinesTotal() - 1); - - if (currentVisibleLine <= topLine && topLine > 0) { - ScrollTo( topLine - scrollSpeed ); - } else if (currentVisibleLine >= lastVisibleLine) { - ScrollTo( topLine + scrollSpeed ); - } else { - RESET_SCROLL_TIMER(1); - return; - } - if (scrollSpeed == 1) { - scrollTicks -= timer.tickSize; - if (scrollTicks <= 0) { - RESET_SCROLL_TIMER(5); - } - } - - SetDragPosition(SPositionFromLocation(dragMouse)); - -#undef RESET_SCROLL_TIMER -} - -bool ScintillaMacOSX::DragWithin(DragRef inDrag ) -{ - PasteboardRef pasteBoard; - bool isFileURL = false; - if (!GetDragData(inDrag, pasteBoard, NULL, &isFileURL)) { - return false; - } - - Point pt = GetDragPoint (inDrag); - SetDragPosition(SPositionFromLocation(pt)); - SetDragCursor(inDrag); - - return true; -} - -bool ScintillaMacOSX::DragLeave(DragRef inDrag ) -{ - HideDragHilite( inDrag ); - SetDragPosition(SelectionPosition(invalidPosition)); - WndProc(SCI_SETCURSOR, Window::cursorArrow, 0); - return true; -} - -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 false; - } - return GetPasteboardData(pasteBoard, selectedText, isFileURL); -} - -void ScintillaMacOSX::SetPasteboardData(PasteboardRef &theClipboard, const SelectionText &selectedText, bool inDragDropSession) -{ - 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((inDragDropSession - ? kPasteboardUniqueName - : 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; - OSStatus status = PasteboardGetItemCount(pasteBoard, &itemCount); - if (status != noErr) { - 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 - CFArrayRef flavorTypeArray = NULL; - bool haveMatch = false; - for (i = 1; i <= itemCount; i++) - { - PasteboardItemID itemID; - CFIndex j, flavorCount = 0; - - status = PasteboardGetItemIdentifier(pasteBoard, i, &itemID); - if (status != noErr) { - return false; - } - - // how many flavors in this item? - status = PasteboardCopyItemFlavors(pasteBoard, itemID, &flavorTypeArray); - if (status != noErr) { - return false; - } - - if (flavorTypeArray != NULL) - flavorCount = CFArrayGetCount(flavorTypeArray); - - // as long as we didn't get our text, let's loop on the flavors. We stop as soon as we get it - for(j = 0; j < flavorCount; j++) - { - CFDataRef flavorData; - CFStringRef flavorType = (CFStringRef)CFArrayGetValueAtIndex(flavorTypeArray, j); - 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) - { - CFIndex dataSize = CFDataGetLength (flavorData); - const UInt8* dataBytes = CFDataGetBytePtr (flavorData); - switch (format) - { - 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; - } - } - } - } -PasteboardDataRetrieved: - if (flavorTypeArray != NULL) CFRelease(flavorTypeArray); - 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; - } - if (haveMatch || selectedText != NULL && selectedText->s != NULL) { - return true; - } - return false; -} - -char *ScintillaMacOSX::GetStringFromCFString(CFStringRef &textString, int *textLen) -{ - - // Allocate a buffer, plus the null byte - CFIndex numUniChars = CFStringGetLength( textString ); - CFStringEncoding encoding = ( IsUnicodeMode() ? kCFStringEncodingUTF8 : kCFStringEncodingMacRoman); - CFIndex maximumByteLength = CFStringGetMaximumSizeForEncoding( numUniChars, encoding ) + 1; - char* cstring = new char[maximumByteLength]; - CFIndex usedBufferLength = 0; - CFIndex numCharsConverted; - numCharsConverted = CFStringGetBytes( textString, CFRangeMake( 0, numUniChars ), encoding, - '?', false, reinterpret_cast<UInt8*>( cstring ), - maximumByteLength, &usedBufferLength ); - cstring[usedBufferLength] = '\0'; // null terminate the ASCII/UTF8 string - - // 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 - *textLen = 0; - char *result = Document::TransformLineEnds(textLen, - cstring + bomLen, - usedBufferLength - bomLen, - pdoc->eolMode); - delete[] cstring; - return result; -} - -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 ); - - PasteboardRef pasteBoard; - SelectionText selectedText; - CFStringRef textString = NULL; - bool isFileURL = false; - if (!GetDragData(inDrag, pasteBoard, &selectedText, &isFileURL)) { - return dragNotAcceptedErr; - } - - if (isFileURL) { - NotifyURIDropped(selectedText.s); - } else { - // figure out if this is a move or a paste - DragAttributes attributes; - SInt16 modifiers = 0; - GetDragAttributes( inDrag, &attributes ); - bool moving = true; - - SelectionPosition position = SPositionFromLocation(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; - } - } - - DropAt(position, selectedText.s, moving, selectedText.rectangular); - } - - 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) { - case SCI_GETDIRECTFUNCTION: - Platform::DebugDisplay( "ScintillaMacOSX::WndProc: Returning DirectFunction address.\n" ); - return reinterpret_cast<sptr_t>( DirectFunction ); - - case SCI_GETDIRECTPOINTER: - Platform::DebugDisplay( "ScintillaMacOSX::WndProc: Returning Direct pointer address.\n" ); - return reinterpret_cast<sptr_t>( this ); - - case SCI_GRABFOCUS: - Platform::DebugDisplay( "ScintillaMacOSX::WndProc: Got an unhandled message. Ignoring it.\n" ); - break; - case WM_UNICHAR: - if (IsUnicodeMode()) { - // Extended UTF8-UTF6-conversion to handle surrogate pairs correctly (CL265070) - UniChar wcs[1] = { (UniChar) wParam}; - InsertCharacters(wcs, 1); - return 1; - } else { - return 0; - } - - default: - unsigned int r = ScintillaBase::WndProc(iMessage, wParam, lParam); - - return r; - } - return 0l; -} - -sptr_t ScintillaMacOSX::DefWndProc(unsigned int, uptr_t, sptr_t) { - return 0; -} - -void ScintillaMacOSX::SetTicking(bool on) { - if (timer.ticking != on) { - timer.ticking = on; - if (timer.ticking) { - // Scintilla ticks = milliseconds - EventLoopTimerRef timerRef = NULL; - InstallTimer( timer.tickSize * kEventDurationMillisecond, &timerRef ); - assert( timerRef != NULL ); - timer.tickerID = reinterpret_cast<TickerID>( timerRef ); - } else if ( timer.tickerID != NULL ) { - RemoveEventLoopTimer( reinterpret_cast<EventLoopTimerRef>( timer.tickerID ) ); - } - } - timer.ticksToWait = caret.period; -} - -bool ScintillaMacOSX::SetIdle(bool on) { - if (on) { - // Start idler, if it's not running. - if (idler.state == false) { - idler.state = true; - EventLoopTimerRef idlTimer; - InstallEventLoopIdleTimer(GetCurrentEventLoop(), - timer.tickSize * kEventDurationMillisecond, - 75 * kEventDurationMillisecond, - IdleTimerEventHandler, this, &idlTimer); - idler.idlerID = reinterpret_cast<IdlerID>( idlTimer ); - } - } else { - // Stop idler, if it's running - if (idler.state == true) { - idler.state = false; - if (idler.idlerID != NULL) - RemoveEventLoopTimer( reinterpret_cast<EventLoopTimerRef>( idler.idlerID ) ); - } - } - return true; -} - -pascal void ScintillaMacOSX::IdleTimerEventHandler( EventLoopTimerRef inTimer, - EventLoopIdleTimerMessage inState, - void *scintilla ) -{ - ScintillaMacOSX *sciThis = reinterpret_cast<ScintillaMacOSX*>( scintilla ); - bool ret = sciThis->Idle(); - if (ret == false) { - sciThis->SetIdle(false); - } -} - -void ScintillaMacOSX::SetMouseCapture(bool on) { - capturedMouse = on; - if (mouseDownCaptures) { - if (capturedMouse) { - WndProc(SCI_SETCURSOR, Window::cursorArrow, 0); - } else { - // reset to normal, buttonmove will change for other area's in the editor - WndProc(SCI_SETCURSOR, (long int)SC_CURSORNORMAL, 0); - } - } -} - -bool ScintillaMacOSX::HaveMouseCapture() { - return capturedMouse; -} - -// The default GetClientRectangle calls GetClientPosition on wMain. -// We override it to return "view local" co-ordinates so we can draw properly -// plus we need to remove the space occupied by the scroll bars -PRectangle ScintillaMacOSX::GetClientRectangle() { - PRectangle rc = wMain.GetClientPosition(); - if (verticalScrollBarVisible) - rc.right -= scrollBarFixedSize + 1; - if (horizontalScrollBarVisible && (wrapState == eWrapNone)) - rc.bottom -= scrollBarFixedSize + 1; - // Move to origin - rc.right -= rc.left; - rc.bottom -= rc.top; - rc.left = 0; - rc.top = 0; - return rc; -} - -// Synchronously paint a rectangle of the window. -void ScintillaMacOSX::SyncPaint(void* gc, PRectangle rc) { - paintState = painting; - rcPaint = rc; - PRectangle rcText = GetTextRectangle(); - paintingAllText = rcPaint.Contains(rcText); - //Platform::DebugPrintf("ScintillaMacOSX::SyncPaint %0d,%0d %0d,%0d\n", - // rcPaint.left, rcPaint.top, rcPaint.right, rcPaint.bottom); - Surface *sw = Surface::Allocate(); - if (sw) { - sw->Init( gc, wMain.GetID() ); - Paint(sw, rc); - if (paintState == paintAbandoned) { - // do a FULL paint. - rcPaint = GetClientRectangle(); - paintState = painting; - paintingAllText = true; - Paint(sw, rcPaint); - wMain.InvalidateAll(); - } - sw->Release(); - delete sw; - } - paintState = notPainting; -} - -void ScintillaMacOSX::ScrollText(int /*linesToMove*/) { - // This function will invalidate the correct regions of the view, - // So shortly after this happens, draw will be called. - // But I'm not quite sure how this works ... - // I have a feeling that it is only supposed to work in conjunction with an HIScrollView. - // TODO: Cook up my own bitblt scroll: Grab the bits on screen, blit them shifted, invalidate the remaining stuff - //CGRect r = CGRectMake( 0, 0, rc.Width(), rc.Height() ); - //HIViewScrollRect( reinterpret_cast<HIViewRef>( wMain.GetID() ), NULL, 0, vs.lineHeight * linesToMove ); - wMain.InvalidateAll(); -} - -void ScintillaMacOSX::SetVerticalScrollPos() { - SetControl32BitValue( vScrollBar, topLine ); -} - -void ScintillaMacOSX::SetHorizontalScrollPos() { - SetControl32BitValue( hScrollBar, xOffset ); -} - -bool ScintillaMacOSX::ModifyScrollBars(int nMax, int nPage) { - Platform::DebugPrintf( "nMax: %d nPage: %d hScroll (%d -> %d) page: %d\n", nMax, nPage, 0, scrollWidth, GetTextRectangle().Width() ); - // Minimum value = 0 - // TODO: This is probably not needed, since we set this when the scroll bars are created - SetControl32BitMinimum( vScrollBar, 0 ); - SetControl32BitMinimum( hScrollBar, 0 ); - - // Maximum vertical value = nMax + 1 - nPage (lines available to scroll) - SetControl32BitMaximum( vScrollBar, Platform::Maximum( nMax + 1 - nPage, 0 ) ); - // Maximum horizontal value = scrollWidth - GetTextRectangle().Width() (pixels available to scroll) - SetControl32BitMaximum( hScrollBar, Platform::Maximum( scrollWidth - GetTextRectangle().Width(), 0 ) ); - - // Vertical page size = nPage - SetControlViewSize( vScrollBar, nPage ); - // Horizontal page size = TextRectangle().Width() - SetControlViewSize( hScrollBar, GetTextRectangle().Width() ); - - // TODO: Verify what this return value is for - // The scroll bar components will handle if they need to be rerendered or not - return false; -} - -void ScintillaMacOSX::ReconfigureScrollBars() { - PRectangle rc = wMain.GetClientPosition(); - Resize(rc.Width(), rc.Height()); -} - -void ScintillaMacOSX::Resize(int width, int height) { - // Get the horizontal/vertical size of the scroll bars - GetThemeMetric( kThemeMetricScrollBarWidth, &scrollBarFixedSize ); - - bool showSBHorizontal = horizontalScrollBarVisible && (wrapState == eWrapNone); - HIRect scrollRect; - if (verticalScrollBarVisible) { - scrollRect.origin.x = width - scrollBarFixedSize; - scrollRect.origin.y = 0; - scrollRect.size.width = scrollBarFixedSize; - if (showSBHorizontal) { - scrollRect.size.height = Platform::Maximum(1, height - scrollBarFixedSize); - } else { - scrollRect.size.height = height; - } - - HIViewSetFrame( vScrollBar, &scrollRect ); - if (HIViewGetSuperview(vScrollBar) == NULL) { - HIViewSetDrawingEnabled( vScrollBar, true ); - HIViewSetVisible(vScrollBar, true); - HIViewAddSubview(GetViewRef(), vScrollBar ); - Draw1Control(vScrollBar); - } - } else if (HIViewGetSuperview(vScrollBar) != NULL) { - HIViewSetDrawingEnabled( vScrollBar, false ); - HIViewRemoveFromSuperview(vScrollBar); - } - - if (showSBHorizontal) { - scrollRect.origin.x = 0; - // Always draw the scrollbar to avoid the "potiential" horizontal scroll bar and to avoid the resize box. - // This should be "good enough". Best would be to avoid the resize box. - // Even better would be to embed Scintilla inside an HIScrollView, which would handle this for us. - scrollRect.origin.y = height - scrollBarFixedSize; - if (verticalScrollBarVisible) { - scrollRect.size.width = Platform::Maximum( 1, width - scrollBarFixedSize ); - } else { - scrollRect.size.width = width; - } - scrollRect.size.height = scrollBarFixedSize; - - HIViewSetFrame( hScrollBar, &scrollRect ); - if (HIViewGetSuperview(hScrollBar) == NULL) { - HIViewSetDrawingEnabled( hScrollBar, true ); - HIViewAddSubview( GetViewRef(), hScrollBar ); - Draw1Control(hScrollBar); - } - } else if (HIViewGetSuperview(hScrollBar) != NULL) { - HIViewSetDrawingEnabled( hScrollBar, false ); - HIViewRemoveFromSuperview(hScrollBar); - } - - ChangeSize(); - - // 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 ) -{ - 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; - OSStatus err; - err = GetControlProperty( control, scintillaMacOSType, 0, sizeof( scintilla ), NULL, &scintilla ); - assert( err == noErr && scintilla != NULL ); - - int singleScroll = 0; - if ( control == scintilla->vScrollBar ) - { - // Vertical single scroll = one line - // TODO: Is there a Scintilla preference for this somewhere? - singleScroll = 1; - } else { - assert( control == scintilla->hScrollBar ); - // Horizontal single scroll = 20 pixels (hardcoded from ScintillaWin) - // TODO: Is there a Scintilla preference for this somewhere? - singleScroll = 20; - } - - // Determine the new value - int newValue = 0; - switch ( part ) - { - case kControlUpButtonPart: - newValue = Platform::Maximum( currentValue - singleScroll, min ); - break; - - case kControlDownButtonPart: - // the the user scrolls to the right, allow more scroll space - if ( control == scintilla->hScrollBar && currentValue >= max) { - // change the max value - scintilla->scrollWidth += singleScroll; - SetControl32BitMaximum( control, - Platform::Maximum( scintilla->scrollWidth - scintilla->GetTextRectangle().Width(), 0 ) ); - max = GetControl32BitMaximum( control ); - scintilla->SetScrollBars(); - } - newValue = Platform::Minimum( currentValue + singleScroll, max ); - break; - - case kControlPageUpPart: - newValue = Platform::Maximum( currentValue - page, min ); - break; - - case kControlPageDownPart: - newValue = Platform::Minimum( currentValue + page, max ); - break; - - case kControlIndicatorPart: - case kControlNoPart: - newValue = currentValue; - break; - - default: - assert( false ); - return; - } - - // Set the new value - if ( control == scintilla->vScrollBar ) - { - scintilla->ScrollTo( newValue ); - } else { - assert( control == scintilla->hScrollBar ); - scintilla->HorizontalScrollTo( newValue ); - } -} - -bool ScintillaMacOSX::ScrollBarHit(HIPoint location) { - // is this on our scrollbars? If so, track them - HIViewRef view; - // view is null if on editor, otherwise on scrollbar - HIViewGetSubviewHit(reinterpret_cast<ControlRef>(wMain.GetID()), - &location, true, &view); - if (view) { - HIViewPartCode part; - - // make the point local to a scrollbar - PRectangle client = GetClientRectangle(); - if (view == vScrollBar) { - location.x -= client.Width(); - } else if (view == hScrollBar) { - location.y -= client.Height(); - } else { - fprintf(stderr, "got a subview hit, but not a scrollbar???\n"); - return false; - } - - HIViewGetPartHit(view, &location, &part); - - switch (part) - { - case kControlUpButtonPart: - case kControlDownButtonPart: - case kControlPageUpPart: - case kControlPageDownPart: - case kControlIndicatorPart: - ::Point p; - p.h = location.x; - p.v = location.y; - // We are assuming Appearance 1.1 or later, so we - // have the "live scroll" variant of the scrollbar, - // which lets you pass the action proc to TrackControl - // for the thumb (this was illegal in previous - // versions of the defproc). - isTracking = true; - ::TrackControl(view, p, ScintillaMacOSX::LiveScrollHandler); - ::HiliteControl(view, 0); - isTracking = false; - // The mouseup was eaten by TrackControl, however if we - // do not get a mouseup in the scintilla xbl widget, - // many bad focus issues happen. Simply post a mouseup - // and this firey pit becomes a bit cooler. - PostEvent(mouseUp, 0); - break; - default: - fprintf(stderr, "PlatformScrollBarHit part %d\n", part); - } - return true; - } - return false; -} - -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()); -} - -void ScintillaMacOSX::NotifyChange() { - if (NULL != notifyProc) - notifyProc (notifyObj, WM_COMMAND, - (uintptr_t) (SCEN_CHANGE << 16), - (uintptr_t) GetViewRef()); -} - -void ScintillaMacOSX::registerNotifyCallback(intptr_t windowid, SciNotifyFunc callback) { - notifyObj = windowid; - notifyProc = callback; -} - -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) { - SCNotification scn; - scn.nmhdr.code = SCN_KEY; - scn.ch = key; - scn.modifiers = modifiers; - - NotifyParent(scn); -} - -void ScintillaMacOSX::NotifyURIDropped(const char *list) { - SCNotification scn; - scn.nmhdr.code = SCN_URIDROPPED; - scn.text = 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); - return 1; - } else { - // Pass up to container in case it is an accelerator - NotifyKey(key, modifiers); - return 0; - } - //Platform::DebugPrintf("SK-key: %d %x %x\n",key, modifiers); -} -#endif - -template <class T, class U> -struct StupidMap -{ -public: - T key; - U value; -}; - -template <class T, class U> -inline static U StupidMapFindFunction( const StupidMap<T, U>* elements, size_t length, const T& desiredKey ) -{ - for ( size_t i = 0; i < length; ++ i ) - { - if ( elements[i].key == desiredKey ) - { - return elements[i].value; - } - } - - return NULL; -} - -// NOTE: If this macro is used on a StupidMap that isn't defined by StupidMap x[] = ... -// The size calculation will fail! -#define StupidMapFind( x, y ) StupidMapFindFunction( x, sizeof(x)/sizeof(*x), y ) - -pascal OSStatus ScintillaMacOSX::CommandEventHandler( EventHandlerCallRef /*inCallRef*/, EventRef event, void* data ) -{ - // TODO: Verify automatically that each constant only appears once? - const StupidMap<UInt32, void (ScintillaMacOSX::*)()> processCommands[] = { - { kHICommandCopy, &ScintillaMacOSX::Copy }, - { kHICommandPaste, &ScintillaMacOSX::Paste }, - { kHICommandCut, &ScintillaMacOSX::Cut }, - { kHICommandUndo, &ScintillaMacOSX::Undo }, - { kHICommandRedo, &ScintillaMacOSX::Redo }, - { kHICommandClear, &ScintillaMacOSX::ClearSelectionSimple }, - { kHICommandSelectAll, &ScintillaMacOSX::SelectAll }, - }; - const StupidMap<UInt32, bool (ScintillaMacOSX::*)()> canProcessCommands[] = { - { kHICommandCopy, &ScintillaMacOSX::HasSelection }, - { kHICommandPaste, &ScintillaMacOSX::CanPaste }, - { kHICommandCut, &ScintillaMacOSX::HasSelection }, - { kHICommandUndo, &ScintillaMacOSX::CanUndo }, - { kHICommandRedo, &ScintillaMacOSX::CanRedo }, - { kHICommandClear, &ScintillaMacOSX::HasSelection }, - { kHICommandSelectAll, &ScintillaMacOSX::AlwaysTrue }, - }; - - HICommand command; - OSStatus result = GetEventParameter( event, kEventParamDirectObject, typeHICommand, NULL, sizeof( command ), NULL, &command ); - assert( result == noErr ); - - UInt32 kind = GetEventKind( event ); - Platform::DebugPrintf("ScintillaMacOSX::CommandEventHandler kind %d\n", kind); - - ScintillaMacOSX* scintilla = reinterpret_cast<ScintillaMacOSX*>( data ); - assert( scintilla != NULL ); - - 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 ); - - if ( methodPtr != NULL ) - { - // Call the method if we found it, and tell the caller that we handled this event - (scintilla->*methodPtr)(); - result = noErr; - } else { - // tell the caller that we did not handle the event - result = eventNotHandledErr; - } - } - // The default Mac OS X text editor does not handle these events to enable/disable menu items - // Why not? I think it should, so Scintilla does. - else if ( kind == kEventCommandUpdateStatus && ( command.attributes & kHICommandFromMenu ) ) - { - // Find the method pointer that matches this command - bool (ScintillaMacOSX::*methodPtr)() = StupidMapFind( canProcessCommands, command.commandID ); - - if ( methodPtr != NULL ) { - // Call the method if we found it: enabling/disabling menu items - if ( (scintilla->*methodPtr)() ) { - EnableMenuItem( command.menu.menuRef, command.menu.menuItemIndex ); - } else { - DisableMenuItem( command.menu.menuRef, command.menu.menuItemIndex ); - } - result = noErr; - } else { - // tell the caller that we did not handle the event - result = eventNotHandledErr; - } - } else { - // Unhandled event: We should never get here - assert( false ); - result = eventNotHandledErr; - } - - return result; -} - -bool ScintillaMacOSX::HasSelection() -{ - return ( !sel.Empty() ); -} - -bool ScintillaMacOSX::CanUndo() -{ - return pdoc->CanUndo(); -} - -bool ScintillaMacOSX::CanRedo() -{ - return pdoc->CanRedo(); -} - -bool ScintillaMacOSX::AlwaysTrue() -{ - return true; -} - -void ScintillaMacOSX::CopyToClipboard(const SelectionText &selectedText) { - PasteboardRef theClipboard; - SetPasteboardData(theClipboard, selectedText, false); // not in drag/drop - // Done with the CFString - CFRelease( theClipboard ); -} - -void ScintillaMacOSX::Copy() -{ - if (!sel.Empty()) { -#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() -{ - if (!Editor::CanPaste()) - return false; - - PasteboardRef theClipboard; - bool isFileURL = false; - - PasteboardCreate( kPasteboardClipboard, &theClipboard ); - bool ok = GetPasteboardData(theClipboard, NULL, &isFileURL); - CFRelease( theClipboard ); - return ok; -} - -void ScintillaMacOSX::Paste() -{ - Paste(false); -} - -// 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 forceRectangular) -{ - 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; - - pdoc->BeginUndoAction(); - ClearSelection(); - if (selectedText.rectangular) { - SelectionPosition selStart = sel.RangeMain().Start(); - PasteRectangular(selStart, selectedText.s, selectedText.len); - } else - if ( pdoc->InsertString( sel.RangeMain().caret.Position(), selectedText.s, selectedText.len ) ) { - SetEmptySelection( sel.RangeMain().caret.Position() + selectedText.len ); - } - - pdoc->EndUndoAction(); - - Redraw(); - EnsureCaretVisible(); -} - -void ScintillaMacOSX::CreateCallTipWindow(PRectangle rc) { - // create a calltip window - if (!ct.wCallTip.Created()) { - WindowClass windowClass = kHelpWindowClass; - WindowAttributes attributes = kWindowNoAttributes; - Rect contentBounds; - WindowRef outWindow; - - // convert PRectangle to Rect - // this adjustment gets the calltip window placed in the correct location relative - // to our editor window - Rect bounds; - OSStatus err; - err = GetWindowBounds( this->GetOwner(), kWindowGlobalPortRgn, &bounds ); - assert( err == noErr ); - contentBounds.top = rc.top + bounds.top; - contentBounds.bottom = rc.bottom + bounds.top; - contentBounds.right = rc.right + bounds.left; - contentBounds.left = rc.left + bounds.left; - - // create our calltip hiview - HIViewRef ctw = scintilla_calltip_new(); - CallTip* objectPtr = &ct; - ScintillaMacOSX* sciThis = this; - SetControlProperty( ctw, scintillaMacOSType, 0, sizeof( this ), &sciThis ); - SetControlProperty( ctw, scintillaCallTipType, 0, sizeof( objectPtr ), &objectPtr ); - - CreateNewWindow(windowClass, attributes, &contentBounds, &outWindow); - ControlRef root; - CreateRootControl(outWindow, &root); - - HIViewRef hiroot = HIViewGetRoot (outWindow); - HIViewAddSubview(hiroot, ctw); - - HIRect boundsRect; - HIViewGetFrame(hiroot, &boundsRect); - HIViewSetFrame( ctw, &boundsRect ); - - // bind the size of the calltip to the size of it's container window - HILayoutInfo layout = { - kHILayoutInfoVersionZero, - { - { NULL, kHILayoutBindTop, 0 }, - { NULL, kHILayoutBindLeft, 0 }, - { NULL, kHILayoutBindBottom, 0 }, - { NULL, kHILayoutBindRight, 0 } - }, - { - { NULL, kHILayoutScaleAbsolute, 0 }, - { NULL, kHILayoutScaleAbsolute, 0 } - - }, - { - { NULL, kHILayoutPositionTop, 0 }, - { NULL, kHILayoutPositionLeft, 0 } - } - }; - HIViewSetLayoutInfo(ctw, &layout); - - ct.wCallTip = root; - ct.wDraw = ctw; - ct.wCallTip.SetWindow(outWindow); - HIViewSetVisible(ctw,true); - - } -} - -void ScintillaMacOSX::CallTipClick() -{ - ScintillaBase::CallTipClick(); -} - -void ScintillaMacOSX::AddToPopUp( const char *label, int cmd, bool enabled ) -{ - // Translate stuff into menu item attributes - MenuItemAttributes attributes = 0; - if ( label[0] == '\0' ) attributes |= kMenuItemAttrSeparator; - if ( ! enabled ) attributes |= kMenuItemAttrDisabled; - - // Translate Scintilla commands into Mac OS commands - // TODO: If I create an AEDesc, OS X may insert these standard - // text editing commands into the menu for me - MenuCommand macCommand; - switch( cmd ) - { - case idcmdUndo: - macCommand = kHICommandUndo; - break; - case idcmdRedo: - macCommand = kHICommandRedo; - break; - case idcmdCut: - macCommand = kHICommandCut; - break; - case idcmdCopy: - macCommand = kHICommandCopy; - break; - case idcmdPaste: - macCommand = kHICommandPaste; - break; - case idcmdDelete: - macCommand = kHICommandClear; - break; - case idcmdSelectAll: - macCommand = kHICommandSelectAll; - break; - case 0: - macCommand = 0; - break; - default: - assert( false ); - return; - } - - CFStringRef string = CFStringCreateWithCString( NULL, label, kCFStringEncodingUTF8 ); - OSStatus err; - err = AppendMenuItemTextWithCFString( reinterpret_cast<MenuRef>( popup.GetID() ), - string, attributes, macCommand, NULL ); - assert( err == noErr ); - - CFRelease( string ); - string = NULL; -} - -void ScintillaMacOSX::ClaimSelection() { - // Mac OS X does not have a primary selection -} - -/** A wrapper function to permit external processes to directly deliver messages to our "message loop". */ -sptr_t ScintillaMacOSX::DirectFunction( - ScintillaMacOSX *sciThis, unsigned int iMessage, uptr_t wParam, sptr_t lParam) { - return sciThis->WndProc(iMessage, wParam, lParam); -} - -sptr_t scintilla_send_message(void* sci, unsigned int iMessage, uptr_t wParam, sptr_t lParam) { - HIViewRef control = reinterpret_cast<HIViewRef>(sci); - // Platform::DebugPrintf("scintilla_send_message %08X control %08X\n",sci,control); - // Get a reference to the Scintilla C++ object - ScintillaMacOSX* scintilla = NULL; - OSStatus err; - err = GetControlProperty( control, scintillaMacOSType, 0, sizeof( scintilla ), NULL, &scintilla ); - assert( err == noErr && scintilla != NULL ); - //Platform::DebugPrintf("scintilla_send_message scintilla %08X\n",scintilla); - - return scintilla->WndProc(iMessage, wParam, lParam); -} - -void ScintillaMacOSX::TimerFired( EventLoopTimerRef ) -{ - Tick(); - DragScroll(); -} - -OSStatus ScintillaMacOSX::BoundsChanged( UInt32 /*inOptions*/, const HIRect& inOriginalBounds, const HIRect& inCurrentBounds, RgnHandle /*inInvalRgn*/ ) -{ - // If the width or height changed, modify the scroll bars and notify Scintilla - // This event is also delivered when the window moves, and we don't care about that - if ( inOriginalBounds.size.width != inCurrentBounds.size.width || inOriginalBounds.size.height != inCurrentBounds.size.height ) - { - Resize( static_cast<int>( inCurrentBounds.size.width ), static_cast<int>( inCurrentBounds.size.height ) ); - } - return noErr; -} - -void ScintillaMacOSX::Draw( RgnHandle rgn, CGContextRef gc ) -{ - Rect invalidRect; - GetRegionBounds( rgn, &invalidRect ); - - // NOTE: We get draw events that include the area covered by the scroll bar. No fear: Scintilla correctly ignores them - SyncPaint( gc, PRectangle( invalidRect.left, invalidRect.top, invalidRect.right, invalidRect.bottom ) ); -} - -ControlPartCode ScintillaMacOSX::HitTest( const HIPoint& where ) -{ - if ( CGRectContainsPoint( Bounds(), where ) ) - return 1; - else - return kControlNoPart; -} - -OSStatus ScintillaMacOSX::SetFocusPart( ControlPartCode desiredFocus, RgnHandle /*invalidRgn*/, Boolean /*inFocusEverything*/, ControlPartCode* outActualFocus ) -{ - assert( outActualFocus != NULL ); - - if ( desiredFocus == 0 ) { - // We are losing the focus - SetFocusState(false); - } else { - // We are getting the focus - SetFocusState(true); - } - - *outActualFocus = desiredFocus; - return noErr; -} - -// Map Mac Roman character codes to their equivalent Scintilla codes -static inline int KeyTranslate( UniChar unicodeChar ) -{ - switch ( unicodeChar ) - { - case kDownArrowCharCode: - return SCK_DOWN; - case kUpArrowCharCode: - return SCK_UP; - case kLeftArrowCharCode: - return SCK_LEFT; - case kRightArrowCharCode: - return SCK_RIGHT; - case kHomeCharCode: - 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 - case kHelpCharCode: - return SCK_INSERT; - 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: - return SCK_BACK; - case '\t': - return SCK_TAB; - case '+': - return SCK_ADD; - case '-': - return SCK_SUBTRACT; - case '/': - return SCK_DIVIDE; - case kFunctionKeyCharCode: - return kFunctionKeyCharCode; - default: - return 0; -#endif - } -} - -static inline UniChar GetCharacterWithoutModifiers( EventRef rawKeyboardEvent ) -{ - UInt32 keyCode; - // Get the key code from the raw key event - GetEventParameter( rawKeyboardEvent, kEventParamKeyCode, typeUInt32, NULL, sizeof( keyCode ), NULL, &keyCode ); - - // Get the current keyboard layout - // TODO: If this is a performance sink, we need to cache these values - SInt16 lastKeyLayoutID = GetScriptVariable( /*currentKeyScript*/ GetScriptManagerVariable(smKeyScript), smScriptKeys); - Handle uchrHandle = GetResource('uchr', lastKeyLayoutID); - - if (uchrHandle) { - // Translate the key press ignoring ctrl and option - UInt32 ignoredDeadKeys = 0; - UInt32 ignoredActualLength = 0; - UniChar unicodeKey = 0; - // (((modifiers & shiftKey) >> 8) & 0xFF) - OSStatus err; - err = UCKeyTranslate( reinterpret_cast<UCKeyboardLayout*>( *uchrHandle ), keyCode, kUCKeyActionDown, - /* modifierKeyState */ 0, LMGetKbdType(), kUCKeyTranslateNoDeadKeysMask, &ignoredDeadKeys, - /* buffer length */ 1, - /* actual length */ &ignoredActualLength, - /* string */ &unicodeKey ); - assert( err == noErr ); - - return unicodeKey; - } - return 0; -} - -// Text input is very annoying: -// If the control key is pressed, or if the key is a "special" key (eg. arrow keys, function keys, whatever) -// we let Scintilla handle it. If scintilla does not handle it, we do nothing (eventNotHandledErr). -// Otherwise, the event is just some text and we add it to the buffer -OSStatus ScintillaMacOSX::TextInput( TCarbonEvent& event ) -{ - // Obtain the number of bytes of text - UInt32 actualSize = 0; - OSStatus err; - err = event.GetParameterSize( kEventParamTextInputSendText, &actualSize ); - assert( err == noErr ); - assert( actualSize != 0 ); - - 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 ); - - // Get a copy of the text - err = event.GetParameter( kEventParamTextInputSendText, typeUnicodeText, actualSize, text ); - assert( err == noErr ); - - // TODO: This is a gross hack to ignore function keys - // Surely we can do better? - if ( numUniChars == 1 && text[0] == kFunctionKeyCharCode ) return eventNotHandledErr; - int modifiers = GetCurrentEventKeyModifiers(); - int scintillaKey = KeyTranslate( text[0] ); - - // Create a CFString which wraps and takes ownership of the "text" buffer - CFStringRef string = CFStringCreateWithCharactersNoCopy( NULL, text, numUniChars, NULL ); - assert( string != NULL ); - //delete text; - text = NULL; - - // If we have a single unicode character that is special or - // to process a command. Try to do some translation. - if ( numUniChars == 1 && ( modifiers & controlKey || scintillaKey != 0 ) ) { - // If we have a modifier, we need to get the character without modifiers - if ( modifiers & controlKey ) { - EventRef rawKeyboardEvent = NULL; - event.GetParameter( - kEventParamTextInputSendKeyboardEvent, - typeEventRef, - sizeof( EventRef ), - &rawKeyboardEvent ); - assert( rawKeyboardEvent != NULL ); - scintillaKey = GetCharacterWithoutModifiers( rawKeyboardEvent ); - - // Make sure that we still handle special characters correctly - int temp = KeyTranslate( scintillaKey ); - if ( temp != 0 ) scintillaKey = temp; - - // TODO: This is a gross Unicode hack: ASCII chars have a value < 127 - if ( scintillaKey <= 127 ) { - scintillaKey = toupper( (char) scintillaKey ); - } - } - - // Code taken from Editor::KeyDown - // It is copied here because we don't want to feed the key via - // KeyDefault if there is no special action - DwellEnd(false); - int scintillaModifiers = ( (modifiers & shiftKey) ? SCI_SHIFT : 0) | ( (modifiers & controlKey) ? SCI_CTRL : 0) | - ( (modifiers & optionKey) ? SCI_ALT : 0); - int msg = kmap.Find( scintillaKey, scintillaModifiers ); - if (msg) { - // The keymap has a special event for this key: perform the operation - WndProc(msg, 0, 0); - err = noErr; - } else { - // We do not handle this event - err = eventNotHandledErr; - } - } else { - CFStringEncoding encoding = ( IsUnicodeMode() ? kCFStringEncodingUTF8 : kCFStringEncodingASCII); - - // Allocate the buffer (don't forget the null!) - CFIndex maximumByteLength = CFStringGetMaximumSizeForEncoding( numUniChars, encoding ) + 1; - char* buffer = new char[maximumByteLength]; - - CFIndex usedBufferLength = 0; - CFIndex numCharsConverted; - numCharsConverted = CFStringGetBytes( string, CFRangeMake( 0, numUniChars ), encoding, - '?', false, reinterpret_cast<UInt8*>( buffer ), - maximumByteLength, &usedBufferLength ); - assert( numCharsConverted == numUniChars ); - buffer[usedBufferLength] = '\0'; // null terminate - - // Add all the characters to the document - // NOTE: OS X doesn't specify that text input events provide only a single character - // if we get a single character, add it as a character - // otherwise, we insert the entire string - if ( numUniChars == 1 ) { - AddCharUTF( buffer, usedBufferLength ); - } else { - // WARNING: This is an untested code path as with my US keyboard, I only enter a single character at a time - if (pdoc->InsertString(sel.RangeMain().caret.Position(), buffer, usedBufferLength)) { - SetEmptySelection(sel.RangeMain().caret.Position() + usedBufferLength); - } - } - - // Free the buffer that was allocated - delete[] buffer; - buffer = NULL; - err = noErr; - } - - // Default allocator releases both the CFString and the UniChar buffer (text) - CFRelease( string ); - string = NULL; - - return err; -#endif -} - -UInt32 ScintillaMacOSX::GetBehaviors() -{ - return TView::GetBehaviors() | kControlGetsFocusOnClick | kControlSupportsEmbedding; -} - -OSStatus ScintillaMacOSX::MouseEntered(HIPoint& location, UInt32 /*inKeyModifiers*/, EventMouseButton /*inMouseButton*/, UInt32 /*inClickCount*/ ) -{ - if (!HaveMouseCapture() && HIViewGetSuperview(GetViewRef()) != NULL) { - HIViewRef view; - HIViewGetSubviewHit(reinterpret_cast<ControlRef>(wMain.GetID()), &location, true, &view); - if (view) { - // the hit is on a subview (ie. scrollbars) - WndProc(SCI_SETCURSOR, Window::cursorArrow, 0); - } 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; - } - return eventNotHandledErr; -} - -OSStatus ScintillaMacOSX::MouseExited(HIPoint& location, UInt32 modifiers, EventMouseButton button, UInt32 clickCount ) -{ - if (HIViewGetSuperview(GetViewRef()) != NULL) { - if (HaveMouseCapture()) { - ButtonUp( Scintilla::Point( static_cast<int>( location.x ), static_cast<int>( location.y ) ), - static_cast<int>( GetCurrentEventTime() / kEventDurationMillisecond ), - (modifiers & controlKey) != 0 ); - } - WndProc(SCI_SETCURSOR, Window::cursorArrow, 0); - return noErr; - } - return eventNotHandledErr; -} - - -OSStatus ScintillaMacOSX::MouseDown( HIPoint& location, UInt32 modifiers, EventMouseButton button, UInt32 clickCount , TCarbonEvent& inEvent) -{ - ConvertEventRefToEventRecord( inEvent.GetEventRef(), &mouseDownEvent ); - return MouseDown(location, modifiers, button, clickCount); -} - -OSStatus ScintillaMacOSX::MouseDown( EventRecord *event ) -{ - HIPoint pt = GetLocalPoint(event->where); - int button = kEventMouseButtonPrimary; - mouseDownEvent = *event; - - if ( event->modifiers & controlKey ) - button = kEventMouseButtonSecondary; - return MouseDown(pt, event->modifiers, button, 1); -} - -OSStatus ScintillaMacOSX::MouseDown( HIPoint& location, UInt32 modifiers, EventMouseButton button, UInt32 /*clickCount*/ ) -{ - // We only deal with the first mouse button - if ( button != kEventMouseButtonPrimary ) return eventNotHandledErr; - // TODO: Verify that Scintilla wants the time in milliseconds - if (!HaveMouseCapture() && HIViewGetSuperview(GetViewRef()) != NULL) { - if (ScrollBarHit(location)) return noErr; - } - ButtonDown( Scintilla::Point( static_cast<int>( location.x ), static_cast<int>( location.y ) ), - static_cast<int>( GetCurrentEventTime() / kEventDurationMillisecond ), - (modifiers & shiftKey) != 0, - (modifiers & controlKey) != 0, - (modifiers & cmdKey) ); -#if !defined(CONTAINER_HANDLES_EVENTS) - OSStatus err; - err = SetKeyboardFocus( this->GetOwner(), this->GetViewRef(), 1 ); - ::SetUserFocusWindow(::HIViewGetWindow( this->GetViewRef() )); - return noErr; -#else - return eventNotHandledErr; // allow event to go to container -#endif -} - -OSStatus ScintillaMacOSX::MouseUp( EventRecord *event ) -{ - HIPoint pt = GetLocalPoint(event->where); - int button = kEventMouseButtonPrimary; - if ( event->modifiers & controlKey ) - button = kEventMouseButtonSecondary; - return MouseUp(pt, event->modifiers, button, 1); -} - -OSStatus ScintillaMacOSX::MouseUp( HIPoint& location, UInt32 modifiers, EventMouseButton button, UInt32 /*clickCount*/ ) -{ - // We only deal with the first mouse button - if ( button != kEventMouseButtonPrimary ) return eventNotHandledErr; - ButtonUp( Scintilla::Point( static_cast<int>( location.x ), static_cast<int>( location.y ) ), - static_cast<int>( GetCurrentEventTime() / kEventDurationMillisecond ), - (modifiers & controlKey) != 0 ); - -#if !defined(CONTAINER_HANDLES_EVENTS) - return noErr; -#else - return eventNotHandledErr; // allow event to go to container -#endif -} - -OSStatus ScintillaMacOSX::MouseDragged( EventRecord *event ) -{ - HIPoint pt = GetLocalPoint(event->where); - int button = 0; - if ( event->modifiers & btnStateBit ) { - button = kEventMouseButtonPrimary; - if ( event->modifiers & controlKey ) - button = kEventMouseButtonSecondary; - } - return MouseDragged(pt, event->modifiers, button, 1); -} - -OSStatus ScintillaMacOSX::MouseDragged( HIPoint& location, UInt32 modifiers, EventMouseButton button, UInt32 clickCount ) -{ -#if !defined(CONTAINER_HANDLES_EVENTS) - ButtonMove( Scintilla::Point( static_cast<int>( location.x ), static_cast<int>( location.y ) ) ); - return noErr; -#else - if (HaveMouseCapture() && !inDragDrop) { - MouseTrackingResult mouseStatus = 0; - ::Point theQDPoint; - UInt32 outModifiers; - EventTimeout inTimeout=0.1; - while (mouseStatus != kMouseTrackingMouseReleased) { - ButtonMove( Scintilla::Point( static_cast<int>( location.x ), static_cast<int>( location.y ) ) ); - TrackMouseLocationWithOptions((GrafPtr)-1, - kTrackMouseLocationOptionDontConsumeMouseUp, - inTimeout, - &theQDPoint, - &outModifiers, - &mouseStatus); - location = GetLocalPoint(theQDPoint); - } - ButtonUp( Scintilla::Point( static_cast<int>( location.x ), static_cast<int>( location.y ) ), - static_cast<int>( GetCurrentEventTime() / kEventDurationMillisecond ), - (modifiers & controlKey) != 0 ); - } else { - if (!HaveMouseCapture() && HIViewGetSuperview(GetViewRef()) != NULL) { - HIViewRef view; - HIViewGetSubviewHit(reinterpret_cast<ControlRef>(wMain.GetID()), &location, true, &view); - if (view) { - // the hit is on a subview (ie. scrollbars) - WndProc(SCI_SETCURSOR, Window::cursorArrow, 0); - return eventNotHandledErr; - } else { - // reset to normal, buttonmove will change for other area's in the editor - WndProc(SCI_SETCURSOR, (long int)SC_CURSORNORMAL, 0); - } - } - ButtonMove( Scintilla::Point( static_cast<int>( location.x ), static_cast<int>( location.y ) ) ); - } - return eventNotHandledErr; // allow event to go to container -#endif -} - -OSStatus ScintillaMacOSX::MouseWheelMoved( EventMouseWheelAxis axis, SInt32 delta, UInt32 modifiers ) -{ - if ( axis != 1 ) return eventNotHandledErr; - - if ( modifiers & controlKey ) { - // Zoom! We play with the font sizes in the styles. - // Number of steps/line is ignored, we just care if sizing up or down - if ( delta > 0 ) { - KeyCommand( SCI_ZOOMIN ); - } else { - KeyCommand( SCI_ZOOMOUT ); - } - } else { - // Decide if this should be optimized? - ScrollTo( topLine - delta ); - } - - return noErr; -} - -OSStatus ScintillaMacOSX::ContextualMenuClick( HIPoint& location ) -{ - // convert screen coords to window relative - Rect bounds; - OSStatus err; - err = GetWindowBounds( this->GetOwner(), kWindowContentRgn, &bounds ); - assert( err == noErr ); - location.x += bounds.left; - location.y += bounds.top; - ContextMenu( Scintilla::Point( static_cast<int>( location.x ), static_cast<int>( location.y ) ) ); - return noErr; -} - -OSStatus ScintillaMacOSX::ActiveStateChanged() -{ - // If the window is being deactivated, lose the focus and turn off the ticking - if ( ! this->IsActive() ) { - DropCaret(); - //SetFocusState( false ); - SetTicking( false ); - } else { - ShowCaretAtCurrentPosition(); - } - return noErr; -} - -HIViewRef ScintillaMacOSX::Create() -{ - // Register the HIView, if needed - static bool registered = false; - - if ( not registered ) { - TView::RegisterSubclass( kScintillaClassID, Construct ); - registered = true; - } - - OSStatus err = noErr; - EventRef event = CreateInitializationEvent(); - assert( event != NULL ); - - HIViewRef control = NULL; - err = HIObjectCreate( kScintillaClassID, event, reinterpret_cast<HIObjectRef*>( &control ) ); - ReleaseEvent( event ); - if ( err == noErr ) { - Platform::DebugPrintf("ScintillaMacOSX::Create control %08X\n",control); - return control; - } - return NULL; -} - -OSStatus ScintillaMacOSX::Construct( HIViewRef inControl, TView** outView ) -{ - *outView = new ScintillaMacOSX( inControl ); - Platform::DebugPrintf("ScintillaMacOSX::Construct scintilla %08X\n",*outView); - if ( *outView != NULL ) - return noErr; - else - return memFullErr; // could be a lie -} - -extern "C" { -HIViewRef scintilla_new() { - return ScintillaMacOSX::Create(); -} -} |