aboutsummaryrefslogtreecommitdiffhomepage
path: root/cocoa/ScintillaView.mm
diff options
context:
space:
mode:
authornyamatongwe <unknown>2009-05-14 12:10:06 +0000
committernyamatongwe <unknown>2009-05-14 12:10:06 +0000
commit805e109be2f52c0ea446511fc33efb3746a3bc18 (patch)
tree3b57cf166d494a0dba47befcea94deba3da0fbda /cocoa/ScintillaView.mm
parent8e12be32e9d14363f1e2f0fe6fc305b68a64ceb5 (diff)
downloadscintilla-mirror-805e109be2f52c0ea446511fc33efb3746a3bc18.tar.gz
First upload of Cocoa platform from Mike Lischke at Sun.
Diffstat (limited to 'cocoa/ScintillaView.mm')
-rw-r--r--cocoa/ScintillaView.mm1082
1 files changed, 1082 insertions, 0 deletions
diff --git a/cocoa/ScintillaView.mm b/cocoa/ScintillaView.mm
new file mode 100644
index 000000000..d6b353256
--- /dev/null
+++ b/cocoa/ScintillaView.mm
@@ -0,0 +1,1082 @@
+
+/**
+ * Implementation of the native Cocoa View that serves as container for the scintilla parts.
+ *
+ * Created by Mike Lischke.
+ *
+ * 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).
+ */
+
+#import "ScintillaView.h"
+
+using namespace Scintilla;
+
+// Two additional cursors we need, which aren't provided by Cocoa.
+static NSCursor* reverseArrowCursor;
+static NSCursor* waitCursor;
+
+// The scintilla indicator used for keyboard input.
+#define INPUT_INDICATOR INDIC_MAX - 1
+
+@implementation InnerView
+
+@synthesize owner = mOwner;
+
+//--------------------------------------------------------------------------------------------------
+
+- (NSView*) initWithFrame: (NSRect) frame
+{
+ self = [super initWithFrame: frame];
+
+ if (self != nil)
+ {
+ // Some initialization for our view.
+ mCurrentCursor = [NSCursor arrowCursor];
+ mCurrentTrackingRect = 0;
+ mMarkedTextRange = NSMakeRange(NSNotFound, 0);
+
+ [self registerForDraggedTypes:[NSArray arrayWithObjects:
+ NSStringPboardType, NSHTMLPboardType, NSFilenamesPboardType, nil]];
+ }
+
+ return self;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * When the view is resized we need to update our tracking rectangle and let the backend know.
+ */
+- (void) setFrame: (NSRect) frame
+{
+ [super setFrame: frame];
+
+ // Make the content also a tracking rectangle for mouse events.
+ if (mCurrentTrackingRect != 0)
+ [self removeTrackingRect: mCurrentTrackingRect];
+ mCurrentTrackingRect = [self addTrackingRect: [self bounds]
+ owner: self
+ userData: nil
+ assumeInside: YES];
+ mOwner.backend->Resize();
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Called by the backend if a new cursor must be set for the view.
+ */
+- (void) setCursor: (Window::Cursor) cursor
+{
+ switch (cursor)
+ {
+ case Window::cursorText:
+ mCurrentCursor = [NSCursor IBeamCursor];
+ break;
+ case Window::cursorArrow:
+ mCurrentCursor = [NSCursor arrowCursor];
+ break;
+ case Window::cursorWait:
+ mCurrentCursor = waitCursor;
+ break;
+ case Window::cursorHoriz:
+ mCurrentCursor = [NSCursor resizeLeftRightCursor];
+ break;
+ case Window::cursorVert:
+ mCurrentCursor = [NSCursor resizeUpDownCursor];
+ break;
+ case Window::cursorReverseArrow:
+ mCurrentCursor = reverseArrowCursor;
+ break;
+ case Window::cursorUp:
+ default:
+ mCurrentCursor = [NSCursor arrowCursor];
+ break;
+ }
+
+ // Trigger recreation of the cursor rectangle(s).
+ [[self window] invalidateCursorRectsForView: self];
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * This method is called to give us the opportunity to define our mouse sensitive rectangle.
+ */
+- (void) resetCursorRects
+{
+ [super resetCursorRects];
+
+ // We only have one cursor rect: our bounds.
+ NSRect bounds = [self bounds];
+ [self addCursorRect: [self bounds] cursor: mCurrentCursor];
+ [mCurrentCursor setOnMouseEntered: YES];
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Gets called by the runtime when the view needs repainting.
+ */
+- (void) drawRect: (NSRect) rect
+{
+ CGContextRef context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
+
+ mOwner.backend->Draw(rect, context);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Windows uses a client coordinate system where the upper left corner is the origin in a window.
+ * We have to adjust for that. However by returning YES here, we are already done with that.
+ * Note that because of returning YES here most coordinates we use now (e.g. for painting,
+ * invalidating rectangles etc.) are given with +Y pointing down!
+ */
+- (BOOL) isFlipped
+{
+ return YES;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+- (BOOL) isOpaque
+{
+ return YES;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Implement the "click through" behavior by telling the caller we accept the first mouse event too.
+ */
+- (BOOL) acceptsFirstMouse: (NSEvent *) theEvent
+{
+ return YES;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Make this view accepting events as first responder.
+ */
+- (BOOL) acceptsFirstResponder
+{
+ return YES;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+// Adoption of NSTextInput protocol.
+
+- (NSAttributedString*) attributedSubstringFromRange: (NSRange) range
+{
+ return nil;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+- (NSUInteger) characterIndexForPoint: (NSPoint) point
+{
+ return NSNotFound;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+- (NSInteger) conversationIdentifier
+{
+ return (NSInteger) self;
+
+}
+
+//--------------------------------------------------------------------------------------------------
+
+- (void) doCommandBySelector: (SEL) selector
+{
+ if ([self respondsToSelector:@selector(selector)])
+ [self performSelector: selector withObject: nil];
+}
+
+//--------------------------------------------------------------------------------------------------
+
+- (NSRect) firstRectForCharacterRange: (NSRange) range
+{
+ return NSZeroRect;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+- (BOOL) hasMarkedText
+{
+ return mMarkedTextRange.length > 0;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * General text input. Used to insert new text at the current input position, replacing the current
+ * selection if there is any.
+ */
+- (void) insertText: (id) aString
+{
+ // Remove any previously marked text first.
+ [self removeMarkedText];
+
+ mOwner.backend->InsertText((NSString*) aString);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+- (NSRange) markedRange
+{
+ return mMarkedTextRange;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+- (NSRange) selectedRange
+{
+ int begin = [mOwner getGeneralProperty: SCI_GETSELECTIONSTART parameter: 0];
+ int end = [mOwner getGeneralProperty: SCI_GETSELECTIONEND parameter: 0];
+ return NSMakeRange(begin, end - begin);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Called by the input manager to set text which might be combined with further input to form
+ * the final text (e.g. composition of ^ and a to รข).
+ *
+ * @param aString The text to insert, either what has been marked already or what is selected already
+ * or simply added at the current insertion point. Depending on what is available.
+ * @param range The range of the new text to select (given relative to the insertion point of the new text).
+ */
+- (void) setMarkedText: (id) aString selectedRange: (NSRange) range
+{
+ // Since we did not return any valid attribute for marked text (see validAttributesForMarkedText)
+ // we can safely assume the passed in text is an NSString instance.
+ NSString* newText = (NSString*) aString;
+ int currentPosition = [mOwner getGeneralProperty: SCI_GETCURRENTPOS parameter: 0];
+
+ // Replace marked text if there is one.
+ if (mMarkedTextRange.length > 0)
+ {
+ // We have already marked text. Replace that.
+ [mOwner setGeneralProperty: SCI_SETSELECTIONSTART
+ parameter: mMarkedTextRange.location
+ value: 0];
+ [mOwner setGeneralProperty: SCI_SETSELECTIONEND
+ parameter: mMarkedTextRange.location + mMarkedTextRange.length
+ value: 0];
+ currentPosition = mMarkedTextRange.location;
+ }
+
+ mOwner.backend->InsertText(newText);
+
+ mMarkedTextRange.location = currentPosition;
+ mMarkedTextRange.length = [newText length];
+
+ // Mark the just inserted text. Keep the marked range for later reset.
+ [mOwner setGeneralProperty: SCI_SETINDICATORCURRENT parameter: INPUT_INDICATOR value: 0];
+ [mOwner setGeneralProperty: SCI_INDICATORFILLRANGE
+ parameter: mMarkedTextRange.location
+ value: mMarkedTextRange.length];
+
+ // Select the part which is indicated in the given range.
+ if (range.length > 0)
+ {
+ [mOwner setGeneralProperty: SCI_SETSELECTIONSTART
+ parameter: currentPosition + range.location
+ value: 0];
+ [mOwner setGeneralProperty: SCI_SETSELECTIONEND
+ parameter: currentPosition + range.location + range.length
+ value: 0];
+ }
+}
+
+//--------------------------------------------------------------------------------------------------
+
+- (void) unmarkText
+{
+ if (mMarkedTextRange.length > 0)
+ {
+ [mOwner setGeneralProperty: SCI_SETINDICATORCURRENT parameter: INPUT_INDICATOR value: 0];
+ [mOwner setGeneralProperty: SCI_INDICATORCLEARRANGE
+ parameter: mMarkedTextRange.location
+ value: mMarkedTextRange.length];
+ mMarkedTextRange = NSMakeRange(NSNotFound, 0);
+ }
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Removes any currently marked text.
+ */
+- (void) removeMarkedText
+{
+ if (mMarkedTextRange.length > 0)
+ {
+ // We have already marked text. Replace that.
+ [mOwner setGeneralProperty: SCI_SETSELECTIONSTART
+ parameter: mMarkedTextRange.location
+ value: 0];
+ [mOwner setGeneralProperty: SCI_SETSELECTIONEND
+ parameter: mMarkedTextRange.location + mMarkedTextRange.length
+ value: 0];
+ mOwner.backend->InsertText(@"");
+ mMarkedTextRange = NSMakeRange(NSNotFound, 0);
+ }
+}
+
+//--------------------------------------------------------------------------------------------------
+
+- (NSArray*) validAttributesForMarkedText
+{
+ return nil;
+}
+
+// End of the NSTextInput protocol adoption.
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Generic input method. It is used to pass on keyboard input to Scintilla. The control itself only
+ * handles shortcuts. The input is then forwarded to the Cocoa text input system, which in turn does
+ * its own input handling (character composition via NSTextInput protocol):
+ */
+- (void) keyDown: (NSEvent *) theEvent
+{
+ mOwner.backend->KeyboardInput(theEvent);
+ NSArray* events = [NSArray arrayWithObject: theEvent];
+ [self interpretKeyEvents: events];
+}
+
+//--------------------------------------------------------------------------------------------------
+
+- (void) mouseDown: (NSEvent *) theEvent
+{
+ mOwner.backend->MouseDown(theEvent);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+- (void) mouseDragged: (NSEvent *) theEvent
+{
+ mOwner.backend->MouseMove(theEvent);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+- (void) mouseUp: (NSEvent *) theEvent
+{
+ mOwner.backend->MouseUp(theEvent);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+- (void) mouseMoved: (NSEvent *) theEvent
+{
+ mOwner.backend->MouseMove(theEvent);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+- (void) mouseEntered: (NSEvent *) theEvent
+{
+ mOwner.backend->MouseEntered(theEvent);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+- (void) mouseExited: (NSEvent *) theEvent
+{
+ mOwner.backend->MouseExited(theEvent);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+- (void) scrollWheel: (NSEvent *) theEvent
+{
+ mOwner.backend->MouseWheel(theEvent);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * The editor is getting the foreground control (the one getting the input focus).
+ */
+- (BOOL) becomeFirstResponder
+{
+ mOwner.backend->WndProc(SCI_SETFOCUS, 1, 0);
+ return YES;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * The editor is losing the input focus.
+ */
+- (BOOL) resignFirstResponder
+{
+ mOwner.backend->WndProc(SCI_SETFOCUS, 0, 0);
+ return YES;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Called when an external drag operation enters the view.
+ */
+- (NSDragOperation) draggingEntered: (id <NSDraggingInfo>) sender
+{
+ return mOwner.backend->DraggingEntered(sender);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Called frequently during an external drag operation if we are the target.
+ */
+- (NSDragOperation) draggingUpdated: (id <NSDraggingInfo>) sender
+{
+ return mOwner.backend->DraggingUpdated(sender);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Drag image left the view. Clean up if necessary.
+ */
+- (void) draggingExited: (id <NSDraggingInfo>) sender
+{
+ mOwner.backend->DraggingExited(sender);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+- (BOOL) prepareForDragOperation: (id <NSDraggingInfo>) sender
+{
+ return YES;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+- (BOOL) performDragOperation: (id <NSDraggingInfo>) sender
+{
+ return mOwner.backend->PerformDragOperation(sender);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Returns operations we allow as drag source.
+ */
+- (NSDragOperation) draggingSourceOperationMaskForLocal: (BOOL) flag
+{
+ return NSDragOperationCopy | NSDragOperationMove;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Drag operation is done. Notify editor.
+ */
+- (void) concludeDragOperation: (id <NSDraggingInfo>) sender
+{
+ // Clean up is the same as if we are no longer the drag target.
+ mOwner.backend->DraggingExited(sender);
+}
+
+@end
+
+//--------------------------------------------------------------------------------------------------
+
+@implementation ScintillaView
+
+@synthesize backend = mBackend;
+
+/**
+ * ScintiallView is a composite control made from an NSView and an embedded NSView that is
+ * used as canvas for the output (by the backend, using its CGContext), plus two scrollers.
+ */
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Initialize custom cursor.
+ */
++ (void) initialize
+{
+ if (self == [ScintillaView class])
+ {
+ NSBundle* bundle = [NSBundle bundleForClass: [ScintillaView class]];
+
+ NSString* path = [bundle pathForResource: @"mac_cursor_busy" ofType: @"png" inDirectory: nil];
+ NSImage* image = [[[NSImage alloc] initWithContentsOfFile: path] autorelease];
+ waitCursor = [[[NSCursor alloc] initWithImage: image hotSpot: NSMakePoint(2, 2)] retain];
+
+ path = [bundle pathForResource: @"mac_cursor_flipped" ofType: @"png" inDirectory: nil];
+ image = [[[NSImage alloc] initWithContentsOfFile: path] autorelease];
+ reverseArrowCursor = [[[NSCursor alloc] initWithImage: image hotSpot: NSMakePoint(12, 2)] retain];
+ }
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Sends a new notification of the given type to the default notification center.
+ */
+- (void) sendNotification: (NSString*) notificationName
+{
+ NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
+ [center postNotificationName: notificationName object: self];
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Notification function used by Scintilla to call us back (e.g. for handling clicks on the
+ * folder margin or changes in the editor).
+ */
+static void notification(intptr_t windowid, unsigned int iMessage, uintptr_t wParam, uintptr_t lParam)
+{
+ // WM_NOTIFY means we got a parent notification with a special notification structure.
+ // Here we don't really differentiate between parent and own notifications and handle both.
+ ScintillaView* editor;
+ switch (iMessage)
+ {
+ case WM_NOTIFY:
+ {
+ // Parent notification. Details are passed as SCNotification structure.
+ SCNotification* scn = reinterpret_cast<SCNotification*>(lParam);
+ editor = reinterpret_cast<InnerView*>(scn->nmhdr.idFrom).owner;
+ switch (scn->nmhdr.code)
+ {
+ case SCN_MARGINCLICK:
+ if (scn->margin == 2)
+ {
+ // Click on the folder margin. Toggle the current line if possible.
+ int line = [editor getGeneralProperty: SCI_LINEFROMPOSITION parameter: scn->position];
+ [editor setGeneralProperty: SCI_TOGGLEFOLD parameter: line value: 0];
+ };
+ break;
+ case SCN_MODIFIED:
+ // Decide depending on the modification type what to do.
+ // There can be more than one modification carried by one notification.
+ if (scn->modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT))
+ [editor sendNotification: NSTextDidChangeNotification];
+ break;
+ }
+ break;
+ }
+ case WM_COMMAND:
+ {
+ // Notifications for the editor itself.
+ ScintillaCocoa* backend = reinterpret_cast<ScintillaCocoa*>(lParam);
+ editor = backend->TopContainer();
+ switch (wParam >> 16)
+ {
+ case SCEN_KILLFOCUS:
+ [editor sendNotification: NSTextDidEndEditingNotification];
+ break;
+ case SCEN_SETFOCUS: // Nothing to do for now.
+ break;
+ }
+ break;
+ }
+ };
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Initialization of the view. Used to setup a few other things we need.
+ */
+- (id) initWithFrame: (NSRect) frame
+{
+ self = [super initWithFrame:frame];
+ if (self)
+ {
+ mContent = [[[InnerView alloc] init] autorelease];
+ mBackend = new ScintillaCocoa(mContent);
+ mContent.owner = self;
+ [self addSubview: mContent];
+
+ // Initialize the scrollers but don't show them yet.
+ // Pick an arbitrary size, just to make NSScroller selecting the proper scroller direction
+ // (horizontal or vertical).
+ NSRect scrollerRect = NSMakeRect(0, 0, 100, 10);
+ mHorizontalScroller = [[[NSScroller alloc] initWithFrame: scrollerRect] autorelease];
+ [mHorizontalScroller setHidden: YES];
+ [mHorizontalScroller setTarget: self];
+ [mHorizontalScroller setAction: @selector(scrollerAction:)];
+ [self addSubview: mHorizontalScroller];
+
+ scrollerRect.size = NSMakeSize(10, 100);
+ mVerticalScroller = [[[NSScroller alloc] initWithFrame: scrollerRect] autorelease];
+ [mVerticalScroller setHidden: YES];
+ [mVerticalScroller setTarget: self];
+ [mVerticalScroller setAction: @selector(scrollerAction:)];
+ [self addSubview: mVerticalScroller];
+
+ // Establish a connection from the back end to this container so we can handle situations
+ // which require our attention.
+ mBackend->RegisterNotifyCallback(nil, notification);
+
+ // Setup a special indicator used in the editor to provide visual feedback for
+ // input composition, depending on language, keyboard etc.
+ [self setColorProperty: SCI_INDICSETFORE parameter: INPUT_INDICATOR fromHTML: @"#FF9A00"];
+ [self setGeneralProperty: SCI_INDICSETUNDER parameter: INPUT_INDICATOR value: 1];
+ [self setGeneralProperty: SCI_INDICSETSTYLE parameter: INPUT_INDICATOR value: INDIC_ROUNDBOX];
+ [self setGeneralProperty: SCI_INDICSETALPHA parameter: INPUT_INDICATOR value: 100];
+ }
+ return self;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Release the backend.
+ */
+- (void) dealloc
+{
+ delete mBackend;
+ [super dealloc];
+}
+
+//--------------------------------------------------------------------------------------------------
+
+- (void) viewDidMoveToWindow
+{
+ [super viewDidMoveToWindow];
+
+ [self layout];
+
+ // Enable also mouse move events for our window (and so this view).
+ [[self window] setAcceptsMouseMovedEvents: YES];
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Used to position and size the parts of the editor (content + scrollers).
+ */
+- (void) layout
+{
+ int scrollerWidth = [NSScroller scrollerWidth];
+
+ NSSize size = [self frame].size;
+ NSPoint hScrollerOrigin = {0, 0};
+ NSPoint vScrollerOrigin = {size.width - scrollerWidth, 0};
+
+ if (![mVerticalScroller isHidden])
+ {
+ // Consider user settings (left vs right vertical scrollbar).
+ BOOL isLeft = [[[NSUserDefaults standardUserDefaults] stringForKey: @"NSScrollerPosition"]
+ isEqualToString: @"left"];
+ if (isLeft)
+ {
+ vScrollerOrigin.x = 0;
+ hScrollerOrigin.x = scrollerWidth;
+ };
+
+ size.width -= scrollerWidth;
+ }
+ if (![mHorizontalScroller isHidden])
+ {
+ size.height -= scrollerWidth;
+ vScrollerOrigin.y += scrollerWidth;
+ };
+
+ NSRect contentRect = {hScrollerOrigin.x, vScrollerOrigin.y, size.width, size.height};
+ [mContent setFrame: contentRect];
+
+ if (![mHorizontalScroller isHidden])
+ [mHorizontalScroller setFrame: NSMakeRect(hScrollerOrigin.x, hScrollerOrigin.y, size.width,
+ scrollerWidth)];
+ if (![mVerticalScroller isHidden])
+ [mVerticalScroller setFrame: NSMakeRect(vScrollerOrigin.x, vScrollerOrigin.y, scrollerWidth,
+ size.height)];
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Called by the backend to adjust the vertical scroller (range and page).
+ *
+ * @param range Determines the total size of the scroll area used in the editor.
+ * @param page Determines how many pixels a page is.
+ * @result Returns YES if anything changed, otherwise NO.
+ */
+- (BOOL) setVerticalScrollRange: (int) range page: (int) page
+{
+ BOOL result = NO;
+ BOOL hideScroller = page >= range;
+
+ if ([mVerticalScroller isHidden] != hideScroller)
+ {
+ result = YES;
+ [mVerticalScroller setHidden: hideScroller];
+ if (!hideScroller)
+ [mVerticalScroller setFloatValue: 0];
+ [self layout];
+ }
+
+ if (!hideScroller)
+ {
+ [mVerticalScroller setEnabled: YES];
+
+ CGFloat currentProportion = [mVerticalScroller knobProportion];
+ CGFloat newProportion = page / (CGFloat) range;
+ if (currentProportion != newProportion)
+ {
+ result = YES;
+ [mVerticalScroller setKnobProportion: newProportion];
+ }
+ }
+
+ return result;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Used to set the position of the vertical scroll thumb.
+ *
+ * @param position The relative position in the rang [0..1];
+ */
+- (void) setVerticalScrollPosition: (float) position
+{
+ [mVerticalScroller setFloatValue: position];
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Called by the backend to adjust the horizontal scroller (range and page).
+ *
+ * @param range Determines the total size of the scroll area used in the editor.
+ * @param page Determines how many pixels a page is.
+ * @result Returns YES if anything changed, otherwise NO.
+ */
+- (BOOL) setHorizontalScrollRange: (int) range page: (int) page
+{
+ BOOL result = NO;
+ BOOL hideScroller = page >= range;
+
+ if ([mHorizontalScroller isHidden] != hideScroller)
+ {
+ result = YES;
+ [mHorizontalScroller setHidden: hideScroller];
+ [self layout];
+ }
+
+ if (!hideScroller)
+ {
+ [mHorizontalScroller setEnabled: YES];
+
+ CGFloat currentProportion = [mHorizontalScroller knobProportion];
+ CGFloat newProportion = page / (CGFloat) range;
+ if (currentProportion != newProportion)
+ {
+ result = YES;
+ [mHorizontalScroller setKnobProportion: newProportion];
+ }
+ }
+
+ return result;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Used to set the position of the vertical scroll thumb.
+ *
+ * @param position The relative position in the rang [0..1];
+ */
+- (void) setHorizontalScrollPosition: (float) position
+{
+ [mHorizontalScroller setFloatValue: position];
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Triggered by one of the scrollers when it gets manipulated by the user. Notify the backend
+ * about the change.
+ */
+- (void) scrollerAction: (id) sender
+{
+ float position = [sender floatValue];
+ mBackend->DoScroll(position, [sender hitPart], sender == mHorizontalScroller);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Used to reposition our content depending on the size of the view.
+ */
+- (void) setFrame: (NSRect) newFrame
+{
+ [super setFrame: newFrame];
+ [self layout];
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Getter for the current text in raw form (no formatting information included).
+ */
+- (NSString*) string
+{
+ NSString *result = nil;
+
+ char *buffer(0);
+ const int length = mBackend->WndProc(SCI_GETLENGTH, 0, 0);
+ if ( length > 0 )
+ {
+ buffer = new char[length + 1];
+ try
+ {
+ mBackend->WndProc(SCI_GETTEXT, length + 1, (sptr_t) buffer);
+ mBackend->WndProc(SCI_SETSAVEPOINT, 0, 0);
+
+ result = [NSString stringWithUTF8String: buffer];
+ delete[] buffer;
+ }
+ catch (...)
+ {
+ delete[] buffer;
+ buffer = 0;
+ }
+ }
+
+ return result;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+- (void) setEditable: (BOOL) editable
+{
+ mBackend->WndProc(SCI_SETREADONLY, editable ? 0 : 1, 0);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Setter for the current text (no formatting included).
+ */
+- (void) setString: (NSString*) aString
+{
+ const char* text = [aString UTF8String];
+ mBackend->WndProc(SCI_SETTEXT, 0, (long) text);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+- (InnerView*) content
+{
+ return mContent;
+}
+
+- (void) setEditorProperty: (int) property wParam: (long) parameter lParam: (long) value
+{
+ mBackend->WndProc(property, parameter, value);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * This is a helper method to get a property in the backend, with native parameters.
+ *
+ * @param property Main property like SCI_STYLESETFORE for which a value is to get.
+ * @param parameter Additional info for this property like a parameter or index.
+ * @result A generic value which must be interpreted depending on the property queried.
+ */
+- (long) getEditorProperty: (int) property wParam: (long) parameter
+{
+ return mBackend->WndProc(property, parameter, 0);
+}
+
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * This is a helper method to set properties in the backend, with native parameters.
+ *
+ * @param property Main property like SCI_STYLESETFORE for which a value is to be set.
+ * @param parameter Additional info for this property like a parameter or index.
+ * @param value The actual value. It depends on the property what this parameter means.
+ */
+- (void) setGeneralProperty: (int) property parameter: (long) parameter value: (long) value
+{
+ mBackend->WndProc(property, parameter, value);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * This is a helper method to get a property in the backend, with native parameters.
+ *
+ * @param property Main property like SCI_STYLESETFORE for which a value is to get.
+ * @param parameter Additional info for this property like a parameter or index.
+ * @result A generic value which must be interpreted depending on the property queried.
+ */
+- (long) getGeneralProperty: (int) property parameter: (long) parameter
+{
+ return mBackend->WndProc(property, parameter, 0);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Specialized property setter for colors.
+ */
+- (void) setColorProperty: (int) property parameter: (long) parameter value: (NSColor*) value
+{
+ if ([value colorSpaceName] != NSDeviceRGBColorSpace)
+ value = [value colorUsingColorSpaceName: NSDeviceRGBColorSpace];
+ long red = [value redComponent] * 255;
+ long green = [value greenComponent] * 255;
+ long blue = [value blueComponent] * 255;
+
+ long color = (blue << 16) + (green << 8) + red;
+ mBackend->WndProc(property, parameter, color);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Another color property setting, which allows to specify the color as string like in HTML
+ * documents (i.e. with leading # and either 3 hex digits or 6).
+ */
+- (void) setColorProperty: (int) property parameter: (long) parameter fromHTML: (NSString*) fromHTML
+{
+ if ([fromHTML length] > 3 && [fromHTML characterAtIndex: 0] == '#')
+ {
+ bool longVersion = [fromHTML length] > 6;
+ int index = 1;
+
+ char value[3] = {0, 0, 0};
+ value[0] = [fromHTML characterAtIndex: index++];
+ if (longVersion)
+ value[1] = [fromHTML characterAtIndex: index++];
+ else
+ value[1] = value[0];
+
+ unsigned rawRed;
+ [[NSScanner scannerWithString: [NSString stringWithUTF8String: value]] scanHexInt: &rawRed];
+
+ value[0] = [fromHTML characterAtIndex: index++];
+ if (longVersion)
+ value[1] = [fromHTML characterAtIndex: index++];
+ else
+ value[1] = value[0];
+
+ unsigned rawGreen;
+ [[NSScanner scannerWithString: [NSString stringWithUTF8String: value]] scanHexInt: &rawGreen];
+
+ value[0] = [fromHTML characterAtIndex: index++];
+ if (longVersion)
+ value[1] = [fromHTML characterAtIndex: index++];
+ else
+ value[1] = value[0];
+
+ unsigned rawBlue;
+ [[NSScanner scannerWithString: [NSString stringWithUTF8String: value]] scanHexInt: &rawBlue];
+
+ long color = (rawBlue << 16) + (rawGreen << 8) + rawRed;
+ mBackend->WndProc(property, parameter, color);
+ }
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Specialized property getter for colors.
+ */
+- (NSColor*) getColorProperty: (int) property parameter: (long) parameter
+{
+ int color = mBackend->WndProc(property, parameter, 0);
+ float red = (color & 0xFF) / 255.0;
+ float green = ((color >> 8) & 0xFF) / 255.0;
+ float blue = ((color >> 16) & 0xFF) / 255.0;
+ NSColor* result = [NSColor colorWithDeviceRed: red green: green blue: blue alpha: 1];
+ return result;
+}
+
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Specialized property setter for references (pointers, addresses).
+ */
+- (void) setReferenceProperty: (int) property parameter: (long) parameter value: (const void*) value
+{
+ mBackend->WndProc(property, parameter, (sptr_t) value);
+}
+
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Specialized property getter for references (pointers, addresses).
+ */
+- (const void*) getReferenceProperty: (int) property parameter: (long) parameter
+{
+ return (const void*) mBackend->WndProc(property, parameter, 0);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Specialized property setter for string values.
+ */
+- (void) setStringProperty: (int) property parameter: (long) parameter value: (NSString*) value
+{
+ const char* rawValue = [value UTF8String];
+ mBackend->WndProc(property, parameter, (sptr_t) rawValue);
+}
+
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Specialized property getter for string values.
+ */
+- (NSString*) getStringProperty: (int) property parameter: (long) parameter
+{
+ const char* rawValue = (const char*) mBackend->WndProc(property, parameter, 0);
+ return [NSString stringWithUTF8String: rawValue];
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Specialized property setter for lexer properties, which are commonly passed as strings.
+ */
+- (void) setLexerProperty: (NSString*) name value: (NSString*) value
+{
+ const char* rawName = [name UTF8String];
+ const char* rawValue = [value UTF8String];
+ mBackend->WndProc(SCI_SETPROPERTY, (sptr_t) rawName, (sptr_t) rawValue);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Specialized property getter for references (pointers, addresses).
+ */
+- (NSString*) getLexerProperty: (NSString*) name
+{
+ const char* rawName = [name UTF8String];
+ const char* result = (const char*) mBackend->WndProc(SCI_SETPROPERTY, (sptr_t) rawName, 0);
+ return [NSString stringWithUTF8String: result];
+}
+
+@end
+
+//--------------------------------------------------------------------------------------------------
+