aboutsummaryrefslogtreecommitdiffhomepage
path: root/cocoa/ScintillaView.mm
diff options
context:
space:
mode:
Diffstat (limited to 'cocoa/ScintillaView.mm')
-rw-r--r--cocoa/ScintillaView.mm241
1 files changed, 131 insertions, 110 deletions
diff --git a/cocoa/ScintillaView.mm b/cocoa/ScintillaView.mm
index 43ceb8b48..d5f658755 100644
--- a/cocoa/ScintillaView.mm
+++ b/cocoa/ScintillaView.mm
@@ -19,9 +19,6 @@ using namespace Scintilla;
static NSCursor* reverseArrowCursor;
static NSCursor* waitCursor;
-// The scintilla indicator used for keyboard input.
-#define INPUT_INDICATOR INDIC_MAX - 1
-
NSString *const SCIUpdateUINotification = @"SCIUpdateUI";
/**
@@ -347,14 +344,52 @@ static NSCursor *cursorFromEnum(Window::Cursor cursor)
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
{
- return nil;
+ const NSRange posRange = mOwner.backend->PositionsFromCharacters(aRange);
+ // The backend validated aRange and may have removed characters beyond the end of the document.
+ const NSRange charRange = mOwner.backend->CharactersFromPositions(posRange);
+ if (!NSEqualRanges(aRange, charRange))
+ {
+ *actualRange = charRange;
+ }
+
+ [mOwner message: SCI_SETTARGETRANGE wParam: posRange.location lParam: NSMaxRange(posRange)];
+ std::string text([mOwner message: SCI_TARGETASUTF8] + 1, 0);
+ [mOwner message: SCI_TARGETASUTF8 wParam: 0 lParam: reinterpret_cast<sptr_t>(&text[0])];
+ NSString *result = [NSString stringWithUTF8String: text.c_str()];
+ NSMutableAttributedString *asResult = [[[NSMutableAttributedString alloc] initWithString:result] autorelease];
+
+ const NSRange rangeAS = NSMakeRange(0, [asResult length]);
+ const long style = [mOwner message: SCI_GETSTYLEAT wParam:posRange.location];
+ std::string fontName([mOwner message: SCI_STYLEGETFONT wParam:style lParam:0] + 1, 0);
+ [mOwner message: SCI_STYLEGETFONT wParam:style lParam:(sptr_t)&fontName[0]];
+ const CGFloat fontSize = [mOwner message: SCI_STYLEGETSIZEFRACTIONAL wParam:style] / 100.0f;
+ NSString *sFontName = [NSString stringWithUTF8String: fontName.c_str()];
+ NSFont *font = [NSFont fontWithName:sFontName size:fontSize];
+ [asResult addAttribute:NSFontAttributeName value:font range:rangeAS];
+
+ return asResult;
}
//--------------------------------------------------------------------------------------------------
- (NSUInteger) characterIndexForPoint: (NSPoint) point
{
- return NSNotFound;
+ const NSRect rectPoint = {point, NSZeroSize};
+ const NSRect rectInWindow = [self.window convertRectFromScreen:rectPoint];
+ const NSRect rectLocal = [[[self superview] superview] convertRect:rectInWindow fromView:nil];
+
+ const long position = [mOwner message: SCI_CHARPOSITIONFROMPOINT
+ wParam: rectLocal.origin.x
+ lParam: rectLocal.origin.y];
+ if (position == INVALID_POSITION)
+ {
+ return NSNotFound;
+ }
+ else
+ {
+ const NSRange index = mOwner.backend->CharactersFromPositions(NSMakeRange(position, 0));
+ return index.location;
+ }
}
//--------------------------------------------------------------------------------------------------
@@ -369,32 +404,19 @@ static NSCursor *cursorFromEnum(Window::Cursor cursor)
- (NSRect) firstRectForCharacterRange: (NSRange) aRange actualRange: (NSRangePointer) actualRange
{
+ const NSRange posRange = mOwner.backend->PositionsFromCharacters(aRange);
+
NSRect rect;
- rect.origin.x = [ScintillaView directCall: mOwner
- message: SCI_POINTXFROMPOSITION
- wParam: 0
- lParam: aRange.location];
- rect.origin.y = [ScintillaView directCall: mOwner
- message: SCI_POINTYFROMPOSITION
- wParam: 0
- lParam: aRange.location];
- NSUInteger rangeEnd = aRange.location + aRange.length;
- rect.size.width = [ScintillaView directCall: mOwner
- message: SCI_POINTXFROMPOSITION
- wParam: 0
- lParam: rangeEnd] - rect.origin.x;
- rect.size.height = [ScintillaView directCall: mOwner
- message: SCI_POINTYFROMPOSITION
- wParam: 0
- lParam: rangeEnd] - rect.origin.y;
- rect.size.height += [ScintillaView directCall: mOwner
- message: SCI_TEXTHEIGHT
- wParam: 0
- lParam: 0];
- rect = [[[self superview] superview] convertRect:rect toView:nil];
- rect = [self.window convertRectToScreen:rect];
-
- return rect;
+ rect.origin.x = [mOwner message: SCI_POINTXFROMPOSITION wParam: 0 lParam: posRange.location];
+ rect.origin.y = [mOwner message: SCI_POINTYFROMPOSITION wParam: 0 lParam: posRange.location];
+ const NSUInteger rangeEnd = NSMaxRange(posRange);
+ rect.size.width = [mOwner message: SCI_POINTXFROMPOSITION wParam: 0 lParam: rangeEnd] - rect.origin.x;
+ rect.size.height = [mOwner message: SCI_POINTYFROMPOSITION wParam: 0 lParam: rangeEnd] - rect.origin.y;
+ rect.size.height += [mOwner message: SCI_TEXTHEIGHT wParam: 0 lParam: 0];
+ const NSRect rectInWindow = [[[self superview] superview] convertRect:rect toView:nil];
+ const NSRect rectScreen = [self.window convertRectToScreen:rectInWindow];
+
+ return rectScreen;
}
//--------------------------------------------------------------------------------------------------
@@ -419,14 +441,27 @@ static NSCursor *cursorFromEnum(Window::Cursor cursor)
}
// Remove any previously marked text first.
- [self removeMarkedText];
+ mOwner.backend->CompositionUndo();
+ if (mMarkedTextRange.location != NSNotFound)
+ {
+ const NSRange posRangeMark = mOwner.backend->PositionsFromCharacters(mMarkedTextRange);
+ [mOwner message: SCI_SETEMPTYSELECTION wParam: posRangeMark.location];
+ }
+ mMarkedTextRange = NSMakeRange(NSNotFound, 0);
if (replacementRange.location == (NSNotFound-1))
// This occurs when the accent popup is visible and menu selected.
// Its replacing a non-existent position so do nothing.
return;
- [mOwner deleteRange: replacementRange];
+ if (replacementRange.location != NSNotFound)
+ {
+ const NSRange posRangeReplacement = mOwner.backend->PositionsFromCharacters(replacementRange);
+ [mOwner message: SCI_DELETERANGE
+ wParam: posRangeReplacement.location
+ lParam: posRangeReplacement.length];
+ [mOwner message: SCI_SETEMPTYSELECTION wParam: posRangeReplacement.location];
+ }
NSString* newText = @"";
if ([aString isKindOfClass:[NSString class]])
@@ -448,9 +483,10 @@ static NSCursor *cursorFromEnum(Window::Cursor cursor)
- (NSRange) selectedRange
{
- long begin = [mOwner getGeneralProperty: SCI_GETSELECTIONSTART parameter: 0];
- long end = [mOwner getGeneralProperty: SCI_GETSELECTIONEND parameter: 0];
- return NSMakeRange(begin, end - begin);
+ const long positionBegin = [mOwner message: SCI_GETSELECTIONSTART];
+ const long positionEnd = [mOwner message: SCI_GETSELECTIONEND];
+ NSRange posRangeSel = NSMakeRange(positionBegin, positionEnd-positionBegin);
+ return mOwner.backend->CharactersFromPositions(posRangeSel);
}
//--------------------------------------------------------------------------------------------------
@@ -473,86 +509,85 @@ static NSCursor *cursorFromEnum(Window::Cursor cursor)
if ([aString isKindOfClass:[NSAttributedString class]])
newText = (NSString*) [aString string];
- long currentPosition;
-
// Replace marked text if there is one.
if (mMarkedTextRange.length > 0)
{
+ mOwner.backend->CompositionUndo();
if (replacementRange.location != NSNotFound)
{
// This situation makes no sense and has not occurred in practice.
- // Should the marked range remain marked in addition to the new text
- // or should it be removed first?
NSLog(@"Can not handle a replacement range when there is also a marked range");
}
-
- [mOwner deleteRange: mMarkedTextRange];
- currentPosition = mMarkedTextRange.location;
+ else
+ {
+ replacementRange = mMarkedTextRange;
+ const NSRange posRangeMark = mOwner.backend->PositionsFromCharacters(mMarkedTextRange);
+ [mOwner message: SCI_SETEMPTYSELECTION wParam: posRangeMark.location];
+ }
}
else
{
// Must perform deletion before entering composition mode or else
// both document and undo history will not contain the deleted text
// leading to an inaccurate and unusable undo history.
+
+ // Convert selection virtual space into real space
+ mOwner.backend->ConvertSelectionVirtualSpace();
if (replacementRange.location != NSNotFound)
{
- [mOwner deleteRange: replacementRange];
- currentPosition = replacementRange.location;
+ const NSRange posRangeReplacement = mOwner.backend->PositionsFromCharacters(replacementRange);
+ [mOwner message: SCI_DELETERANGE
+ wParam: posRangeReplacement.location
+ lParam: posRangeReplacement.length];
}
else // No marked or replacement range, so replace selection
{
- // Ensure only a single selection
- mOwner.backend->SelectOnlyMainSelection();
-
- NSRange selectionRangeCurrent = [self selectedRange];
- if (selectionRangeCurrent.length > 0)
- {
- [mOwner deleteRange: selectionRangeCurrent];
+ if (!mOwner.backend->ScintillaCocoa::ClearAllSelections()) {
+ // Some of the selection is protected so can not perform composition here
+ return;
}
- currentPosition = selectionRangeCurrent.location;
+ // Ensure only a single selection.
+ mOwner.backend->SelectOnlyMainSelection();
+ replacementRange = [self selectedRange];
}
-
- // Switching into composition so remember if collecting undo.
- undoCollectionWasActive = [mOwner getGeneralProperty: SCI_GETUNDOCOLLECTION] != 0;
-
- // Keep Scintilla from collecting undo actions for the composition task.
- [mOwner setGeneralProperty: SCI_SETUNDOCOLLECTION value: 0];
}
- [mOwner message: SCI_SETEMPTYSELECTION wParam: currentPosition];
- // Note: Scintilla internally works almost always with bytes instead chars, so we need to take
- // this into account when determining selection ranges and such.
- int lengthInserted = mOwner.backend->InsertText(newText);
+ // To support IME input to multiple selections, the following code would
+ // need to insert newText at each selection, mark each piece of new text and then
+ // select range relative to each insertion.
- if (lengthInserted > 0)
+ if ([newText length])
{
- mMarkedTextRange = NSMakeRange(currentPosition, lengthInserted);
+ // Switching into composition.
+ mOwner.backend->CompositionStart();
+
+ NSRange posRangeCurrent = mOwner.backend->PositionsFromCharacters(NSMakeRange(replacementRange.location, 0));
+ // Note: Scintilla internally works almost always with bytes instead chars, so we need to take
+ // this into account when determining selection ranges and such.
+ int lengthInserted = mOwner.backend->InsertText(newText);
+ posRangeCurrent.length = lengthInserted;
+ mMarkedTextRange = mOwner.backend->CharactersFromPositions(posRangeCurrent);
// Mark the just inserted text. Keep the marked range for later reset.
- [mOwner setGeneralProperty: SCI_SETINDICATORCURRENT value: INPUT_INDICATOR];
+ [mOwner setGeneralProperty: SCI_SETINDICATORCURRENT value: INDIC_IME];
[mOwner setGeneralProperty: SCI_INDICATORFILLRANGE
- parameter: mMarkedTextRange.location
- value: mMarkedTextRange.length];
+ parameter: posRangeCurrent.location
+ value: posRangeCurrent.length];
}
else
{
mMarkedTextRange = NSMakeRange(NSNotFound, 0);
// Re-enable undo action collection if composition ended (indicated by an empty mark string).
- if (undoCollectionWasActive)
- [mOwner setGeneralProperty: SCI_SETUNDOCOLLECTION value: range.length == 0];
+ mOwner.backend->CompositionCommit();
}
// Select the part which is indicated in the given range. It does not scroll the caret into view.
if (range.length > 0)
{
// range is in characters so convert to bytes for selection.
- long rangeStart = currentPosition;
- for (size_t characterInComposition=0; characterInComposition<range.location; characterInComposition++)
- rangeStart = [mOwner getGeneralProperty: SCI_POSITIONAFTER parameter: rangeStart];
- long rangeEnd = rangeStart;
- for (size_t characterInRange=0; characterInRange<range.length; characterInRange++)
- rangeEnd = [mOwner getGeneralProperty: SCI_POSITIONAFTER parameter: rangeEnd];
- [mOwner setGeneralProperty: SCI_SETSELECTION parameter: rangeEnd value: rangeStart];
+ range.location += replacementRange.location;
+ NSRange posRangeSelect = mOwner.backend->PositionsFromCharacters(range);
+ [mOwner setGeneralProperty: SCI_SETSELECTION parameter: NSMaxRange(posRangeSelect) value: posRangeSelect.location];
}
}
@@ -562,35 +597,8 @@ static NSCursor *cursorFromEnum(Window::Cursor cursor)
{
if (mMarkedTextRange.length > 0)
{
- [mOwner setGeneralProperty: SCI_SETINDICATORCURRENT value: INPUT_INDICATOR];
- [mOwner setGeneralProperty: SCI_INDICATORCLEARRANGE
- parameter: mMarkedTextRange.location
- value: mMarkedTextRange.length];
- mMarkedTextRange = NSMakeRange(NSNotFound, 0);
-
- // Reenable undo action collection, after we are done with text composition.
- if (undoCollectionWasActive)
- [mOwner setGeneralProperty: SCI_SETUNDOCOLLECTION value: 1];
- }
-}
-
-//--------------------------------------------------------------------------------------------------
-
-/**
- * Removes any currently marked text.
- */
-- (void) removeMarkedText
-{
- if (mMarkedTextRange.length > 0)
- {
- // We have already marked text. Replace that.
- [mOwner deleteRange: mMarkedTextRange];
-
+ mOwner.backend->CompositionCommit();
mMarkedTextRange = NSMakeRange(NSNotFound, 0);
-
- // Reenable undo action collection, after we are done with text composition.
- if (undoCollectionWasActive)
- [mOwner setGeneralProperty: SCI_SETUNDOCOLLECTION value: 1];
}
}
@@ -831,12 +839,24 @@ static NSCursor *cursorFromEnum(Window::Cursor cursor)
- (void) paste: (id) sender
{
#pragma unused(sender)
+ if (mMarkedTextRange.location != NSNotFound)
+ {
+ [[NSTextInputContext currentInputContext] discardMarkedText];
+ mOwner.backend->CompositionCommit();
+ mMarkedTextRange = NSMakeRange(NSNotFound, 0);
+ }
mOwner.backend->Paste();
}
- (void) undo: (id) sender
{
#pragma unused(sender)
+ if (mMarkedTextRange.location != NSNotFound)
+ {
+ [[NSTextInputContext currentInputContext] discardMarkedText];
+ mOwner.backend->CompositionCommit();
+ mMarkedTextRange = NSMakeRange(NSNotFound, 0);
+ }
mOwner.backend->Undo();
}
@@ -848,7 +868,7 @@ static NSCursor *cursorFromEnum(Window::Cursor cursor)
- (BOOL) canUndo
{
- return mOwner.backend->CanUndo();
+ return mOwner.backend->CanUndo() && (mMarkedTextRange.location == NSNotFound);
}
- (BOOL) canRedo
@@ -1141,10 +1161,10 @@ static NSCursor *cursorFromEnum(Window::Cursor cursor)
// 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: @"#FF0000"];
- [self setGeneralProperty: SCI_INDICSETUNDER parameter: INPUT_INDICATOR value: 1];
- [self setGeneralProperty: SCI_INDICSETSTYLE parameter: INPUT_INDICATOR value: INDIC_PLAIN];
- [self setGeneralProperty: SCI_INDICSETALPHA parameter: INPUT_INDICATOR value: 100];
+ [self setColorProperty: SCI_INDICSETFORE parameter: INDIC_IME fromHTML: @"#FF0000"];
+ [self setGeneralProperty: SCI_INDICSETUNDER parameter: INDIC_IME value: 1];
+ [self setGeneralProperty: SCI_INDICSETSTYLE parameter: INDIC_IME value: INDIC_PLAIN];
+ [self setGeneralProperty: SCI_INDICSETALPHA parameter: INDIC_IME value: 100];
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self
@@ -1318,7 +1338,8 @@ static NSCursor *cursorFromEnum(Window::Cursor cursor)
{
if (aRange.length > 0)
{
- [self message: SCI_DELETERANGE wParam: aRange.location lParam: aRange.length];
+ NSRange posRange = mBackend->PositionsFromCharacters(aRange);
+ [self message: SCI_DELETERANGE wParam: posRange.location lParam: posRange.length];
}
}