aboutsummaryrefslogtreecommitdiffhomepage
path: root/cocoa/ScintillaCocoa.mm
diff options
context:
space:
mode:
Diffstat (limited to 'cocoa/ScintillaCocoa.mm')
-rw-r--r--cocoa/ScintillaCocoa.mm1430
1 files changed, 1430 insertions, 0 deletions
diff --git a/cocoa/ScintillaCocoa.mm b/cocoa/ScintillaCocoa.mm
new file mode 100644
index 000000000..b0456a53d
--- /dev/null
+++ b/cocoa/ScintillaCocoa.mm
@@ -0,0 +1,1430 @@
+
+/**
+ * Scintilla source code edit control
+ * ScintillaCocoa.mm - Cocoa subclass of ScintillaBase
+ *
+ * Mike Lischke <mlischke@sun.com>
+ *
+ * Loosely based on ScintillaMacOSX.cxx.
+ * 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.
+ *
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * This file is dual licensed under LGPL v2.1 and the Scintilla license (http://www.scintilla.org/License.txt).
+ */
+
+#include "ScintillaCocoa.h"
+#include "ScintillaView.h"
+
+#include <Carbon/Carbon.h> // Temporary
+
+using namespace Scintilla;
+
+#ifndef WM_UNICHAR
+#define WM_UNICHAR 0x0109
+#endif
+
+NSString* ScintillaRecPboardType = @"com.scintilla.utf16-plain-text.rectangular";
+
+//--------------------------------------------------------------------------------------------------
+
+// Define keyboard shortcuts (equivalents) the Mac way.
+#define SCI_CMD ( SCI_ALT | SCI_CTRL)
+#define SCI_SCMD ( SCI_CMD | 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_LEFT, SCI_SCMD, SCI_VCHOMEEXTEND},
+ {SCK_RIGHT, SCI_CMD, SCI_LINEEND},
+ {SCK_RIGHT, SCI_SCMD, SCI_LINEENDEXTEND},
+ {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_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_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_CMD, SCI_UNDO},
+ {'y', SCI_CMD, SCI_REDO},
+ {'x', SCI_CMD, SCI_CUT},
+ {'c', SCI_CMD, SCI_COPY},
+ {'v', SCI_CMD, SCI_PASTE},
+ {'a', SCI_CMD, 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_CMD, SCI_ZOOMIN},
+ {SCK_SUBTRACT, SCI_CMD, SCI_ZOOMOUT},
+ {SCK_DIVIDE, SCI_CMD, SCI_SETZOOM},
+ {'l', SCI_CMD, 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},
+};
+
+//--------------------------------------------------------------------------------------------------
+
+@implementation TimerTarget
+
+- (id) init: (void*) target
+{
+ [super init];
+ if (self != nil)
+ {
+ mTarget = target;
+
+ // Get the default notification queue for the thread which created the instance (usually the
+ // main thread). We need that later for idle event processing.
+ NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
+ notificationQueue = [[NSNotificationQueue alloc] initWithNotificationCenter: center];
+ [center addObserver: self selector: @selector(idleTriggered:) name: @"Idle" object: nil];
+ }
+ return self;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Method called by a timer installed by ScintillaCocoa. This two step approach is needed because
+ * a native Obj-C class is required as target for the timer.
+ */
+- (void) timerFired: (NSTimer*) timer
+{
+ reinterpret_cast<ScintillaCocoa*>(mTarget)->TimerFired(timer);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Another timer callback for the idle timer.
+ */
+- (void) idleTimerFired: (NSTimer*) timer
+{
+ // Idle timer event.
+ // Post a new idle notification, which gets executed when the run loop is idle.
+ // Since we are coalescing on name and sender there will always be only one actual notification
+ // even for multiple requests.
+ NSNotification *notification = [NSNotification notificationWithName: @"Idle" object: self];
+ [notificationQueue enqueueNotification: notification
+ postingStyle: NSPostWhenIdle
+ coalesceMask: (NSNotificationCoalescingOnName | NSNotificationCoalescingOnSender)
+ forModes: nil];
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Another step for idle events. The timer (for idle events) simply requests a notification on
+ * idle time. Only when this notification is send we actually call back the editor.
+ */
+- (void) idleTriggered: (NSNotification*) notification
+{
+ reinterpret_cast<ScintillaCocoa*>(mTarget)->IdleTimerFired();
+}
+
+@end
+
+//----------------- ScintillaCocoa -----------------------------------------------------------------
+
+ScintillaCocoa::ScintillaCocoa(NSView* view)
+{
+ wMain= [view retain];
+ timerTarget = [[[TimerTarget alloc] init: this] retain];
+ Initialise();
+}
+
+//--------------------------------------------------------------------------------------------------
+
+ScintillaCocoa::~ScintillaCocoa()
+{
+ SetTicking(false);
+ [timerTarget release];
+ NSView* container = ContentView();
+ [container release];
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Core initialization of the control. Everything that needs to be set up happens here.
+ */
+void ScintillaCocoa::Initialise()
+{
+ notifyObj = NULL;
+ notifyProc = NULL;
+
+ capturedMouse = false;
+
+ // Tell Scintilla not to buffer: Quartz buffers drawing for us.
+ WndProc(SCI_SETBUFFEREDDRAW, 0, 0);
+
+ // We are working with Unicode exclusively.
+ WndProc(SCI_SETCODEPAGE, SC_CP_UTF8, 0);
+
+ // Add Mac specific key bindings.
+ for (int i = 0; macMapDefault[i].key; i++)
+ kmap.AssignCmdKey(macMapDefault[i].key, macMapDefault[i].modifiers, macMapDefault[i].msg);
+
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * We need some clean up. Do it here.
+ */
+void ScintillaCocoa::Finalise()
+{
+ SetTicking(false);
+ ScintillaBase::Finalise();
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Helper function to get the outer container which represents the Scintilla editor on application side.
+ */
+ScintillaView* ScintillaCocoa::TopContainer()
+{
+ NSView* container = static_cast<NSView*>(wMain.GetID());
+ return static_cast<ScintillaView*>([container superview]);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Helper function to get the inner container which represents the actual "canvas" we work with.
+ */
+NSView* ScintillaCocoa::ContentView()
+{
+ return static_cast<NSView*>(wMain.GetID());
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Instead of returning the size of the inner view we have to return the visible part of it
+ * in order to make scrolling working properly.
+ */
+PRectangle ScintillaCocoa::GetClientRectangle()
+{
+ NSView* host = ContentView();
+ NSSize size = [host frame].size;
+ return PRectangle(0, 0, size.width, size.height);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Converts the given point from base coordinates to local coordinates and at the same time into
+ * a native Point structure. Base coordinates are used for the top window used in the view hierarchy.
+ */
+Scintilla::Point ScintillaCocoa::ConvertPoint(NSPoint point)
+{
+ NSView* container = ContentView();
+ NSPoint result = [container convertPoint: point fromView: nil];
+
+ return Point(result.x, result.y);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * A function to directly execute code that would usually go the long way via window messages.
+ * However this is a Windows metapher and not used here, hence we just call our fake
+ * window proc. The given parameters directly reflect the message parameters used on Windows.
+ *
+ * @param sciThis The target which is to be called.
+ * @param iMessage A code that indicates which message was sent.
+ * @param wParam One of the two free parameters for the message. Traditionally a word sized parameter
+ * (hence the w prefix).
+ * @param lParam The other of the two free parameters. A signed long.
+ */
+sptr_t ScintillaCocoa::DirectFunction(ScintillaCocoa *sciThis, unsigned int iMessage, uptr_t wParam,
+ sptr_t lParam)
+{
+ return sciThis->WndProc(iMessage, wParam, lParam);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * This method is very similar to DirectFunction. On Windows it sends a message (not in the Obj-C sense)
+ * to the target window. Here we simply call our fake window proc.
+ */
+sptr_t scintilla_send_message(void* sci, unsigned int iMessage, uptr_t wParam, sptr_t lParam)
+{
+ ScintillaView *control = reinterpret_cast<ScintillaView*>(sci);
+ ScintillaCocoa* scintilla = [control backend];
+ return scintilla->WndProc(iMessage, wParam, lParam);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * That's our fake window procedure. On Windows each window has a dedicated procedure to handle
+ * commands (also used to synchronize UI and background threads), which is not the case in Cocoa.
+ *
+ * Messages handled here are almost solely for special commands of the backend. Everything which
+ * would be sytem messages on Windows (e.g. for key down, mouse move etc.) are handled by
+ * directly calling appropriate handlers.
+ */
+sptr_t ScintillaCocoa::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam)
+{
+ switch (iMessage)
+ {
+ case SCI_GETDIRECTFUNCTION:
+ return reinterpret_cast<sptr_t>(DirectFunction);
+
+ case SCI_GETDIRECTPOINTER:
+ return reinterpret_cast<sptr_t>(this);
+
+ case SCI_GRABFOCUS:
+ // TODO: implement it
+ break;
+
+ case WM_UNICHAR:
+ // Special case not used normally. Characters passed in this way will be inserted
+ // regardless of their value or modifier states. That means no command interpretation is
+ // performed.
+ if (IsUnicodeMode())
+ {
+ NSString* input = [[NSString stringWithCharacters: (const unichar*) &wParam length: 1] autorelease];
+ const char* utf8 = [input UTF8String];
+ AddCharUTF((char*) utf8, strlen(utf8), false);
+ return 1;
+ }
+ return 0;
+
+ default:
+ unsigned int r = ScintillaBase::WndProc(iMessage, wParam, lParam);
+
+ return r;
+ }
+ return 0l;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * In Windows lingo this is the handler which handles anything that wasn't handled in the normal
+ * window proc which would usually send the message back to generic window proc that Windows uses.
+ */
+sptr_t ScintillaCocoa::DefWndProc(unsigned int, uptr_t, sptr_t)
+{
+ return 0;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Enables or disables a timer that can trigger background processing at a regular interval, like
+ * drag scrolling or caret blinking.
+ */
+void ScintillaCocoa::SetTicking(bool on)
+{
+ if (timer.ticking != on)
+ {
+ timer.ticking = on;
+ if (timer.ticking)
+ {
+ // Scintilla ticks = milliseconds
+ // Using userInfo as flag to distinct between tick and idle timer.
+ NSTimer* tickTimer = [NSTimer scheduledTimerWithTimeInterval: timer.tickSize / 1000.0
+ target: timerTarget
+ selector: @selector(timerFired:)
+ userInfo: nil
+ repeats: YES];
+ timer.tickerID = reinterpret_cast<TickerID>(tickTimer);
+ }
+ else
+ if (timer.tickerID != NULL)
+ {
+ [reinterpret_cast<NSTimer*>(timer.tickerID) invalidate];
+ timer.tickerID = 0;
+ }
+ }
+ timer.ticksToWait = caret.period;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+bool ScintillaCocoa::SetIdle(bool on)
+{
+ if (idler.state != on)
+ {
+ idler.state = on;
+ if (idler.state)
+ {
+ // Scintilla ticks = milliseconds
+ NSTimer* idleTimer = [NSTimer scheduledTimerWithTimeInterval: timer.tickSize / 1000.0
+ target: timerTarget
+ selector: @selector(idleTimerFired:)
+ userInfo: nil
+ repeats: YES];
+ idler.idlerID = reinterpret_cast<IdlerID>(idleTimer);
+ }
+ else
+ if (idler.idlerID != NULL)
+ {
+ [reinterpret_cast<NSTimer*>(idler.idlerID) invalidate];
+ idler.idlerID = 0;
+ }
+ }
+ return true;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+void ScintillaCocoa::CopyToClipboard(const SelectionText &selectedText)
+{
+ SetPasteboardData([NSPasteboard generalPasteboard], selectedText);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+void ScintillaCocoa::Copy()
+{
+ if (currentPos != anchor)
+ {
+ SelectionText selectedText;
+ CopySelectionRange(&selectedText);
+ CopyToClipboard(selectedText);
+ }
+}
+
+//--------------------------------------------------------------------------------------------------
+
+bool ScintillaCocoa::CanPaste()
+{
+ if (!Editor::CanPaste())
+ return false;
+
+ bool ok = GetPasteboardData([NSPasteboard generalPasteboard], NULL);
+ return ok;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+void ScintillaCocoa::Paste()
+{
+ Paste(false);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Pastes data from the paste board into the editor.
+ */
+void ScintillaCocoa::Paste(bool forceRectangular)
+{
+ SelectionText selectedText;
+ bool ok = GetPasteboardData([NSPasteboard generalPasteboard], &selectedText);
+ if (forceRectangular)
+ selectedText.rectangular = forceRectangular;
+
+ if (!ok || !selectedText.s)
+ // No data or no flavor we support.
+ return;
+
+ pdoc->BeginUndoAction();
+ ClearSelection();
+ if (selectedText.rectangular)
+ {
+ int selStart = SelectionStart();
+ PasteRectangular(selStart, selectedText.s, selectedText.len);
+ }
+ else
+ if (pdoc->InsertString(currentPos, selectedText.s, selectedText.len))
+ SetEmptySelection(currentPos + selectedText.len);
+
+ pdoc->EndUndoAction();
+
+ Redraw();
+ EnsureCaretVisible();
+}
+
+//--------------------------------------------------------------------------------------------------
+
+void ScintillaCocoa::CreateCallTipWindow(PRectangle rc)
+{
+#if 0
+ // 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;
+ ScintillaCocoa* 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);
+
+ }
+#endif
+}
+
+
+void ScintillaCocoa::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;
+ }
+
+// NSMenu *menu= reinterpret_cast<NSMenu*>(popup.GetID());
+
+ //XXX TODO:XXX
+// [menu addItemWithTitle:[NSString stringWithUTF8String:label]
+// action:@selector()
+// keyEquivalent:@""];
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void ScintillaCocoa::ClaimSelection()
+{
+ // Mac OS X does not have a primary selection.
+}
+
+// -------------------------------------------------------------------------------------------------
+
+#pragma segment Drag
+
+/**
+ * Triggered by the tick timer on a regular basis to scroll the content during a drag operation.
+ */
+void ScintillaCocoa::DragScroll()
+{
+ if (posDrag == invalidPosition)
+ {
+ scrollSpeed = 1;
+ scrollTicks = 2000;
+ return;
+ }
+
+ // TODO: does not work for wrapped lines, fix it.
+ int line = pdoc->LineFromPosition(posDrag);
+ int currentVisibleLine = cs.DisplayFromDoc(line);
+ int lastVisibleLine = Platform::Minimum(topLine + LinesOnScreen(), cs.LinesDisplayed()) - 2;
+
+ if (currentVisibleLine <= topLine && topLine > 0)
+ ScrollTo(topLine - scrollSpeed);
+ else
+ if (currentVisibleLine >= lastVisibleLine)
+ ScrollTo(topLine + scrollSpeed);
+ else
+ {
+ scrollSpeed = 1;
+ scrollTicks = 2000;
+ return;
+ }
+
+ // TODO: also handle horizontal scrolling.
+
+ if (scrollSpeed == 1)
+ {
+ scrollTicks -= timer.tickSize;
+ if (scrollTicks <= 0)
+ {
+ scrollSpeed = 5;
+ scrollTicks = 2000;
+ }
+ }
+
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Called when a drag operation was initiated from within Scintilla.
+ */
+void ScintillaCocoa::StartDrag()
+{
+ if (currentPos == anchor)
+ return;
+
+ // Put the data to be dragged on the drag pasteboard.
+ SelectionText selectedText;
+ NSPasteboard* pasteboard = [NSPasteboard pasteboardWithName: NSDragPboard];
+ CopySelectionRange(&selectedText);
+ SetPasteboardData(pasteboard, selectedText);
+
+ // Prepare drag image.
+ PRectangle localRectangle = RectangleFromRange(SelectionStart(), SelectionEnd());
+ NSRect selectionRectangle = PRectangleToNSRect(localRectangle);
+
+ NSView* content = ContentView();
+
+#if 0 // TODO: fix initialization of the drag image with CGImageRef.
+ // 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;
+
+ bool lastHideSelection = hideSelection;
+ hideSelection = true;
+ if (sw)
+ {
+ pixmap = new SurfaceImpl();
+ if (pixmap)
+ {
+ PRectangle client = GetClientRectangle();
+ PRectangle imageRect = NSRectToPRectangle(selectionRectangle);
+ paintState = painting;
+ //sw->InitPixMap(client.Width(), client.Height(), NULL, NULL);
+ sw->InitPixMap(imageRect.Width(), imageRect.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
+ 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;
+ }
+ hideSelection = lastHideSelection;
+
+ NSBitmapImageRep* bitmap = NULL;
+ if (pixmap)
+ {
+ bitmap = [[[NSBitmapImageRep alloc] initWithCGImage: pixmap->GetImage()] autorelease];
+ pixmap->Release();
+ delete pixmap;
+ }
+#else
+
+ // Poor man's drag image: take a snapshot of the content view.
+ [content lockFocus];
+ NSBitmapImageRep* bitmap = [[[NSBitmapImageRep alloc] initWithFocusedViewRect: selectionRectangle] autorelease];
+ [bitmap setColorSpaceName: NSDeviceRGBColorSpace];
+ [content unlockFocus];
+
+#endif
+
+ NSImage* image = [[[NSImage alloc] initWithSize: selectionRectangle.size] autorelease];
+ [image addRepresentation: bitmap];
+
+ NSImage* dragImage = [[[NSImage alloc] initWithSize: selectionRectangle.size] autorelease];
+ [dragImage setBackgroundColor: [NSColor clearColor]];
+ [dragImage lockFocus];
+ [image dissolveToPoint: NSMakePoint(0.0, 0.0) fraction: 0.5];
+ [dragImage unlockFocus];
+
+ NSPoint startPoint;
+ startPoint.x = selectionRectangle.origin.x;
+ startPoint.y = selectionRectangle.origin.y + selectionRectangle.size.height;
+ [content dragImage: dragImage
+ at: startPoint
+ offset: NSZeroSize
+ event: lastMouseEvent // Set in MouseMove.
+ pasteboard: pasteboard
+ source: content
+ slideBack: YES];
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Called when a drag operation reaches the control which was initiated outside.
+ */
+NSDragOperation ScintillaCocoa::DraggingEntered(id <NSDraggingInfo> info)
+{
+ inDragDrop = ddDragging;
+ return DraggingUpdated(info);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Called frequently during a drag operation if we are the target. Keep telling the caller
+ * what drag operation we accept and update the drop caret position to indicate the
+ * potential insertion point of the dragged data.
+ */
+NSDragOperation ScintillaCocoa::DraggingUpdated(id <NSDraggingInfo> info)
+{
+ // Convert the drag location from window coordinates to view coordinates and
+ // from there to a text position to finally set the drag position.
+ Point location = ConvertPoint([info draggingLocation]);
+ SetDragPosition(PositionFromLocation(location));
+
+ NSDragOperation sourceDragMask = [info draggingSourceOperationMask];
+ if (sourceDragMask == NSDragOperationNone)
+ return sourceDragMask;
+
+ NSPasteboard* pasteboard = [info draggingPasteboard];
+
+ // Return what type of operation we will perform. Prefer move over copy.
+ if ([[pasteboard types] containsObject: NSStringPboardType] ||
+ [[pasteboard types] containsObject: ScintillaRecPboardType])
+ return (sourceDragMask & NSDragOperationMove) ? NSDragOperationMove : NSDragOperationCopy;
+
+ if ([[pasteboard types] containsObject: NSFilenamesPboardType])
+ return (sourceDragMask & NSDragOperationGeneric);
+ return NSDragOperationNone;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Resets the current drag position as we are no longer the drag target.
+ */
+void ScintillaCocoa::DraggingExited(id <NSDraggingInfo> info)
+{
+ SetDragPosition(invalidPosition);
+ inDragDrop = ddNone;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Here is where the real work is done. Insert the text from the pasteboard.
+ */
+bool ScintillaCocoa::PerformDragOperation(id <NSDraggingInfo> info)
+{
+ NSPasteboard* pasteboard = [info draggingPasteboard];
+
+ if ([[pasteboard types] containsObject: NSFilenamesPboardType])
+ {
+ NSArray* files = [pasteboard propertyListForType: NSFilenamesPboardType];
+ for (NSString* uri in files)
+ NotifyURIDropped([uri UTF8String]);
+ }
+ else
+ {
+ SelectionText text;
+ GetPasteboardData(pasteboard, &text);
+
+ if (text.len > 0)
+ {
+ NSDragOperation operation = [info draggingSourceOperationMask];
+ bool moving = (operation & NSDragOperationMove) != 0;
+
+ DropAt(posDrag, text.s, moving, text.rectangular);
+ };
+ }
+
+ return true;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+void ScintillaCocoa::SetPasteboardData(NSPasteboard* board, const SelectionText &selectedText)
+{
+ if (selectedText.len == 0)
+ return;
+
+ NSString *string;
+ string = [NSString stringWithUTF8String: selectedText.s];
+
+ [board declareTypes:[NSArray arrayWithObjects:
+ NSStringPboardType,
+ selectedText.rectangular ? ScintillaRecPboardType : nil,
+ nil] owner:nil];
+
+ if (selectedText.rectangular)
+ {
+ // This is specific to scintilla, allows us to drag rectangular selections around the document.
+ [board setString: string forType: ScintillaRecPboardType];
+ }
+
+ [board setString: string forType: NSStringPboardType];
+
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Helper method to retrieve the best fitting alternative from the general pasteboard.
+ */
+bool ScintillaCocoa::GetPasteboardData(NSPasteboard* board, SelectionText* selectedText)
+{
+ NSArray* supportedTypes = [NSArray arrayWithObjects: ScintillaRecPboardType,
+ NSStringPboardType,
+ nil];
+ NSString *bestType = [board availableTypeFromArray: supportedTypes];
+ NSString* data = [board stringForType: bestType];
+
+ if (data != nil)
+ {
+ if (selectedText != nil)
+ {
+ char* text = (char*) [data UTF8String];
+ bool rectangular = bestType == ScintillaRecPboardType;
+ selectedText->Copy(text, strlen(text) + 1, 0, 0, rectangular, false);
+ }
+ return true;
+ }
+
+ return false;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+void ScintillaCocoa::SetMouseCapture(bool on)
+{
+ capturedMouse = on;
+ /*
+ if (mouseDownCaptures)
+ {
+ if (capturedMouse)
+ WndProc(SCI_SETCURSOR, Window::cursorArrow, 0);
+ else
+ // Reset to normal. Actual image will be set on mouse move.
+ WndProc(SCI_SETCURSOR, (unsigned int) SC_CURSORNORMAL, 0);
+ }
+ */
+}
+
+//--------------------------------------------------------------------------------------------------
+
+bool ScintillaCocoa::HaveMouseCapture()
+{
+ return capturedMouse;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Synchronously paint a rectangle of the window.
+ */
+void ScintillaCocoa::SyncPaint(void* gc, PRectangle rc)
+{
+ paintState = painting;
+ rcPaint = rc;
+ PRectangle rcText = GetTextRectangle();
+ paintingAllText = rcPaint.Contains(rcText);
+ 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);
+ }
+ sw->Release();
+ delete sw;
+ }
+ paintState = notPainting;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Modfies the vertical scroll position to make the current top line show up as such.
+ */
+void ScintillaCocoa::SetVerticalScrollPos()
+{
+ ScintillaView* topContainer = TopContainer();
+
+ // Convert absolute coordinate into the range [0..1]. Keep in mind that the visibile area
+ // does *not* belong to the scroll range.
+ float relativePosition = (float) topLine / MaxScrollPos();
+ [topContainer setVerticalScrollPosition: relativePosition];
+}
+
+//--------------------------------------------------------------------------------------------------
+
+void ScintillaCocoa::SetHorizontalScrollPos()
+{
+ ScintillaView* topContainer = TopContainer();
+ PRectangle textRect = GetTextRectangle();
+
+ // Convert absolute coordinate into the range [0..1]. Keep in mind that the visibile area
+ // does *not* belong to the scroll range.
+ float relativePosition = (float) xOffset / (scrollWidth - textRect.Width());
+ [topContainer setHorizontalScrollPosition: relativePosition];
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Used to adjust both scrollers to reflect the current scroll range and position in the editor.
+ *
+ * @param nMax Number of lines in the editor.
+ * @param nPage Number of lines per scroll page.
+ * @return True if there was a change, otherwise false.
+ */
+bool ScintillaCocoa::ModifyScrollBars(int nMax, int nPage)
+{
+ // Input values are given in lines, not pixels, so we have to convert.
+ int lineHeight = WndProc(SCI_TEXTHEIGHT, 0, 0);
+ NSView* container = ContentView();
+ NSRect bounds = [container frame];
+ ScintillaView* topContainer = TopContainer();
+
+ int scrollRange = lineHeight * (nMax + 1); // +1 because the caller subtracted one.
+ int pageSize = bounds.size.height;
+ bool verticalChange = [topContainer setVerticalScrollRange: scrollRange page: pageSize];
+
+ scrollRange = scrollWidth;
+ pageSize = bounds.size.width;
+ bool horizontalChange = [topContainer setHorizontalScrollRange: scrollRange page: pageSize];
+
+ return verticalChange || horizontalChange;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+void ScintillaCocoa::Resize()
+{
+ ChangeSize();
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Called by the frontend control when the user manipulates one of the scrollers.
+ *
+ * @param position The relative position of the scroller in the range of [0..1].
+ * @param part Specifies which part was clicked on by the user, so we can handle thumb tracking
+ * as well as page and line scrolling.
+ * @param horizontal True if the horizontal scroller was hit, otherwise false.
+ */
+void ScintillaCocoa::DoScroll(float position, NSScrollerPart part, bool horizontal)
+{
+ // If the given scroller part is not the knob (or knob slot) then the given position is not yet
+ // current and we have to update it.
+ if (horizontal)
+ {
+ // Horizontal offset is given in pixels.
+ PRectangle textRect = GetTextRectangle();
+ int offset = (int) (position * (scrollWidth - textRect.Width()));
+ int smallChange = (int) (textRect.Width() / 30);
+ if (smallChange < 5)
+ smallChange = 5;
+ switch (part)
+ {
+ case NSScrollerDecrementLine:
+ offset -= smallChange;
+ break;
+ case NSScrollerDecrementPage:
+ offset -= textRect.Width();
+ break;
+ case NSScrollerIncrementLine:
+ offset += smallChange;
+ break;
+ case NSScrollerIncrementPage:
+ offset += textRect.Width();
+ break;
+ };
+ HorizontalScrollTo(offset);
+ }
+ else
+ {
+ // VerticalScrolling is by line.
+ int topLine = (int) (position * MaxScrollPos());
+ int page = LinesOnScreen();
+ switch (part)
+ {
+ case NSScrollerDecrementLine:
+ topLine--;
+ break;
+ case NSScrollerDecrementPage:
+ topLine -= page;
+ break;
+ case NSScrollerIncrementLine:
+ topLine++;
+ break;
+ case NSScrollerIncrementPage:
+ topLine += page;
+ break;
+ };
+ ScrollTo(topLine, true);
+ }
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Used to register a callback function for a given window. This is used to emulate the way
+ * Windows notfies other controls (mainly up in the view hierarchy) about certain events.
+ *
+ * @param windowid A handle to a window. That value is generic and can be anything. It is passed
+ * through to the callback.
+ * @param callback The callback function to be used for future notifications. If NULL then no
+ * notifications will be sent anymore.
+ */
+void ScintillaCocoa::RegisterNotifyCallback(intptr_t windowid, SciNotifyFunc callback)
+{
+ notifyObj = windowid;
+ notifyProc = callback;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+void ScintillaCocoa::NotifyChange()
+{
+ if (notifyProc != NULL)
+ notifyProc(notifyObj, WM_COMMAND, (uintptr_t) (SCEN_CHANGE << 16), (uintptr_t) this);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+void ScintillaCocoa::NotifyFocus(bool focus)
+{
+ if (notifyProc != NULL)
+ notifyProc(notifyObj, WM_COMMAND, (uintptr_t) ((focus ? SCEN_SETFOCUS : SCEN_KILLFOCUS) << 16), (uintptr_t) this);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Used to send a notification (as WM_NOTIFY call) to the procedure, which has been set by the call
+ * to RegisterNotifyCallback (so it is not necessarily the parent window).
+ *
+ * @param scn The notification to send.
+ */
+void ScintillaCocoa::NotifyParent(SCNotification scn)
+{
+ if (notifyProc != NULL)
+ {
+ scn.nmhdr.hwndFrom = (void*) this;
+ scn.nmhdr.idFrom = (unsigned int) wMain.GetID();
+ notifyProc(notifyObj, WM_NOTIFY, (uintptr_t) 0, (uintptr_t) &scn);
+ }
+}
+
+//--------------------------------------------------------------------------------------------------
+
+void ScintillaCocoa::NotifyURIDropped(const char *uri)
+{
+ SCNotification scn;
+ scn.nmhdr.code = SCN_URIDROPPED;
+ scn.text = uri;
+
+ NotifyParent(scn);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+bool ScintillaCocoa::HasSelection()
+{
+ return (SelectionEnd() - SelectionStart()) > 0;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+bool ScintillaCocoa::CanUndo()
+{
+ return pdoc->CanUndo();
+}
+
+//--------------------------------------------------------------------------------------------------
+
+bool ScintillaCocoa::CanRedo()
+{
+ return pdoc->CanRedo();
+}
+
+//--------------------------------------------------------------------------------------------------
+
+void ScintillaCocoa::TimerFired(NSTimer* timer)
+{
+ Tick();
+ DragScroll();
+}
+
+//--------------------------------------------------------------------------------------------------
+
+void ScintillaCocoa::IdleTimerFired()
+{
+ bool more = Idle();
+ if (!more)
+ SetIdle(false);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Main entry point for drawing the control.
+ *
+ * @param rect The area to paint, given in the sender's coordinate.
+ * @param gc The context we can use to paint.
+ */
+void ScintillaCocoa::Draw(NSRect rect, CGContextRef gc)
+{
+ SyncPaint(gc, NSRectToPRectangle(rect));
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Helper function to translate OS X key codes to Scintilla key codes.
+ */
+static inline UniChar KeyTranslate(UniChar unicodeChar)
+{
+ switch (unicodeChar)
+ {
+ case NSDownArrowFunctionKey:
+ return SCK_DOWN;
+ case NSUpArrowFunctionKey:
+ return SCK_UP;
+ case NSLeftArrowFunctionKey:
+ return SCK_LEFT;
+ case NSRightArrowFunctionKey:
+ return SCK_RIGHT;
+ case NSHomeFunctionKey:
+ return SCK_HOME;
+ case NSEndFunctionKey:
+ return SCK_END;
+ case NSPageUpFunctionKey:
+ return SCK_PRIOR;
+ case NSPageDownFunctionKey:
+ return SCK_NEXT;
+ case NSDeleteFunctionKey:
+ return SCK_DELETE;
+ case NSInsertFunctionKey:
+ return SCK_INSERT;
+ case '\n':
+ case 3:
+ return SCK_RETURN;
+ case 27:
+ return SCK_ESCAPE;
+ case 127:
+ return SCK_BACK;
+ case '\t':
+ case 25: // Shift tab, return to unmodified tab and handle that via modifiers.
+ return SCK_TAB;
+ default:
+ return unicodeChar;
+ }
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Main keyboard input handling method. It is called for any key down event, including function keys,
+ * numeric keypad input and whatnot.
+ *
+ * @param event The event instance associated with the key down event.
+ * @return True if the input was handled, false otherwise.
+ */
+bool ScintillaCocoa::KeyboardInput(NSEvent* event)
+{
+ // For now filter out function keys.
+ NSUInteger modifiers = [event modifierFlags];
+
+ NSString* input = [event characters];
+
+ bool control = (modifiers & NSControlKeyMask) != 0;
+ bool shift = (modifiers & NSShiftKeyMask) != 0;
+ bool command = (modifiers & NSCommandKeyMask) != 0;
+ bool alt = (modifiers & NSAlternateKeyMask) != 0;
+
+ bool handled = false;
+
+ // Handle each entry individually. Usually we only have one entry anway.
+ for (int i = 0; i < input.length; i++)
+ {
+ const UniChar originalKey = [input characterAtIndex: i];
+ UniChar key = KeyTranslate(originalKey);
+
+ bool consumed = false; // Consumed as command?
+
+ // Signal command as control + alt. This leaves us without command + control and command + alt
+ // but that's what we get when we have a modifier key more than other platforms.
+ if (KeyDown(key, shift, control || command, alt || command, &consumed))
+ handled = true;
+ if (consumed)
+ handled = true;
+ }
+
+ return handled;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Used to insert already processed text provided by the Cocoa text input system.
+ */
+int ScintillaCocoa::InsertText(NSString* input)
+{
+ const char* utf8 = [input UTF8String];
+ AddCharUTF((char*) utf8, strlen(utf8), false);
+ return true;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Called by the owning view when the mouse pointer enters the control.
+ */
+void ScintillaCocoa::MouseEntered(NSEvent* event)
+{
+ if (!HaveMouseCapture())
+ {
+ WndProc(SCI_SETCURSOR, (long int)SC_CURSORNORMAL, 0);
+
+ // Mouse location is given in screen coordinates and might also be outside of our bounds.
+ Point location = ConvertPoint([event locationInWindow]);
+ ButtonMove(location);
+ }
+}
+
+//--------------------------------------------------------------------------------------------------
+
+void ScintillaCocoa::MouseExited(NSEvent* event)
+{
+ // Nothing to do here.
+}
+
+//--------------------------------------------------------------------------------------------------
+
+void ScintillaCocoa::MouseDown(NSEvent* event)
+{
+ Point location = ConvertPoint([event locationInWindow]);
+ NSTimeInterval time = [event timestamp];
+ bool command = ([event modifierFlags] & NSCommandKeyMask) != 0;
+ bool shift = ([event modifierFlags] & NSShiftKeyMask) != 0;
+ bool control = ([event modifierFlags] & NSControlKeyMask) != 0;
+
+ ButtonDown(Point(location.x, location.y), (int) (time * 1000), shift, control, command);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+void ScintillaCocoa::MouseMove(NSEvent* event)
+{
+ lastMouseEvent = event;
+ ButtonMove(ConvertPoint([event locationInWindow]));
+}
+
+//--------------------------------------------------------------------------------------------------
+
+void ScintillaCocoa::MouseUp(NSEvent* event)
+{
+ NSTimeInterval time = [event timestamp];
+ bool control = ([event modifierFlags] & NSControlKeyMask) != 0;
+
+ ButtonUp(ConvertPoint([event locationInWindow]), (int) (time * 1000), control);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+void ScintillaCocoa::MouseWheel(NSEvent* event)
+{
+ bool command = ([event modifierFlags] & NSCommandKeyMask) != 0;
+ bool shift = ([event modifierFlags] & NSShiftKeyMask) != 0;
+ int lineHeight = WndProc(SCI_TEXTHEIGHT, 0, 0);
+ int delta;
+ if (shift)
+ delta = 10 * [event deltaX]; // Arbitrary scale factor.
+ else
+ {
+ // In order to make scrolling with larger offset smoother we scroll less line the larger the
+ // delta value is.
+ if ([event deltaY] < 0)
+ delta = -(int) sqrt(-10.0 * [event deltaY]);
+ else
+ delta = (int) sqrt(10.0 * [event deltaY]);
+ }
+
+ if (command)
+ {
+ // 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
+ if (shift)
+ HorizontalScrollTo(xOffset - delta);
+ else
+ ScrollTo(topLine - delta, true);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+//OSStatus ScintillaCocoa::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 ScintillaCocoa::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;
+//}
+//
+
+//--------------------------------------------------------------------------------------------------