NSTextView Double-Click Setup

If a NSTextViewcontains the following:

SELECT someTable.someColumn FROM someTable

And the user double clicks someTable.someColumn, the whole thing is selected (both sides of the period). In this particular case (the query editor) it would be wiser to choose either someTable, or someColumn.

I tried to look back to find out if I can determine how to configure the selection, but so far I have not succeeded.

At the moment, I think this is a subclassification NSTextViewand execution of something like:

- (void)mouseDown:(NSEvent *)theEvent
{
  if(theEvent.clickCount == 2)
  {
    // TODO: Handle double click selection.
  }
  else
  {
    [super mouseDown:theEvent];
  }
}

Does anyone have any thoughts or alternatives to this? (Is there any other method that I'm missing, which might be better for overriding)?

+3
source share
2 answers

, , NSTextView selectionRangeForProposedRange:granularity: . Apple "Cocoa " doc (https://developer.apple.com/library/prerelease/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/TextEditing/TextEditing.html - . " NSTextView" ). Apple : " arent (, )." , Apple , , , selectionRangeForProposedRange:granularity: , , , - , ; - , . , , ; . , Apple , . , , "", . ; Apple, .

, Apple doc : " ( ) ". , , : doubleClickAtIndex: NSAttributedString ( NSAttributedStringKitAdditions). ( NSTextStorage NSAttributedString) Cocoa . NSTextStorage , MyTextStorage. NSTextStorage Apple.

MyTextStorage .h:

@interface MyTextStorage : NSTextStorage
- (id)init;
- (id)initWithAttributedString:(NSAttributedString *)attrStr;
@end

MyTextStorage.m:

@interface MyTextStorage ()
{
    NSMutableAttributedString *contents;
}
@end

@implementation MyTextStorage

- (id)initWithAttributedString:(NSAttributedString *)attrStr
{
    if (self = [super init])
    {
        contents = attrStr ? [attrStr mutableCopy] : [[NSMutableAttributedString alloc] init];
    }
    return self;
}

- init
{
    return [self initWithAttributedString:nil];
}

- (void)dealloc
{
    [contents release];
    [super dealloc];
}

// The next set of methods are the primitives for attributed and mutable attributed string...

- (NSString *)string
{
    return [contents string];
}

- (NSDictionary *)attributesAtIndex:(NSUInteger)location effectiveRange:(NSRange *)range
{
    return [contents attributesAtIndex:location effectiveRange:range];
}

- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str
{
    NSUInteger origLen = [self length];
    [contents replaceCharactersInRange:range withString:str];
    [self edited:NSTextStorageEditedCharacters range:range changeInLength:[self length] - origLen];
}

- (void)setAttributes:(NSDictionary *)attrs range:(NSRange)range
{
    [contents setAttributes:attrs range:range];
    [self edited:NSTextStorageEditedAttributes range:range changeInLength:0];
}

// And now the actual reason for this subclass: to provide code-aware word selection behavior

- (NSRange)doubleClickAtIndex:(NSUInteger)location
{
    // Start by calling super to get a proposed range.  This is documented to raise if location >= [self length]
    // or location < 0, so in the code below we can assume that location indicates a valid character position.
    NSRange superRange = [super doubleClickAtIndex:location];
    NSString *string = [self string];

    // If the user has actually double-clicked a period, we want to just return the range of the period.
    if ([string characterAtIndex:location] == '.')
        return NSMakeRange(location, 1);

    // The case where super behavior is wrong involves the dot operator; x.y should not be considered a word.
    // So we check for a period before or after the anchor position, and trim away the periods and everything
    // past them on both sides.  This will correctly handle longer sequences like foo.bar.baz.is.a.test.
    NSRange candidateRangeBeforeLocation = NSMakeRange(superRange.location, location - superRange.location);
    NSRange candidateRangeAfterLocation = NSMakeRange(location + 1, NSMaxRange(superRange) - (location + 1));
    NSRange periodBeforeRange = [string rangeOfString:@"." options:NSBackwardsSearch range:candidateRangeBeforeLocation];
    NSRange periodAfterRange = [string rangeOfString:@"." options:(NSStringCompareOptions)0 range:candidateRangeAfterLocation];

    if (periodBeforeRange.location != NSNotFound)
    {
        // Change superRange to start after the preceding period; fix its length so its end remains unchanged.
        superRange.length -= (periodBeforeRange.location + 1 - superRange.location);
        superRange.location = periodBeforeRange.location + 1;
    }

    if (periodAfterRange.location != NSNotFound)
    {
        // Change superRange to end before the following period
        superRange.length -= (NSMaxRange(superRange) - periodAfterRange.location);
    }

    return superRange;
}

@end

. NSTextView, awakeFromNib; , , , , ; awakeFromNib , , nib, . ( textview - NSTextView):

[[textview layoutManager] replaceTextStorage:[[[MyTextStorage alloc] init] autorelease]];

, !

, , NSAttributedString, nextWordFromIndex:forward:, Cocoa, / . , , . - , / a.b.c.d(, , ) - , . .

+6

NSTextView -selectionRangeForProposedRange:granularity:, - :

-(NSRange)selectionRangeForProposedRange:(NSRange)proposedSelRange granularity:(NSSelectionGranularity)granularity
{
    if (granularity == NSSelectByWord)
    {
        NSRange doubleRange = [[self textStorage] doubleClickAtIndex:proposedSelRange.location];
        if (doubleRange.location != NSNotFound)
        {
            NSRange dotRange = [[[self textStorage] string] rangeOfString:@"." options:0 range:doubleRange];
            if (dotRange.location != NSNotFound)
            {
                // double click after '.' ?
                if (dotRange.location < proposedSelRange.location)
                    return NSMakeRange(dotRange.location + 1, doubleRange.length - (dotRange.location-doubleRange.location) - 1);
                else
                    return NSMakeRange(doubleRange.location, dotRange.location-doubleRange.location);
            }
        }
    }
    return [super selectionRangeForProposedRange:proposedSelRange granularity:granularity];
}
+1

All Articles