diff options
-rw-r--r-- | Source/CMTextView.h | 12 | ||||
-rw-r--r-- | Source/CMTextView.m | 154 | ||||
-rw-r--r-- | Source/CustomQuery.h | 2 | ||||
-rw-r--r-- | Source/CustomQuery.m | 57 |
4 files changed, 158 insertions, 67 deletions
diff --git a/Source/CMTextView.h b/Source/CMTextView.h index 96028a5e..1546d1e8 100644 --- a/Source/CMTextView.h +++ b/Source/CMTextView.h @@ -65,8 +65,19 @@ static inline void NSMutableAttributedStringAddAttributeValueRange (NSMutableAtt NSInteger currentSnippetIndex; BOOL snippetWasJustInserted; + + NSColor *queryHiliteColor; + NSColor *queryEditorBackgroundColor; + NSRange queryRange; + BOOL shouldHiliteQuery; + } +@property(retain) NSColor* queryHiliteColor; +@property(retain) NSColor* queryEditorBackgroundColor; +@property(assign) NSRange queryRange; +@property(assign) BOOL shouldHiliteQuery; + - (IBAction)showMySQLHelpForCurrentWord:(id)sender; - (BOOL) isNextCharMarkedBy:(id)attribute withValue:(id)aValue; @@ -92,6 +103,7 @@ static inline void NSMutableAttributedStringAddAttributeValueRange (NSMutableAtt - (NSUInteger) getLineNumberForCharacterIndex:(NSUInteger)anIndex; - (void) autoHelp; - (void) doSyntaxHighlighting; +- (NSBezierPath*)roundedBezierPathAroundRange:(NSRange)aRange; - (void) setConnection:(MCPConnection *)theConnection withVersion:(NSInteger)majorVersion; - (void) doCompletionByUsingSpellChecker:(BOOL)isDictMode fuzzyMode:(BOOL)fuzzySearch; - (NSArray *)suggestionsForSQLCompletionWith:(NSString *)currentWord dictMode:(BOOL)isDictMode browseMode:(BOOL)dbBrowseMode withTableName:(NSString*)aTableName withDbName:(NSString*)aDbName; diff --git a/Source/CMTextView.m b/Source/CMTextView.m index 55e6d4e1..c14f5d59 100644 --- a/Source/CMTextView.m +++ b/Source/CMTextView.m @@ -72,8 +72,23 @@ YY_BUFFER_STATE yy_scan_string (const char *); #pragma mark - +// some helper functions for handling rectangles and points +// needed in roundedBezierPathAroundRange: +static inline CGFloat SPRectTop(NSRect rectangle) { return rectangle.origin.y; } +static inline CGFloat SPRectBottom(NSRect rectangle) { return rectangle.origin.y+rectangle.size.height; } +static inline CGFloat SPRectLeft(NSRect rectangle) { return rectangle.origin.x; } +static inline CGFloat SPRectRight(NSRect rectangle) { return rectangle.origin.x+rectangle.size.width; } +static inline CGFloat SPPointDistance(NSPoint a, NSPoint b) { return sqrt( (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) ); } +static inline NSPoint SPPointOnLine(NSPoint a, NSPoint b, CGFloat t) { return NSMakePoint(a.x*(1.-t) + b.x*t, a.y*(1.-t) + b.y*t); } + + @implementation CMTextView +@synthesize queryHiliteColor; +@synthesize queryEditorBackgroundColor; +@synthesize queryRange; +@synthesize shouldHiliteQuery; + /* * Sort function (mainly used to sort the words in the textView) */ @@ -147,6 +162,11 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) prefs = [[NSUserDefaults standardUserDefaults] retain]; + // Register observers for the when editor background colors preference changes + [prefs addObserver:self forKeyPath:SPCustomQueryEditorBackgroundColor options:NSKeyValueObservingOptionNew context:NULL]; + [prefs addObserver:self forKeyPath:SPCustomQueryEditorHighlightQueryColor options:NSKeyValueObservingOptionNew context:NULL]; + [prefs addObserver:self forKeyPath:SPCustomQueryHighlightCurrentQuery options:NSKeyValueObservingOptionNew context:NULL]; + } - (void) setConnection:(MCPConnection *)theConnection withVersion:(NSInteger)majorVersion @@ -155,6 +175,23 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) mySQLmajorVersion = majorVersion; } +/** + * This method is called as part of Key Value Observing which is used to watch for prefernce changes which effect the interface. + */ +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if ([keyPath isEqualToString:SPCustomQueryEditorBackgroundColor]) { + [self setQueryEditorBackgroundColor:[NSUnarchiver unarchiveObjectWithData:[change objectForKey:NSKeyValueChangeNewKey]]]; + [self setNeedsDisplay:YES]; + } else if ([keyPath isEqualToString:SPCustomQueryEditorHighlightQueryColor]) { + [self setQueryHiliteColor:[NSUnarchiver unarchiveObjectWithData:[change objectForKey:NSKeyValueChangeNewKey]]]; + [self setNeedsDisplay:YES]; + } else if ([keyPath isEqualToString:SPCustomQueryHighlightCurrentQuery]) { + [self setShouldHiliteQuery:[[change objectForKey:NSKeyValueChangeNewKey] boolValue]]; + [self setNeedsDisplay:YES]; + } +} + /* * Return an array of NSDictionary containing the sorted strings representing * the set of unique words, SQL keywords, user-defined funcs/procs, tables etc. @@ -2911,40 +2948,109 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) } - - (void)drawRect:(NSRect)rect { + // Draw textview's background since due to the snippet highlighting we're responsible for it. + [[[NSUnarchiver unarchiveObjectWithData:[prefs dataForKey:SPCustomQueryEditorBackgroundColor]] retain] setFill]; + [[self queryEditorBackgroundColor] setFill]; + NSRectFill(rect); + + // Highlightes the current query if set in the Pref and no snippet session + // and if nothing is selected in the text view + if ([self shouldHiliteQuery] && snippetControlCounter<=-1 && ![self selectedRange].length) { + NSUInteger rectCount; + NSRectArray queryRects = [[self layoutManager] rectArrayForCharacterRange: [self queryRange] + withinSelectedCharacterRange: [self queryRange] + inTextContainer: [self textContainer] + rectCount: &rectCount ]; + [[self queryHiliteColor] setFill]; + NSRectFillList(queryRects, rectCount); + } + // Highlight snippets coming from the Query Favorite text macro if(snippetControlCounter > -1) { - NSInteger i; - for(i=0; i<snippetControlMax; i++) { + for(NSUInteger i=0; i<snippetControlMax; i++) { if(snippetControlArray[i][0] > -1) { - NSUInteger rectCount; - NSRange snippetRange = NSMakeRange(snippetControlArray[i][0],snippetControlArray[i][1]); - NSRectArray snippetRects = [[self layoutManager] rectArrayForCharacterRange: snippetRange - withinSelectedCharacterRange: snippetRange - inTextContainer: [self textContainer] - rectCount: &rectCount ]; - for (NSUInteger j=0; j<rectCount; j++) - { - NSRect snippetRect = snippetRects[j]; - snippetRect = NSInsetRect(snippetRect, -4, 0.2); - NSBezierPath *aBezierPath = [NSBezierPath bezierPathWithRoundedRect:snippetRect xRadius:6 yRadius:10]; - if(i == currentSnippetIndex) - [[NSColor colorWithCalibratedRed:1.0 green:0.6 blue:0.0 alpha:0.7] setFill]; - else - [[NSColor colorWithCalibratedRed:1.0 green:1.0 blue:0.0 alpha:0.4] setFill]; - [aBezierPath fill]; - snippetRect = NSInsetRect(snippetRect, 1.3, 1.3); - aBezierPath = [NSBezierPath bezierPathWithRoundedRect:snippetRect xRadius:6 yRadius:10]; - [[NSColor colorWithCalibratedRed:1.0 green:1.0 blue:1.0 alpha:0.8] setFill]; - [aBezierPath fill]; + // choose the colors for the snippet parts + if(i == currentSnippetIndex) { + [[NSColor colorWithCalibratedRed:1.0 green:0.6 blue:0.0 alpha:0.4] setFill]; + [[NSColor colorWithCalibratedRed:1.0 green:0.6 blue:0.0 alpha:0.8] setStroke]; + } else { + [[NSColor colorWithCalibratedRed:1.0 green:0.8 blue:0.2 alpha:0.2] setFill]; + [[NSColor colorWithCalibratedRed:1.0 green:0.8 blue:0.2 alpha:0.5] setStroke]; } + NSBezierPath *snippetPath = [self roundedBezierPathAroundRange: NSMakeRange(snippetControlArray[i][0],snippetControlArray[i][1]) ]; + [snippetPath fill]; + [snippetPath stroke]; } } } [super drawRect:rect]; +} +- (NSBezierPath*)roundedBezierPathAroundRange:(NSRange)aRange +{ + // parameters for snippet highlighting + CGFloat kappa = 0.5522847498; // magic number from http://www.whizkidtech.redprince.net/bezier/circle/ + CGFloat radius = 6; + CGFloat horzInset = -3; + CGFloat vertInset = 0.3; + BOOL connectDisconnectedPartsWithLine = NO; + + NSBezierPath *funkyPath = [NSBezierPath bezierPath]; + NSUInteger rectCount; + NSRectArray rects = [[self layoutManager] rectArrayForCharacterRange: aRange + withinSelectedCharacterRange: aRange + inTextContainer: [self textContainer] + rectCount: &rectCount ]; + if (rectCount>2 || (rectCount>1 && (SPRectRight(rects[1]) >= SPRectLeft(rects[0]) || connectDisconnectedPartsWithLine))) { + // highlight complicated multiline snippet + NSRect lineRects[4]; + lineRects[0] = rects[0]; + lineRects[1] = rects[1]; + lineRects[2] = rects[rectCount-2]; + lineRects[3] = rects[rectCount-1]; + for(int j=0;j<4;j++) lineRects[j] = NSInsetRect(lineRects[j], horzInset, vertInset); + NSPoint vertices[8]; + vertices[0] = NSMakePoint( SPRectLeft(lineRects[0]), SPRectTop(lineRects[0]) ); // point a + vertices[1] = NSMakePoint( SPRectRight(lineRects[0]), SPRectTop(lineRects[0]) ); // point b + vertices[2] = NSMakePoint( SPRectRight(lineRects[2]), SPRectBottom(lineRects[2]) ); // point c + vertices[3] = NSMakePoint( SPRectRight(lineRects[3]), SPRectBottom(lineRects[2]) ); // point d + vertices[4] = NSMakePoint( SPRectRight(lineRects[3]), SPRectBottom(lineRects[3]) ); // point e + vertices[5] = NSMakePoint( SPRectLeft(lineRects[3]), SPRectBottom(lineRects[3]) ); // point f + vertices[6] = NSMakePoint( SPRectLeft(lineRects[1]), SPRectTop(lineRects[1]) ); // point g + vertices[7] = NSMakePoint( SPRectLeft(lineRects[0]), SPRectTop(lineRects[1]) ); // point h + + for (NSUInteger j=0; j<8; j++) { + NSPoint curr = vertices[j]; + NSPoint prev = vertices[(j+8-1)%8]; + NSPoint next = vertices[(j+1)%8]; + + CGFloat s = radius/SPPointDistance(prev, curr); + if (s>0.5) s = 0.5; + CGFloat t = radius/SPPointDistance(curr, next); + if (t>0.5) t = 0.5; + + NSPoint a = SPPointOnLine(curr, prev, 0.5); + NSPoint b = SPPointOnLine(curr, prev, s); + NSPoint c = curr; + NSPoint d = SPPointOnLine(curr, next, t); + NSPoint e = SPPointOnLine(curr, next, 0.5); + + if (j==0) [funkyPath moveToPoint:a]; + [funkyPath lineToPoint: b]; + [funkyPath curveToPoint:d controlPoint1:SPPointOnLine(b, c, kappa) controlPoint2:SPPointOnLine(d, c, kappa)]; + [funkyPath lineToPoint: e]; + } + } else { + //highlight disconnected snippet parts (or single line snippet) + for (NSUInteger j=0; j<rectCount; j++) { + NSRect rect = rects[j]; + rect = NSInsetRect(rect, horzInset, vertInset); + [funkyPath appendBezierPathWithRoundedRect:rect xRadius:radius yRadius:radius]; + } + } + return funkyPath; } @@ -3360,6 +3466,8 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse) { [[NSNotificationCenter defaultCenter] removeObserver:self]; [lineNumberView release]; + if(queryHiliteColor) [queryHiliteColor release]; + if(queryEditorBackgroundColor) [queryEditorBackgroundColor release]; [super dealloc]; } diff --git a/Source/CustomQuery.h b/Source/CustomQuery.h index 8e2e9fdd..e92dc4f0 100644 --- a/Source/CustomQuery.h +++ b/Source/CustomQuery.h @@ -117,7 +117,7 @@ NSRange currentQueryRange; NSArray *currentQueryRanges; NSRange oldThreadedQueryRange; - BOOL hasBackgroundAttribute; + BOOL selectionButtonCanBeEnabled; NSString *mySQLversion; NSTableColumn *sortColumn; diff --git a/Source/CustomQuery.m b/Source/CustomQuery.m index 4c850bde..f30e19ca 100644 --- a/Source/CustomQuery.m +++ b/Source/CustomQuery.m @@ -1273,22 +1273,16 @@ mySQLConnection = theConnection; currentQueryRanges = nil; - hasBackgroundAttribute = NO; - // Set up the interface - // Bind backgroundColor + [textView setAllowsDocumentBackgroundColorChange:YES]; - NSMutableDictionary *bindingOptions = [NSMutableDictionary dictionary]; - [bindingOptions setObject:NSUnarchiveFromDataTransformerName - forKey:@"NSValueTransformerName"]; - [textView bind: @"backgroundColor" - toObject: [NSUserDefaultsController sharedUserDefaultsController] - withKeyPath:@"values.CustomQueryEditorBackgroundColor" - options:bindingOptions]; - [textView setBackgroundColor:[NSUnarchiver unarchiveObjectWithData:[prefs dataForKey:SPCustomQueryEditorBackgroundColor]]]; [textView setTextColor:[NSUnarchiver unarchiveObjectWithData:[prefs dataForKey:SPCustomQueryEditorTextColor]]]; [textView setInsertionPointColor:[NSUnarchiver unarchiveObjectWithData:[prefs dataForKey:SPCustomQueryEditorCaretColor]]]; - + + [textView setQueryHiliteColor:[NSUnarchiver unarchiveObjectWithData:[prefs dataForKey:SPCustomQueryEditorHighlightQueryColor]]]; + [textView setQueryEditorBackgroundColor:[NSUnarchiver unarchiveObjectWithData:[prefs dataForKey:SPCustomQueryEditorBackgroundColor]]]; + [textView setShouldHiliteQuery:[prefs boolForKey:SPCustomQueryHighlightCurrentQuery]]; + [customQueryView setVerticalMotionCanBeginDrag:NO]; [textView setContinuousSpellCheckingEnabled:NO]; [autoindentMenuItem setState:([prefs boolForKey:SPCustomQueryAutoIndent]?NSOnState:NSOffState)]; @@ -2048,44 +2042,20 @@ // Ensure that the notification is from the custom query text view if ( [aNotification object] != textView ) return; - // Remove all background color attributes used by highlighting the current query - if([prefs boolForKey:SPCustomQueryHighlightCurrentQuery]) { - // Remove only the background attribute for the current range if still valid - NSRange textRange = NSMakeRange(0,[[textView string] length]); - NSRange r = NSIntersectionRange(currentQueryRange, textRange); - if(r.length) - [[textView textStorage] removeAttribute:NSBackgroundColorAttributeName range:r]; - else - [[textView textStorage] removeAttribute:NSBackgroundColorAttributeName range:textRange]; - } else { - // ensure that we do it only once - if(hasBackgroundAttribute) { - [[textView textStorage] removeAttribute:NSBackgroundColorAttributeName range:NSMakeRange(0,[[textView string] length])]; - hasBackgroundAttribute = NO; - } - } - BOOL isLookBehind = YES; NSRange currentSelection = [textView selectedRange]; NSUInteger caretPosition = currentSelection.location; NSRange qRange = [self queryRangeAtPosition:caretPosition lookBehind:&isLookBehind]; - // Highlight by setting a background color the current query - // if nothing is selected - if(qRange.length && !currentSelection.length && ![textView isSnippetMode]) { - if([prefs boolForKey:SPCustomQueryHighlightCurrentQuery]) { - [[textView textStorage] addAttribute: NSBackgroundColorAttributeName - value: [NSUnarchiver unarchiveObjectWithData:[prefs dataForKey:SPCustomQueryEditorHighlightQueryColor]] - range: qRange ]; - hasBackgroundAttribute = YES; - } + if(qRange.length) currentQueryRange = qRange; - - } else { + else currentQueryRange = NSMakeRange(0, 0); - } - // disable "Comment Current Query" meun item if no current query is selectable + [textView setQueryRange:qRange]; + [textView setNeedsDisplay:YES]; + + // disable "Comment Current Query" menu item if no current query is selectable [commentCurrentQueryMenuItem setEnabled:(currentQueryRange.length) ? YES : NO]; // If no text is selected, disable the button and action menu. @@ -3026,8 +2996,9 @@ resultDataCount = 0; resultData = [[SPDataStorage alloc] init]; editedRow = -1; - + prefs = [NSUserDefaults standardUserDefaults]; + } return self; |