mirror of
https://github.com/macvim-dev/macvim.git
synced 2026-06-11 15:37:29 +02:00
New text drawing model, better unicode support, 'gfw' support, etc.
Characters and columns are no longer assumed to be in one-to-one
correspondence. Thus when modifying the text storage a (row,column) pair must
be searched for. This is managed using
[NSString rangeOfComposedCharacterSequenceAtIndex:]
and by marking wide characters with an attribute (called "MMWideChar"). We
also provide an optimized code path for when characters and columns are in
one-to-one correspondence (no wide chars, only chars that can be represented by
one utf16 character).
A new typesetter is also used. Instead of overriding
layoutGlyphsInLayoutManager:::: we override willSetLineFragmentRect::::. This
typesetter can handle composing characters, whereas the old typesetter couldn't
(it can still be used by setting the user default "MMTypesetter" to
"MMTypesetter2".)
Note that text rendering still has flaws, the Cocoa Text System and Vim has
different opinions on how much space certain characters take up and this
results in display bugs. (E.g. nonspacing marks such as U+064C are
problematic.) Also, sometimes the layout manager hides glyphs but Vim assumes
that each character is displayed (resulting in yet more display bugs).
Added support for 'guifontwide'. This can be set to anything (different font
size); it is quite possible to set it so that the display becomes completely
messed up.
This commit is contained in:
@@ -7,6 +7,24 @@
|
||||
* Do ":help credits" in Vim to see a list of people who contributed.
|
||||
* See README.txt for an overview of the Vim source code.
|
||||
*/
|
||||
/*
|
||||
* MMAppController
|
||||
*
|
||||
* MMAppController is the delegate of NSApp and as such handles file open
|
||||
* requests, application termination, etc. It sets up a named NSConnection on
|
||||
* which it listens to incoming connections from Vim processes. It also
|
||||
* coordinates all MMVimControllers.
|
||||
*
|
||||
* A new Vim process is started by calling launchVimProcessWithArguments:.
|
||||
* When the Vim process is initialized it notifies the app controller by
|
||||
* sending a connectBackend:pid: message. At this point a new MMVimController
|
||||
* is allocated. Afterwards, the Vim process communicates directly with its
|
||||
* MMVimController.
|
||||
*
|
||||
* A Vim process started from the command line connects directly by sending the
|
||||
* connectBackend:pid: message (launchVimProcessWithArguments: is never called
|
||||
* in this case).
|
||||
*/
|
||||
|
||||
#import "MMAppController.h"
|
||||
#import "MMVimController.h"
|
||||
@@ -63,6 +81,7 @@ static NSTimeInterval MMReplyTimeout = 5;
|
||||
[NSNumber numberWithFloat:-1], MMBaselineOffsetKey,
|
||||
[NSNumber numberWithBool:YES], MMTranslateCtrlClickKey,
|
||||
[NSNumber numberWithBool:NO], MMOpenFilesInTabsKey,
|
||||
[NSNumber numberWithBool:NO], MMNoFontSubstitutionKey,
|
||||
nil];
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:dict];
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
* Do ":help credits" in Vim to see a list of people who contributed.
|
||||
* See README.txt for an overview of the Vim source code.
|
||||
*/
|
||||
/*
|
||||
* MMApplication
|
||||
*
|
||||
* Some default NSApplication key input behavior is overridden here.
|
||||
*/
|
||||
|
||||
#import "MMApplication.h"
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
NSMutableDictionary *serverReplyDict;
|
||||
NSString *alternateServerName;
|
||||
ATSFontContainerRef fontContainerRef;
|
||||
NSFont *oldWideFont;
|
||||
}
|
||||
|
||||
+ (MMBackend *)sharedInstance;
|
||||
@@ -60,8 +61,8 @@
|
||||
toRow:(int)row2 column:(int)col2;
|
||||
- (void)deleteLinesFromRow:(int)row count:(int)count
|
||||
scrollBottom:(int)bottom left:(int)left right:(int)right;
|
||||
- (void)replaceString:(char*)s length:(int)len row:(int)row column:(int)col
|
||||
flags:(int)flags;
|
||||
- (void)drawString:(char*)s length:(int)len row:(int)row column:(int)col
|
||||
cells:(int)cells flags:(int)flags;
|
||||
- (void)insertLinesFromRow:(int)row count:(int)count
|
||||
scrollBottom:(int)bottom left:(int)left right:(int)right;
|
||||
- (void)drawCursorAtRow:(int)row column:(int)col shape:(int)shape
|
||||
@@ -96,7 +97,8 @@
|
||||
- (void)setScrollbarPosition:(int)pos length:(int)len identifier:(long)ident;
|
||||
- (void)setScrollbarThumbValue:(long)val size:(long)size max:(long)max
|
||||
identifier:(long)ident;
|
||||
- (BOOL)setFontWithName:(char *)name;
|
||||
- (void)setFont:(NSFont *)font;
|
||||
- (void)setWideFont:(NSFont *)font;
|
||||
- (void)executeActionWithName:(NSString *)name;
|
||||
- (void)setMouseShape:(int)shape;
|
||||
- (void)setBlinkWait:(int)wait on:(int)on off:(int)off;
|
||||
|
||||
+54
-76
@@ -7,6 +7,25 @@
|
||||
* Do ":help credits" in Vim to see a list of people who contributed.
|
||||
* See README.txt for an overview of the Vim source code.
|
||||
*/
|
||||
/*
|
||||
* MMBackend
|
||||
*
|
||||
* MMBackend communicates with the frontend (MacVim). It maintains a queue of
|
||||
* output which is flushed to the frontend under controlled circumstances (so
|
||||
* as to maintain a steady framerate). Input from the frontend is also handled
|
||||
* here.
|
||||
*
|
||||
* The frontend communicates with the backend via the MMBackendProtocol. In
|
||||
* particular, input is sent to the backend via processInput:data: and Vim
|
||||
* state can be queried from the frontend with evaluateExpression:.
|
||||
*
|
||||
* It is very important to realize that all state is held by the backend, the
|
||||
* frontend must either ask for state [MMBackend evaluateExpression:] or wait
|
||||
* for the backend to update [MMVimController processCommandQueue:].
|
||||
*
|
||||
* The client/server functionality of Vim is handled by the backend. It sets
|
||||
* up a named NSConnection to which other Vim processes can connect.
|
||||
*/
|
||||
|
||||
#import "MMBackend.h"
|
||||
|
||||
@@ -30,10 +49,6 @@ static int MMFlushQueueLenHint = 80*40;
|
||||
|
||||
static unsigned MMServerMax = 1000;
|
||||
|
||||
// NOTE: The default font is bundled with the application.
|
||||
static NSString *MMDefaultFontName = @"DejaVu Sans Mono";
|
||||
static float MMDefaultFontSize = 12.0f;
|
||||
|
||||
// TODO: Move to separate file.
|
||||
static int eventModifierFlagsToVimModMask(int modifierFlags);
|
||||
static int vimModMaskToEventModifierFlags(int mods);
|
||||
@@ -134,6 +149,7 @@ enum {
|
||||
//NSLog(@"%@ %s", [self className], _cmd);
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
|
||||
[oldWideFont release]; oldWideFont = nil;
|
||||
[blinkTimer release]; blinkTimer = nil;
|
||||
[alternateServerName release]; alternateServerName = nil;
|
||||
[serverReplyDict release]; serverReplyDict = nil;
|
||||
@@ -319,12 +335,12 @@ enum {
|
||||
[drawData appendBytes:&right length:sizeof(int)];
|
||||
}
|
||||
|
||||
- (void)replaceString:(char*)s length:(int)len row:(int)row column:(int)col
|
||||
flags:(int)flags
|
||||
- (void)drawString:(char*)s length:(int)len row:(int)row column:(int)col
|
||||
cells:(int)cells flags:(int)flags
|
||||
{
|
||||
if (len <= 0) return;
|
||||
if (len <= 0 || cells <= 0) return;
|
||||
|
||||
int type = ReplaceStringDrawType;
|
||||
int type = DrawStringDrawType;
|
||||
|
||||
[drawData appendBytes:&type length:sizeof(int)];
|
||||
|
||||
@@ -333,6 +349,7 @@ enum {
|
||||
[drawData appendBytes:&specialColor length:sizeof(unsigned)];
|
||||
[drawData appendBytes:&row length:sizeof(int)];
|
||||
[drawData appendBytes:&col length:sizeof(int)];
|
||||
[drawData appendBytes:&cells length:sizeof(int)];
|
||||
[drawData appendBytes:&flags length:sizeof(int)];
|
||||
[drawData appendBytes:&len length:sizeof(int)];
|
||||
[drawData appendBytes:s length:len];
|
||||
@@ -392,6 +409,13 @@ enum {
|
||||
return;
|
||||
|
||||
if ([drawData length] > 0) {
|
||||
// HACK! Detect changes to 'guifontwide'.
|
||||
if (gui.wide_font != (GuiFont)oldWideFont) {
|
||||
[oldWideFont release];
|
||||
oldWideFont = [(NSFont*)gui.wide_font retain];
|
||||
[self setWideFont:oldWideFont];
|
||||
}
|
||||
|
||||
[self queueMessage:BatchDrawMsgID data:[drawData copy]];
|
||||
[drawData setLength:0];
|
||||
}
|
||||
@@ -818,81 +842,35 @@ enum {
|
||||
[self queueMessage:SetScrollbarThumbMsgID data:data];
|
||||
}
|
||||
|
||||
- (BOOL)setFontWithName:(char *)name
|
||||
- (void)setFont:(NSFont *)font
|
||||
{
|
||||
NSString *fontName = MMDefaultFontName;
|
||||
float size = MMDefaultFontSize;
|
||||
BOOL parseFailed = NO;
|
||||
NSString *fontName = [font displayName];
|
||||
float size = [font pointSize];
|
||||
int len = [fontName lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
|
||||
if (len > 0) {
|
||||
NSMutableData *data = [NSMutableData data];
|
||||
|
||||
if (name) {
|
||||
fontName = [NSString stringWithUTF8String:name];
|
||||
[data appendBytes:&size length:sizeof(float)];
|
||||
[data appendBytes:&len length:sizeof(int)];
|
||||
[data appendBytes:[fontName UTF8String] length:len];
|
||||
|
||||
if ([fontName isEqual:@"*"]) {
|
||||
// :set gfn=* shows the font panel.
|
||||
do_cmdline_cmd((char_u*)":macaction orderFrontFontPanel:");
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSArray *components = [fontName componentsSeparatedByString:@":"];
|
||||
if ([components count] == 2) {
|
||||
NSString *sizeString = [components lastObject];
|
||||
if ([sizeString length] > 0
|
||||
&& [sizeString characterAtIndex:0] == 'h') {
|
||||
sizeString = [sizeString substringFromIndex:1];
|
||||
if ([sizeString length] > 0) {
|
||||
size = [sizeString floatValue];
|
||||
fontName = [components objectAtIndex:0];
|
||||
}
|
||||
} else {
|
||||
parseFailed = YES;
|
||||
}
|
||||
} else if ([components count] > 2) {
|
||||
parseFailed = YES;
|
||||
}
|
||||
|
||||
if (!parseFailed) {
|
||||
// Replace underscores with spaces.
|
||||
fontName = [[fontName componentsSeparatedByString:@"_"]
|
||||
componentsJoinedByString:@" "];
|
||||
}
|
||||
[self queueMessage:SetFontMsgID data:data];
|
||||
}
|
||||
}
|
||||
|
||||
if (!parseFailed && [fontName length] > 0) {
|
||||
if (size < 6 || size > 100) {
|
||||
// Font size 0.0 tells NSFont to use the 'user default size'.
|
||||
size = 0.0f;
|
||||
}
|
||||
- (void)setWideFont:(NSFont *)font
|
||||
{
|
||||
NSString *fontName = [font displayName];
|
||||
float size = [font pointSize];
|
||||
int len = [fontName lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
|
||||
NSMutableData *data = [NSMutableData data];
|
||||
|
||||
NSFont *font = [NSFont fontWithName:fontName size:size];
|
||||
[data appendBytes:&size length:sizeof(float)];
|
||||
[data appendBytes:&len length:sizeof(int)];
|
||||
if (len > 0)
|
||||
[data appendBytes:[fontName UTF8String] length:len];
|
||||
|
||||
if (!font && MMDefaultFontName == fontName) {
|
||||
// If for some reason the MacVim default font is not in the app
|
||||
// bundle, then fall back on the system default font.
|
||||
size = 0;
|
||||
font = [NSFont userFixedPitchFontOfSize:size];
|
||||
fontName = [font displayName];
|
||||
}
|
||||
|
||||
if (font) {
|
||||
//NSLog(@"Setting font '%@' of size %.2f", fontName, size);
|
||||
int len = [fontName
|
||||
lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
|
||||
if (len > 0) {
|
||||
NSMutableData *data = [NSMutableData data];
|
||||
|
||||
[data appendBytes:&size length:sizeof(float)];
|
||||
[data appendBytes:&len length:sizeof(int)];
|
||||
[data appendBytes:[fontName UTF8String] length:len];
|
||||
|
||||
[self queueMessage:SetFontMsgID data:data];
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//NSLog(@"WARNING: Cannot set font with name '%@' of size %.2f",
|
||||
// fontName, size);
|
||||
return NO;
|
||||
[self queueMessage:SetWideFontMsgID data:data];
|
||||
}
|
||||
|
||||
- (void)executeActionWithName:(NSString *)name
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
* Do ":help credits" in Vim to see a list of people who contributed.
|
||||
* See README.txt for an overview of the Vim source code.
|
||||
*/
|
||||
/*
|
||||
* MMFullscreen
|
||||
*
|
||||
* Support for full-screen editing.
|
||||
*/
|
||||
|
||||
#import "MMFullscreenWindow.h"
|
||||
#import <PSMTabBarControl.h>
|
||||
|
||||
@@ -11,6 +11,17 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
#define MM_USE_ROW_CACHE 1
|
||||
|
||||
|
||||
#if MM_USE_ROW_CACHE
|
||||
typedef struct {
|
||||
unsigned length; // length of row in unichars
|
||||
int col; // last column accessed (in this row)
|
||||
unsigned colOffset; // offset of 'col' from start of row (in unichars)
|
||||
} MMRowCacheEntry;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@interface MMTextStorage : NSTextStorage {
|
||||
@@ -22,15 +33,25 @@
|
||||
NSFont *boldFont;
|
||||
NSFont *italicFont;
|
||||
NSFont *boldItalicFont;
|
||||
NSFont *fontWide;
|
||||
NSFont *boldFontWide;
|
||||
NSFont *italicFontWide;
|
||||
NSFont *boldItalicFontWide;
|
||||
NSColor *defaultBackgroundColor;
|
||||
NSColor *defaultForegroundColor;
|
||||
NSSize cellSize;
|
||||
float linespace;
|
||||
#if MM_USE_ROW_CACHE
|
||||
MMRowCacheEntry *rowCache;
|
||||
#endif
|
||||
BOOL characterEqualsColumn;
|
||||
}
|
||||
|
||||
- (NSString *)string;
|
||||
- (NSDictionary *)attributesAtIndex:(unsigned)index
|
||||
effectiveRange:(NSRangePointer)aRange;
|
||||
- (id)attribute:(NSString *)attrib atIndex:(unsigned)index
|
||||
effectiveRange:(NSRangePointer)range;
|
||||
- (void)replaceCharactersInRange:(NSRange)aRange
|
||||
withString:(NSString *)aString;
|
||||
- (void)setAttributes:(NSDictionary *)attributes range:(NSRange)aRange;
|
||||
@@ -43,9 +64,10 @@
|
||||
- (void)setLinespace:(float)newLinespace;
|
||||
- (void)getMaxRows:(int*)rows columns:(int*)cols;
|
||||
- (void)setMaxRows:(int)rows columns:(int)cols;
|
||||
- (void)replaceString:(NSString *)string atRow:(int)row column:(int)col
|
||||
withFlags:(int)flags foregroundColor:(NSColor *)fg
|
||||
backgroundColor:(NSColor *)bg specialColor:(NSColor *)sp;
|
||||
- (void)drawString:(NSString *)string atRow:(int)row column:(int)col
|
||||
cells:(int)cells withFlags:(int)flags
|
||||
foregroundColor:(NSColor *)fg backgroundColor:(NSColor *)bg
|
||||
specialColor:(NSColor *)sp;
|
||||
- (void)deleteLinesFromRow:(int)row lineCount:(int)count
|
||||
scrollBottom:(int)bottom left:(int)left right:(int)right
|
||||
color:(NSColor *)color;
|
||||
@@ -58,6 +80,7 @@
|
||||
- (void)setDefaultColorsBackground:(NSColor *)bgColor
|
||||
foreground:(NSColor *)fgColor;
|
||||
- (void)setFont:(NSFont *)newFont;
|
||||
- (void)setWideFont:(NSFont *)newFont;
|
||||
- (NSFont *)font;
|
||||
- (NSColor *)defaultBackgroundColor;
|
||||
- (NSColor *)defaultForegroundColor;
|
||||
@@ -69,5 +92,9 @@
|
||||
- (BOOL)resizeToFitSize:(NSSize)size;
|
||||
- (NSSize)fitToSize:(NSSize)size;
|
||||
- (NSSize)fitToSize:(NSSize)size rows:(int *)rows columns:(int *)columns;
|
||||
- (NSRect)boundingRectForCharacterAtRow:(int)row column:(int)col;
|
||||
#if MM_USE_ROW_CACHE
|
||||
- (MMRowCacheEntry *)rowCache;
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
+455
-187
@@ -7,6 +7,11 @@
|
||||
* Do ":help credits" in Vim to see a list of people who contributed.
|
||||
* See README.txt for an overview of the Vim source code.
|
||||
*/
|
||||
/*
|
||||
* MMTextStorage
|
||||
*
|
||||
* Text rendering related code.
|
||||
*/
|
||||
|
||||
#import "MMTextStorage.h"
|
||||
#import "MacVim.h"
|
||||
@@ -22,12 +27,18 @@
|
||||
#define DRAW_UNDERL 0x04 /* draw underline text */
|
||||
#define DRAW_UNDERC 0x08 /* draw undercurl text */
|
||||
#define DRAW_ITALIC 0x10 /* draw italic text */
|
||||
#define DRAW_CURSOR 0x20
|
||||
|
||||
|
||||
static NSString *MMWideCharacterAttributeName = @"MMWideChar";
|
||||
|
||||
|
||||
|
||||
|
||||
@interface MMTextStorage (Private)
|
||||
- (void)lazyResize:(BOOL)force;
|
||||
- (NSRange)charRangeForRow:(int)row column:(int)col cells:(int)cells;
|
||||
- (void)fixInvalidCharactersInRange:(NSRange)range;
|
||||
@end
|
||||
|
||||
|
||||
@@ -41,9 +52,6 @@
|
||||
// NOTE! It does not matter which font is set here, Vim will set its
|
||||
// own font on startup anyway. Just set some bogus values.
|
||||
font = [[NSFont userFixedPitchFontOfSize:0] retain];
|
||||
boldFont = [font retain];
|
||||
italicFont = [font retain];
|
||||
boldItalicFont = [font retain];
|
||||
cellSize.height = [font pointSize];
|
||||
cellSize.width = [font defaultLineHeightForFont];
|
||||
}
|
||||
@@ -53,9 +61,17 @@
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
//NSLog(@"%@ %s", [self className], _cmd);
|
||||
|
||||
#if MM_USE_ROW_CACHE
|
||||
if (rowCache) {
|
||||
free(rowCache);
|
||||
rowCache = NULL;
|
||||
}
|
||||
#endif
|
||||
[emptyRowString release];
|
||||
[boldItalicFontWide release];
|
||||
[italicFontWide release];
|
||||
[boldFontWide release];
|
||||
[fontWide release];
|
||||
[boldItalicFont release];
|
||||
[italicFont release];
|
||||
[boldFont release];
|
||||
@@ -68,30 +84,31 @@
|
||||
|
||||
- (NSString *)string
|
||||
{
|
||||
//NSLog(@"%s : attribString=%@", _cmd, attribString);
|
||||
return [attribString string];
|
||||
}
|
||||
|
||||
- (NSDictionary *)attributesAtIndex:(unsigned)index
|
||||
effectiveRange:(NSRangePointer)range
|
||||
{
|
||||
//NSLog(@"%s", _cmd);
|
||||
if (index>=[attribString length]) {
|
||||
//NSLog(@"%sWARNING: index (%d) out of bounds", _cmd, index);
|
||||
if (range) {
|
||||
if (index >= [attribString length]) {
|
||||
if (range)
|
||||
*range = NSMakeRange(NSNotFound, 0);
|
||||
}
|
||||
|
||||
return [NSDictionary dictionary];
|
||||
}
|
||||
|
||||
return [attribString attributesAtIndex:index effectiveRange:range];
|
||||
}
|
||||
|
||||
- (id)attribute:(NSString *)attrib atIndex:(unsigned)index
|
||||
effectiveRange:(NSRangePointer)range
|
||||
{
|
||||
return [attribString attribute:attrib atIndex:index effectiveRange:range];
|
||||
}
|
||||
|
||||
- (void)replaceCharactersInRange:(NSRange)range
|
||||
withString:(NSString *)string
|
||||
{
|
||||
//NSLog(@"replaceCharactersInRange:(%d,%d) withString:%@", range.location,
|
||||
// range.length, string);
|
||||
NSLog(@"WARNING: calling %s on MMTextStorage is unsupported", _cmd);
|
||||
//[attribString replaceCharactersInRange:range withString:string];
|
||||
}
|
||||
@@ -102,22 +119,28 @@
|
||||
// constantly to 'fix attributes', apply font substitution, etc.
|
||||
#if 0
|
||||
[attribString setAttributes:attributes range:range];
|
||||
#else
|
||||
#elif 1
|
||||
// HACK! If the font attribute is being modified, then ensure that the new
|
||||
// font has a fixed advancement which is either the same as the current
|
||||
// font or twice that, depending on whether it is a 'wide' character that
|
||||
// is being fixed or not. This code really only works if 'range' has
|
||||
// length 1 or 2.
|
||||
// is being fixed or not.
|
||||
//
|
||||
// TODO: This code assumes that the characters in 'range' all have the same
|
||||
// width.
|
||||
NSFont *newFont = [attributes objectForKey:NSFontAttributeName];
|
||||
if (newFont) {
|
||||
// Allow disabling of font substitution via a user default. Not
|
||||
// recommended since the typesetter hides the corresponding glyphs and
|
||||
// the display gets messed up.
|
||||
if ([[NSUserDefaults standardUserDefaults]
|
||||
boolForKey:MMNoFontSubstitutionKey])
|
||||
return;
|
||||
|
||||
float adv = cellSize.width;
|
||||
if ([attribString length] > range.location+1) {
|
||||
// If the first char is followed by zero-width space, then it is a
|
||||
// 'wide' character, so double the advancement.
|
||||
NSString *string = [attribString string];
|
||||
if ([string characterAtIndex:range.location+1] == 0x200b)
|
||||
adv += adv;
|
||||
}
|
||||
if ([attribString attribute:MMWideCharacterAttributeName
|
||||
atIndex:range.location
|
||||
effectiveRange:NULL])
|
||||
adv += adv;
|
||||
|
||||
// Create a new font which has the 'fixed advance attribute' set.
|
||||
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@@ -192,9 +215,10 @@
|
||||
maxColumns = cols;
|
||||
}
|
||||
|
||||
- (void)replaceString:(NSString *)string atRow:(int)row column:(int)col
|
||||
withFlags:(int)flags foregroundColor:(NSColor *)fg
|
||||
backgroundColor:(NSColor *)bg specialColor:(NSColor *)sp
|
||||
- (void)drawString:(NSString *)string atRow:(int)row column:(int)col
|
||||
cells:(int)cells withFlags:(int)flags
|
||||
foregroundColor:(NSColor *)fg backgroundColor:(NSColor *)bg
|
||||
specialColor:(NSColor *)sp
|
||||
{
|
||||
//NSLog(@"replaceString:atRow:%d column:%d withFlags:%d "
|
||||
// "foreground:%@ background:%@ special:%@",
|
||||
@@ -202,59 +226,75 @@
|
||||
[self lazyResize:NO];
|
||||
|
||||
if (row < 0 || row >= maxRows || col < 0 || col >= maxColumns
|
||||
|| col+[string length] > maxColumns) {
|
||||
//NSLog(@"[%s] WARNING : out of range, row=%d (%d) col=%d (%d) "
|
||||
// "length=%d (%d)", _cmd, row, maxRows, col, maxColumns,
|
||||
// [string length], [attribString length]);
|
||||
|| col+cells > maxColumns || !string || !(fg && bg && sp))
|
||||
return;
|
||||
|
||||
// Find range of characters in text storage to replace.
|
||||
NSRange range = [self charRangeForRow:row column:col cells:cells];
|
||||
if (NSMaxRange(range) > [[attribString string] length]) {
|
||||
NSLog(@"%s Out of bounds");
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: If 'string' was initialized with bad data it might be nil; this
|
||||
// may be due to 'enc' being set to an unsupported value, so don't print an
|
||||
// error message or stdout will most likely get flooded.
|
||||
if (!string) return;
|
||||
|
||||
if (!(fg && bg && sp)) {
|
||||
NSLog(@"[%s] WARNING: background, foreground or special color not "
|
||||
"specified", _cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
NSRange range = NSMakeRange(col+row*(maxColumns+1), [string length]);
|
||||
[attribString replaceCharactersInRange:range withString:string];
|
||||
|
||||
// Create dictionary of attributes to apply to the new characters.
|
||||
NSFont *theFont = font;
|
||||
if (flags & DRAW_BOLD)
|
||||
theFont = flags & DRAW_ITALIC ? boldItalicFont : boldFont;
|
||||
else if (flags & DRAW_ITALIC)
|
||||
theFont = italicFont;
|
||||
if (flags & DRAW_WIDE) {
|
||||
if (flags & DRAW_BOLD)
|
||||
theFont = flags & DRAW_ITALIC ? boldItalicFontWide : boldFontWide;
|
||||
else if (flags & DRAW_ITALIC)
|
||||
theFont = italicFontWide;
|
||||
else
|
||||
theFont = fontWide;
|
||||
} else {
|
||||
if (flags & DRAW_BOLD)
|
||||
theFont = flags & DRAW_ITALIC ? boldItalicFont : boldFont;
|
||||
else if (flags & DRAW_ITALIC)
|
||||
theFont = italicFont;
|
||||
}
|
||||
|
||||
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
NSMutableDictionary *attributes =
|
||||
[NSMutableDictionary dictionaryWithObjectsAndKeys:
|
||||
theFont, NSFontAttributeName,
|
||||
bg, NSBackgroundColorAttributeName,
|
||||
fg, NSForegroundColorAttributeName,
|
||||
sp, NSUnderlineColorAttributeName,
|
||||
nil];
|
||||
|
||||
[attribString setAttributes:attributes range:range];
|
||||
|
||||
if (flags & DRAW_UNDERL) {
|
||||
NSNumber *value = [NSNumber numberWithInt:(NSUnderlineStyleSingle
|
||||
| NSUnderlinePatternSolid)]; // | NSUnderlineByWordMask
|
||||
[attribString addAttribute:NSUnderlineStyleAttributeName
|
||||
value:value range:range];
|
||||
[attributes setObject:value forKey:NSUnderlineStyleAttributeName];
|
||||
}
|
||||
|
||||
// TODO: figure out how do draw proper undercurls
|
||||
if (flags & DRAW_UNDERC) {
|
||||
// TODO: figure out how do draw proper undercurls
|
||||
NSNumber *value = [NSNumber numberWithInt:(NSUnderlineStyleThick
|
||||
| NSUnderlinePatternDot)]; // | NSUnderlineByWordMask
|
||||
[attribString addAttribute:NSUnderlineStyleAttributeName
|
||||
value:value range:range];
|
||||
[attributes setObject:value forKey:NSUnderlineStyleAttributeName];
|
||||
}
|
||||
|
||||
// Mark these characters as wide. This attribute is subsequently checked
|
||||
// when translating (row,col) pairs to offsets within 'attribString'.
|
||||
if (flags & DRAW_WIDE)
|
||||
[attributes setObject:[NSNull null]
|
||||
forKey:MMWideCharacterAttributeName];
|
||||
|
||||
// Replace characters in text storage and apply new attributes.
|
||||
NSRange r = NSMakeRange(range.location, [string length]);
|
||||
[attribString replaceCharactersInRange:range withString:string];
|
||||
[attribString setAttributes:attributes range:r];
|
||||
|
||||
if ((flags & DRAW_WIDE) || [string length] != cells)
|
||||
characterEqualsColumn = NO;
|
||||
|
||||
[self fixInvalidCharactersInRange:r];
|
||||
|
||||
[self edited:(NSTextStorageEditedCharacters|NSTextStorageEditedAttributes)
|
||||
range:range changeInLength:0];
|
||||
range:range changeInLength:[string length]-range.length];
|
||||
|
||||
#if MM_USE_ROW_CACHE
|
||||
rowCache[row].length += [string length] - range.length;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -268,67 +308,56 @@
|
||||
//NSLog(@"deleteLinesFromRow:%d lineCount:%d color:%@", row, count, color);
|
||||
[self lazyResize:NO];
|
||||
|
||||
if (row < 0 || row+count > maxRows) {
|
||||
//NSLog(@"[%s] WARNING : out of range, row=%d (%d) count=%d", _cmd, row,
|
||||
// maxRows, count);
|
||||
if (row < 0 || row+count > maxRows)
|
||||
return;
|
||||
}
|
||||
|
||||
int total = 1 + bottom - row;
|
||||
int move = total - count;
|
||||
int width = right - left + 1;
|
||||
NSRange destRange = { row*(maxColumns+1) + left, width };
|
||||
NSRange srcRange = { (row+count)*(maxColumns+1) + left, width };
|
||||
int destRow = row;
|
||||
NSRange destRange, srcRange;
|
||||
int i;
|
||||
|
||||
if (width != maxColumns) { // if this is the case, then left must be 0
|
||||
for (i = 0; i < move; ++i) {
|
||||
NSAttributedString *srcString = [attribString
|
||||
attributedSubstringFromRange:srcRange];
|
||||
[attribString replaceCharactersInRange:destRange
|
||||
withAttributedString:srcString];
|
||||
[self edited:(NSTextStorageEditedCharacters
|
||||
| NSTextStorageEditedAttributes)
|
||||
range:destRange changeInLength:0];
|
||||
destRange.location += maxColumns+1;
|
||||
srcRange.location += maxColumns+1;
|
||||
}
|
||||
|
||||
NSRange emptyRange = {0,width};
|
||||
NSAttributedString *emptyString =
|
||||
[emptyRowString attributedSubstringFromRange: emptyRange];
|
||||
NSDictionary *attribs = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
font, NSFontAttributeName,
|
||||
color, NSBackgroundColorAttributeName, nil];
|
||||
for (i = 0; i < move; ++i, ++destRow) {
|
||||
destRange = [self charRangeForRow:destRow column:left cells:width];
|
||||
srcRange = [self charRangeForRow:(destRow+count) column:left
|
||||
cells:width];
|
||||
NSAttributedString *srcString = [attribString
|
||||
attributedSubstringFromRange:srcRange];
|
||||
|
||||
[attribString replaceCharactersInRange:destRange
|
||||
withAttributedString:srcString];
|
||||
[self edited:(NSTextStorageEditedCharacters
|
||||
| NSTextStorageEditedAttributes) range:destRange
|
||||
changeInLength:([srcString length]-destRange.length)];
|
||||
|
||||
#if MM_USE_ROW_CACHE
|
||||
rowCache[destRow].length += [srcString length] - destRange.length;
|
||||
#endif
|
||||
}
|
||||
|
||||
NSRange emptyRange = {0,width};
|
||||
NSAttributedString *emptyString =
|
||||
[emptyRowString attributedSubstringFromRange:emptyRange];
|
||||
NSDictionary *attribs = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
font, NSFontAttributeName,
|
||||
color, NSBackgroundColorAttributeName, nil];
|
||||
|
||||
for (i = 0; i < count; ++i, ++destRow) {
|
||||
destRange = [self charRangeForRow:destRow column:left cells:width];
|
||||
|
||||
[attribString replaceCharactersInRange:destRange
|
||||
withAttributedString:emptyString];
|
||||
[attribString setAttributes:attribs
|
||||
range:NSMakeRange(destRange.location, width)];
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
[attribString replaceCharactersInRange:destRange
|
||||
withAttributedString:emptyString];
|
||||
[attribString setAttributes:attribs range:destRange];
|
||||
[self edited:(NSTextStorageEditedAttributes
|
||||
| NSTextStorageEditedCharacters) range:destRange
|
||||
changeInLength:0];
|
||||
destRange.location += maxColumns+1;
|
||||
}
|
||||
} else {
|
||||
NSRange delRange = {row*(maxColumns+1), count*(maxColumns+1)};
|
||||
[attribString deleteCharactersInRange: delRange];
|
||||
destRange.location += move*(maxColumns+1);
|
||||
|
||||
NSDictionary *attribs = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
font, NSFontAttributeName,
|
||||
color, NSBackgroundColorAttributeName, nil];
|
||||
destRange.length = maxColumns;
|
||||
for (i = 0; i < count; ++i) {
|
||||
[attribString insertAttributedString:emptyRowString
|
||||
atIndex:destRange.location];
|
||||
[attribString setAttributes:attribs range:destRange];
|
||||
destRange.location += maxColumns+1;
|
||||
}
|
||||
NSRange editedRange = {row*(maxColumns+1),total*(maxColumns+1)};
|
||||
[self edited:(NSTextStorageEditedAttributes
|
||||
| NSTextStorageEditedCharacters) range:editedRange
|
||||
changeInLength:0];
|
||||
| NSTextStorageEditedCharacters) range:destRange
|
||||
changeInLength:([emptyString length]-destRange.length)];
|
||||
|
||||
#if MM_USE_ROW_CACHE
|
||||
rowCache[destRow].length += [emptyString length] - destRange.length;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,66 +372,55 @@
|
||||
//NSLog(@"insertLinesAtRow:%d lineCount:%d color:%@", row, count, color);
|
||||
[self lazyResize:NO];
|
||||
|
||||
if (row < 0 || row+count > maxRows) {
|
||||
//NSLog(@"[%s] WARNING : out of range, row=%d (%d) count=%d", _cmd, row,
|
||||
// maxRows, count);
|
||||
if (row < 0 || row+count > maxRows)
|
||||
return;
|
||||
}
|
||||
|
||||
int total = 1 + bottom - row;
|
||||
int move = total - count;
|
||||
int width = right - left + 1;
|
||||
NSRange destRange = { bottom*(maxColumns+1) + left, width };
|
||||
NSRange srcRange = { (row+move-1)*(maxColumns+1) + left, width };
|
||||
int destRow = bottom;
|
||||
int srcRow = row + move - 1;
|
||||
NSRange destRange, srcRange;
|
||||
int i;
|
||||
|
||||
if (width != maxColumns) { // if this is the case, then left must be 0
|
||||
for (i = 0; i < move; ++i) {
|
||||
NSAttributedString *srcString = [attribString
|
||||
attributedSubstringFromRange:srcRange];
|
||||
[attribString replaceCharactersInRange:destRange
|
||||
withAttributedString:srcString];
|
||||
[self edited:(NSTextStorageEditedCharacters
|
||||
| NSTextStorageEditedAttributes)
|
||||
range:destRange changeInLength:0];
|
||||
destRange.location -= maxColumns+1;
|
||||
srcRange.location -= maxColumns+1;
|
||||
}
|
||||
|
||||
NSRange emptyRange = {0,width};
|
||||
NSAttributedString *emptyString =
|
||||
[emptyRowString attributedSubstringFromRange:emptyRange];
|
||||
NSDictionary *attribs = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
font, NSFontAttributeName,
|
||||
color, NSBackgroundColorAttributeName, nil];
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
[attribString replaceCharactersInRange:destRange
|
||||
withAttributedString:emptyString];
|
||||
[attribString setAttributes:attribs range:destRange];
|
||||
[self edited:(NSTextStorageEditedAttributes
|
||||
| NSTextStorageEditedCharacters) range:destRange
|
||||
changeInLength:0];
|
||||
destRange.location -= maxColumns+1;
|
||||
}
|
||||
} else {
|
||||
NSRange delRange = {(row+move)*(maxColumns+1),count*(maxColumns+1)};
|
||||
[attribString deleteCharactersInRange: delRange];
|
||||
for (i = 0; i < move; ++i, --destRow, --srcRow) {
|
||||
destRange = [self charRangeForRow:destRow column:left cells:width];
|
||||
srcRange = [self charRangeForRow:srcRow column:left cells:width];
|
||||
NSAttributedString *srcString = [attribString
|
||||
attributedSubstringFromRange:srcRange];
|
||||
[attribString replaceCharactersInRange:destRange
|
||||
withAttributedString:srcString];
|
||||
[self edited:(NSTextStorageEditedCharacters
|
||||
| NSTextStorageEditedAttributes) range:destRange
|
||||
changeInLength:([srcString length]-destRange.length)];
|
||||
|
||||
#if MM_USE_ROW_CACHE
|
||||
rowCache[destRow].length += [srcString length] - destRange.length;
|
||||
#endif
|
||||
}
|
||||
|
||||
NSRange emptyRange = {0,width};
|
||||
NSAttributedString *emptyString =
|
||||
[emptyRowString attributedSubstringFromRange:emptyRange];
|
||||
NSDictionary *attribs = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
font, NSFontAttributeName,
|
||||
color, NSBackgroundColorAttributeName, nil];
|
||||
|
||||
for (i = 0; i < count; ++i, --destRow) {
|
||||
destRange = [self charRangeForRow:destRow column:left cells:width];
|
||||
|
||||
[attribString replaceCharactersInRange:destRange
|
||||
withAttributedString:emptyString];
|
||||
[attribString setAttributes:attribs
|
||||
range:NSMakeRange(destRange.location, width)];
|
||||
|
||||
NSDictionary *attribs = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
font, NSFontAttributeName,
|
||||
color, NSBackgroundColorAttributeName, nil];
|
||||
|
||||
destRange.location = row*(maxColumns+1);
|
||||
for (i = 0; i < count; ++i) {
|
||||
[attribString insertAttributedString:emptyRowString
|
||||
atIndex:destRange.location];
|
||||
[attribString setAttributes:attribs range:destRange];
|
||||
}
|
||||
NSRange editedRange = {row*(maxColumns+1),total*(maxColumns+1)};
|
||||
[self edited:(NSTextStorageEditedAttributes
|
||||
| NSTextStorageEditedCharacters) range:editedRange
|
||||
changeInLength:0];
|
||||
| NSTextStorageEditedCharacters) range:destRange
|
||||
changeInLength:([emptyString length]-destRange.length)];
|
||||
|
||||
#if MM_USE_ROW_CACHE
|
||||
rowCache[destRow].length += [emptyString length] - destRange.length;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,45 +431,45 @@
|
||||
// row1, col1, row2, col2, color);
|
||||
[self lazyResize:NO];
|
||||
|
||||
if (row1 < 0 || row2 >= maxRows || col1 < 0 || col2 > maxColumns) {
|
||||
//NSLog(@"[%s] WARNING : out of range, row1=%d row2=%d (%d) col1=%d "
|
||||
// "col2=%d (%d)", _cmd, row1, row2, maxRows, col1, col2,
|
||||
// maxColumns);
|
||||
if (row1 < 0 || row2 >= maxRows || col1 < 0 || col2 > maxColumns)
|
||||
return;
|
||||
}
|
||||
|
||||
NSDictionary *attribs = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
font, NSFontAttributeName,
|
||||
color, NSBackgroundColorAttributeName, nil];
|
||||
|
||||
NSRange range = { row1*(maxColumns+1) + col1, col2-col1+1 };
|
||||
|
||||
NSRange emptyRange = {0,col2-col1+1};
|
||||
int cells = col2 - col1 + 1;
|
||||
NSRange range, emptyRange = {0, cells};
|
||||
NSAttributedString *emptyString =
|
||||
[emptyRowString attributedSubstringFromRange:emptyRange];
|
||||
int r;
|
||||
|
||||
for (r=row1; r<=row2; ++r) {
|
||||
range = [self charRangeForRow:r column:col1 cells:cells];
|
||||
|
||||
[attribString replaceCharactersInRange:range
|
||||
withAttributedString:emptyString];
|
||||
[attribString setAttributes:attribs range:range];
|
||||
[attribString setAttributes:attribs
|
||||
range:NSMakeRange(range.location, cells)];
|
||||
|
||||
[self edited:(NSTextStorageEditedAttributes
|
||||
| NSTextStorageEditedCharacters) range:range
|
||||
changeInLength:0];
|
||||
range.location += maxColumns+1;
|
||||
changeInLength:cells-range.length];
|
||||
|
||||
#if MM_USE_ROW_CACHE
|
||||
rowCache[r].length += cells - range.length;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
- (void)clearAll
|
||||
{
|
||||
//NSLog(@"%s%@", _cmd, color);
|
||||
//NSLog(@"%s", _cmd);
|
||||
[self lazyResize:YES];
|
||||
}
|
||||
|
||||
- (void)setDefaultColorsBackground:(NSColor *)bgColor
|
||||
foreground:(NSColor *)fgColor
|
||||
{
|
||||
//NSLog(@"setDefaultColorsBackground:%@ foreground:%@", bgColor, fgColor);
|
||||
|
||||
if (defaultBackgroundColor != bgColor) {
|
||||
[defaultBackgroundColor release];
|
||||
defaultBackgroundColor = bgColor ? [bgColor retain] : nil;
|
||||
@@ -469,10 +487,13 @@
|
||||
- (void)setFont:(NSFont*)newFont
|
||||
{
|
||||
if (newFont && font != newFont) {
|
||||
[boldItalicFont release];
|
||||
[italicFont release];
|
||||
[boldFont release];
|
||||
[font release];
|
||||
|
||||
// NOTE! When setting a new font we make sure that the advancement of
|
||||
// each glyph is fixed.
|
||||
// each glyph is fixed.
|
||||
|
||||
float em = [newFont widthOfString:@"m"];
|
||||
float cellWidthMultiplier = [[NSUserDefaults standardUserDefaults]
|
||||
@@ -524,6 +545,51 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setWideFont:(NSFont *)newFont
|
||||
{
|
||||
if (!newFont) {
|
||||
// Use the normal font as the wide font (note that the normal font may
|
||||
// very well include wide characters.)
|
||||
if (font) [self setWideFont:font];
|
||||
} else if (newFont != fontWide) {
|
||||
[boldItalicFontWide release];
|
||||
[italicFontWide release];
|
||||
[boldFontWide release];
|
||||
[fontWide release];
|
||||
|
||||
float pointSize = [newFont pointSize];
|
||||
NSFontDescriptor *desc = [newFont fontDescriptor];
|
||||
NSDictionary *dictWide = [NSDictionary
|
||||
dictionaryWithObject:[NSNumber numberWithFloat:2*cellSize.width]
|
||||
forKey:NSFontFixedAdvanceAttribute];
|
||||
|
||||
desc = [desc fontDescriptorByAddingAttributes:dictWide];
|
||||
fontWide = [NSFont fontWithDescriptor:desc size:pointSize];
|
||||
[fontWide retain];
|
||||
|
||||
boldFontWide = [[NSFontManager sharedFontManager]
|
||||
convertFont:fontWide toHaveTrait:NSBoldFontMask];
|
||||
desc = [boldFontWide fontDescriptor];
|
||||
desc = [desc fontDescriptorByAddingAttributes:dictWide];
|
||||
boldFontWide = [NSFont fontWithDescriptor:desc size:pointSize];
|
||||
[boldFontWide retain];
|
||||
|
||||
italicFontWide = [[NSFontManager sharedFontManager]
|
||||
convertFont:fontWide toHaveTrait:NSItalicFontMask];
|
||||
desc = [italicFontWide fontDescriptor];
|
||||
desc = [desc fontDescriptorByAddingAttributes:dictWide];
|
||||
italicFontWide = [NSFont fontWithDescriptor:desc size:pointSize];
|
||||
[italicFontWide retain];
|
||||
|
||||
boldItalicFontWide = [[NSFontManager sharedFontManager]
|
||||
convertFont:italicFontWide toHaveTrait:NSBoldFontMask];
|
||||
desc = [boldItalicFontWide fontDescriptor];
|
||||
desc = [desc fontDescriptorByAddingAttributes:dictWide];
|
||||
boldItalicFontWide = [NSFont fontWithDescriptor:desc size:pointSize];
|
||||
[boldItalicFontWide retain];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSFont*)font
|
||||
{
|
||||
return font;
|
||||
@@ -581,14 +647,8 @@
|
||||
|
||||
- (unsigned)characterIndexForRow:(int)row column:(int)col
|
||||
{
|
||||
// Ensure the offset returned is valid.
|
||||
// This code also works if maxRows and/or maxColumns is 0.
|
||||
if (row >= maxRows) row = maxRows-1;
|
||||
if (row < 0) row = 0;
|
||||
if (col >= maxColumns) col = maxColumns-1;
|
||||
if (col < 0) col = 0;
|
||||
|
||||
return (unsigned)(col + row*(maxColumns+1));
|
||||
NSRange range = [self charRangeForRow:row column:col cells:1];
|
||||
return range.location != NSNotFound ? range.location : 0;
|
||||
}
|
||||
|
||||
// XXX: unused at the moment
|
||||
@@ -661,24 +721,71 @@
|
||||
return fitSize;
|
||||
}
|
||||
|
||||
- (NSRect)boundingRectForCharacterAtRow:(int)row column:(int)col
|
||||
{
|
||||
#if 1
|
||||
// This properly computes the position of where Vim expects the glyph to be
|
||||
// drawn. Had the typesetter actually computed the right position of each
|
||||
// character and not hidden some, this code would be correct.
|
||||
NSRect rect = NSZeroRect;
|
||||
|
||||
rect.origin.x = col*cellSize.width;
|
||||
rect.origin.y = row*cellSize.height;
|
||||
rect.size = cellSize;
|
||||
|
||||
// Wide character take up twice the width of a normal character.
|
||||
NSRange r = [self charRangeForRow:row column:col cells:1];
|
||||
if (NSNotFound != r.location
|
||||
&& [attribString attribute:MMWideCharacterAttributeName
|
||||
atIndex:r.location
|
||||
effectiveRange:nil])
|
||||
rect.size.width += rect.size.width;
|
||||
|
||||
return rect;
|
||||
#else
|
||||
// Use layout manager to compute bounding rect. This works in situations
|
||||
// where the layout manager decides to hide glyphs (Vim assumes all glyphs
|
||||
// are drawn).
|
||||
NSLayoutManager *lm = [[self layoutManagers] objectAtIndex:0];
|
||||
NSTextContainer *tc = [[lm textContainers] objectAtIndex:0];
|
||||
NSRange range = [self charRangeForRow:row column:col cells:1];
|
||||
NSRange glyphRange = [lm glyphRangeForCharacterRange:range
|
||||
actualCharacterRange:NULL];
|
||||
|
||||
return [lm boundingRectForGlyphRange:glyphRange inTextContainer:tc];
|
||||
#endif
|
||||
}
|
||||
|
||||
#if MM_USE_ROW_CACHE
|
||||
- (MMRowCacheEntry *)rowCache
|
||||
{
|
||||
return rowCache;
|
||||
}
|
||||
#endif
|
||||
|
||||
@end // MMTextStorage
|
||||
|
||||
|
||||
|
||||
|
||||
@implementation MMTextStorage (Private)
|
||||
|
||||
- (void)lazyResize:(BOOL)force
|
||||
{
|
||||
int i;
|
||||
|
||||
// Do nothing if the dimensions are already right.
|
||||
if (!force && actualRows == maxRows && actualColumns == maxColumns)
|
||||
return;
|
||||
|
||||
NSRange oldRange = NSMakeRange(0, actualRows*(actualColumns+1));
|
||||
NSRange oldRange = NSMakeRange(0, [attribString length]);
|
||||
|
||||
actualRows = maxRows;
|
||||
actualColumns = maxColumns;
|
||||
characterEqualsColumn = YES;
|
||||
|
||||
#if MM_USE_ROW_CACHE
|
||||
free(rowCache);
|
||||
rowCache = (MMRowCacheEntry*)calloc(actualRows, sizeof(MMRowCacheEntry));
|
||||
#endif
|
||||
|
||||
NSDictionary *dict;
|
||||
if (defaultBackgroundColor) {
|
||||
@@ -691,6 +798,7 @@
|
||||
}
|
||||
|
||||
NSMutableString *rowString = [NSMutableString string];
|
||||
int i;
|
||||
for (i = 0; i < maxColumns; ++i) {
|
||||
[rowString appendString:@" "];
|
||||
}
|
||||
@@ -703,6 +811,9 @@
|
||||
[attribString release];
|
||||
attribString = [[NSMutableAttributedString alloc] init];
|
||||
for (i=0; i<maxRows; ++i) {
|
||||
#if MM_USE_ROW_CACHE
|
||||
rowCache[i].length = actualColumns + 1;
|
||||
#endif
|
||||
[attribString appendAttributedString:emptyRowString];
|
||||
}
|
||||
|
||||
@@ -711,4 +822,161 @@
|
||||
range:oldRange changeInLength:fullRange.length-oldRange.length];
|
||||
}
|
||||
|
||||
- (NSRange)charRangeForRow:(int)row column:(int)col cells:(int)cells
|
||||
{
|
||||
// If no wide chars are used and if every char has length 1 (no composing
|
||||
// characters, no > 16 bit characters), then we can compute the range.
|
||||
if (characterEqualsColumn)
|
||||
return NSMakeRange(row*(actualColumns+1) + col, cells);
|
||||
|
||||
NSString *string = [attribString string];
|
||||
NSRange r, range = { NSNotFound, 0 };
|
||||
unsigned idx;
|
||||
int i;
|
||||
|
||||
if (row < 0 || row >= actualRows || col < 0 || col >= actualColumns
|
||||
|| col+cells > actualColumns) {
|
||||
NSLog(@"%s row=%d col=%d cells=%d is out of range (length=%d)",
|
||||
_cmd, row, col, cells, [string length]);
|
||||
return range;
|
||||
}
|
||||
|
||||
#if MM_USE_ROW_CACHE
|
||||
// Locate the beginning of the row
|
||||
MMRowCacheEntry *cache = rowCache;
|
||||
idx = 0;
|
||||
for (i = 0; i < row; ++i, ++cache)
|
||||
idx += cache->length;
|
||||
#else
|
||||
// Locate the beginning of the row by scanning for EOL characters.
|
||||
r.location = 0;
|
||||
for (i = 0; i < row; ++i) {
|
||||
r.length = [string length] - r.location;
|
||||
r = [string rangeOfString:@"\n" options:NSLiteralSearch range:r];
|
||||
if (NSNotFound == r.location)
|
||||
return range;
|
||||
++r.location;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Locate the column
|
||||
#if MM_USE_ROW_CACHE
|
||||
cache = &rowCache[row];
|
||||
|
||||
i = cache->col;
|
||||
if (col == i) {
|
||||
// Cache hit
|
||||
idx += cache->colOffset;
|
||||
} else {
|
||||
range.location = idx;
|
||||
|
||||
// Cache miss
|
||||
if (col < i - col) {
|
||||
// Search forward from beginning of line.
|
||||
i = 0;
|
||||
} else if (actualColumns - col < col - i) {
|
||||
// Search backward from end of line.
|
||||
i = actualColumns - 1;
|
||||
idx += cache->length - 2;
|
||||
} else {
|
||||
// Search from cache spot (forward or backward).
|
||||
idx += cache->colOffset;
|
||||
}
|
||||
|
||||
if (col > i) {
|
||||
// Forward search
|
||||
while (col > i) {
|
||||
r = [string rangeOfComposedCharacterSequenceAtIndex:idx];
|
||||
|
||||
// Wide chars take up two display cells.
|
||||
if ([attribString attribute:MMWideCharacterAttributeName
|
||||
atIndex:idx
|
||||
effectiveRange:nil])
|
||||
++i;
|
||||
|
||||
idx += r.length;
|
||||
++i;
|
||||
}
|
||||
} else if (col < i) {
|
||||
// Backward search
|
||||
while (col < i) {
|
||||
r = [string rangeOfComposedCharacterSequenceAtIndex:idx-1];
|
||||
idx -= r.length;
|
||||
--i;
|
||||
|
||||
// Wide chars take up two display cells.
|
||||
if ([attribString attribute:MMWideCharacterAttributeName
|
||||
atIndex:idx
|
||||
effectiveRange:nil])
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
cache->col = i;
|
||||
cache->colOffset = idx - range.location;
|
||||
}
|
||||
#else
|
||||
idx = r.location;
|
||||
for (i = 0; i < col; ++i) {
|
||||
r = [string rangeOfComposedCharacterSequenceAtIndex:idx];
|
||||
|
||||
// Wide chars take up two display cells.
|
||||
if ([attribString attribute:MMWideCharacterAttributeName
|
||||
atIndex:idx
|
||||
effectiveRange:nil])
|
||||
++i;
|
||||
|
||||
idx += r.length;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Count the number of characters that cover the cells.
|
||||
range.location = idx;
|
||||
for (i = 0; i < cells; ++i) {
|
||||
r = [string rangeOfComposedCharacterSequenceAtIndex:idx];
|
||||
|
||||
// Wide chars take up two display cells.
|
||||
if ([attribString attribute:MMWideCharacterAttributeName
|
||||
atIndex:idx
|
||||
effectiveRange:nil])
|
||||
++i;
|
||||
|
||||
idx += r.length;
|
||||
range.length += r.length;
|
||||
}
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
- (void)fixInvalidCharactersInRange:(NSRange)range
|
||||
{
|
||||
static NSCharacterSet *invalidCharacterSet = nil;
|
||||
NSRange invalidRange;
|
||||
unsigned end;
|
||||
|
||||
if (!invalidCharacterSet)
|
||||
invalidCharacterSet = [[NSCharacterSet characterSetWithRange:
|
||||
NSMakeRange(0x2028, 2)] retain];
|
||||
|
||||
// HACK! Replace characters that the text system can't handle (currently
|
||||
// LINE SEPARATOR U+2028 and PARAGRAPH SEPARATOR U+2029) with space.
|
||||
//
|
||||
// TODO: Treat these separately inside of Vim so we don't have to bother
|
||||
// here.
|
||||
while (range.length > 0) {
|
||||
invalidRange = [[attribString string]
|
||||
rangeOfCharacterFromSet:invalidCharacterSet
|
||||
options:NSLiteralSearch
|
||||
range:range];
|
||||
if (NSNotFound == invalidRange.location)
|
||||
break;
|
||||
|
||||
[attribString replaceCharactersInRange:invalidRange withString:@" "];
|
||||
|
||||
end = NSMaxRange(invalidRange);
|
||||
range.length -= end - range.location;
|
||||
range.location = end;
|
||||
}
|
||||
}
|
||||
|
||||
@end // MMTextStorage (Private)
|
||||
|
||||
+7
-13
@@ -7,6 +7,11 @@
|
||||
* Do ":help credits" in Vim to see a list of people who contributed.
|
||||
* See README.txt for an overview of the Vim source code.
|
||||
*/
|
||||
/*
|
||||
* MMTextView
|
||||
*
|
||||
* Dispatches keyboard and mouse input to the backend. Also handles drag&drop.
|
||||
*/
|
||||
|
||||
#import "MMTextView.h"
|
||||
#import "MMTextStorage.h"
|
||||
@@ -111,20 +116,9 @@ static NSString *MMKeypadEnterString = @"KA";
|
||||
|
||||
if (shouldDrawInsertionPoint) {
|
||||
MMTextStorage *ts = (MMTextStorage*)[self textStorage];
|
||||
NSLayoutManager *lm = [self layoutManager];
|
||||
NSTextContainer *tc = [self textContainer];
|
||||
|
||||
// Given (row,column), calculate the bounds of the glyph at that spot.
|
||||
// We use the layout manager because this gives us exactly the size and
|
||||
// location of the glyph so that we can match the insertion point to
|
||||
// it.
|
||||
unsigned charIdx = [ts characterIndexForRow:insertionPointRow
|
||||
column:insertionPointColumn];
|
||||
NSRange glyphRange =
|
||||
[lm glyphRangeForCharacterRange:NSMakeRange(charIdx,1)
|
||||
actualCharacterRange:NULL];
|
||||
NSRect ipRect = [lm boundingRectForGlyphRange:glyphRange
|
||||
inTextContainer:tc];
|
||||
NSRect ipRect = [ts boundingRectForCharacterAtRow:insertionPointRow
|
||||
column:insertionPointColumn];
|
||||
ipRect.origin.x += [self textContainerOrigin].x;
|
||||
ipRect.origin.y += [self textContainerOrigin].y;
|
||||
|
||||
|
||||
@@ -13,10 +13,8 @@
|
||||
|
||||
@interface MMTypesetter : NSATSTypesetter {
|
||||
}
|
||||
|
||||
- (void)layoutGlyphsInLayoutManager:(NSLayoutManager *)lm
|
||||
startingAtGlyphIndex:(unsigned)startGlyphIdx
|
||||
maxNumberOfLineFragments:(unsigned)maxNumLines
|
||||
nextGlyphIndex:(unsigned *)nextGlyph;
|
||||
|
||||
@end
|
||||
|
||||
@interface MMTypesetter2 : NSATSTypesetter {
|
||||
}
|
||||
@end
|
||||
|
||||
+132
-104
@@ -7,28 +7,90 @@
|
||||
* Do ":help credits" in Vim to see a list of people who contributed.
|
||||
* See README.txt for an overview of the Vim source code.
|
||||
*/
|
||||
/*
|
||||
* MMTypesetter
|
||||
*
|
||||
* Ensures that each line has a fixed height and deals with some baseline
|
||||
* issues.
|
||||
*/
|
||||
|
||||
#import "MMTypesetter.h"
|
||||
#import "MMTextStorage.h"
|
||||
#import "MacVim.h"
|
||||
|
||||
|
||||
// The 'linerange' functions count U+2028 and U+2029 as line end characters,
|
||||
// which causes rendering to be screwed up because Vim does not count them as
|
||||
// line end characters.
|
||||
#define MM_USE_LINERANGE 0
|
||||
|
||||
|
||||
#if 0
|
||||
@interface MMTypesetter (Private)
|
||||
- (NSCharacterSet *)hiddenCharSet;
|
||||
@end
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@implementation MMTypesetter
|
||||
|
||||
- (void)willSetLineFragmentRect:(NSRectPointer)lineRect
|
||||
forGlyphRange:(NSRange)glyphRange
|
||||
usedRect:(NSRectPointer)usedRect
|
||||
baselineOffset:(float *)baselineOffset
|
||||
{
|
||||
MMTextStorage *ts = (MMTextStorage*)[[self layoutManager] textStorage];
|
||||
float h = [ts cellSize].height;
|
||||
|
||||
// HACK! Force each line fragment rect to have a fixed height. By also
|
||||
// forcing the 'usedRect' to the same height we also ensure that the cursor
|
||||
// is as high as the line itself.
|
||||
lineRect->size.height = h;
|
||||
usedRect->size.height = h;
|
||||
|
||||
// See [MMTextStorage setLinespace:] for info on how 'linespace' support
|
||||
// works.
|
||||
*baselineOffset += floor(.5*[ts linespace]);
|
||||
}
|
||||
|
||||
#if 0
|
||||
- (void)setNotShownAttribute:(BOOL)flag forGlyphRange:(NSRange)glyphRange
|
||||
{
|
||||
if (1 != glyphRange.length)
|
||||
return;
|
||||
|
||||
NSLayoutManager *lm = [self layoutManager];
|
||||
unsigned charIdx = [lm characterIndexForGlyphAtIndex:glyphRange.location];
|
||||
|
||||
if ('\n' == [[[lm textStorage] string] characterAtIndex:charIdx])
|
||||
[lm setNotShownAttribute:flag forGlyphAtIndex:glyphRange.location];
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
- (NSTypesetterControlCharacterAction)
|
||||
actionForControlCharacterAtIndex:(unsigned)charIndex
|
||||
{
|
||||
//NSLog(@"%s%d", _cmd, charIndex);
|
||||
/*NSTextStorage *ts = [[self layoutManager] textStorage];
|
||||
|
||||
if ('\n' == [[ts string] characterAtIndex:charIndex])
|
||||
return NSTypesetterLineBreakAction;*/
|
||||
|
||||
return NSTypesetterWhitespaceAction;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
- (void)setLocation:(NSPoint)location
|
||||
withAdvancements:(const float *)advancements
|
||||
forStartOfGlyphRange:(NSRange)glyphRange
|
||||
{
|
||||
NSLog(@"setLocation:%@ withAdvancements:%f forStartOfGlyphRange:%@",
|
||||
NSStringFromPoint(location), advancements ? *advancements : 0,
|
||||
NSStringFromRange(glyphRange));
|
||||
[super setLocation:location withAdvancements:advancements
|
||||
forStartOfGlyphRange:glyphRange];
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@end // MMTypesetter
|
||||
|
||||
|
||||
|
||||
|
||||
@implementation MMTypesetter2
|
||||
|
||||
//
|
||||
// Layout glyphs so that each line fragment has a fixed size.
|
||||
//
|
||||
@@ -37,126 +99,92 @@
|
||||
// depending on whether it is a wide character or not). This is taken care of
|
||||
// by MMTextStorage in setAttributes:range: and in setFont:. All that is left
|
||||
// for the typesetter to do is to make sure each line fragment has the same
|
||||
// height and that unwanted glyphs are hidden.
|
||||
// height and that EOL glyphs are hidden.
|
||||
//
|
||||
- (void)layoutGlyphsInLayoutManager:(NSLayoutManager *)lm
|
||||
startingAtGlyphIndex:(unsigned)startGlyphIdx
|
||||
maxNumberOfLineFragments:(unsigned)maxNumLines
|
||||
nextGlyphIndex:(unsigned *)nextGlyph
|
||||
{
|
||||
// TODO: Check that it really is an MMTextStorage.
|
||||
// TODO: Check that it really is an MMTextStorage?
|
||||
MMTextStorage *ts = (MMTextStorage*)[lm textStorage];
|
||||
NSTextView *tv = [lm firstTextView];
|
||||
NSTextContainer *tc = [tv textContainer];
|
||||
NSTextContainer *tc = [[lm firstTextView] textContainer];
|
||||
NSFont *font = [ts font];
|
||||
NSString *text = [ts string];
|
||||
unsigned textLen = [text length];
|
||||
NSSize cellSize = [ts cellSize];
|
||||
// NOTE: With non-zero linespace the baseline is adjusted so that the text
|
||||
// is centered within a line.
|
||||
float baseline = [font descender] - floor(.5*[ts linespace]);
|
||||
|
||||
if (!(lm && ts && tv && tc && font && text && textLen
|
||||
if (!(lm && ts && tc && font && text
|
||||
&& [lm isValidGlyphIndex:startGlyphIdx]))
|
||||
return;
|
||||
|
||||
float baselineOffset = [[NSUserDefaults standardUserDefaults]
|
||||
floatForKey:MMBaselineOffsetKey];
|
||||
|
||||
baseline += baselineOffset;
|
||||
|
||||
// Note that we always start laying out lines from the beginning of a line,
|
||||
// even if 'startCharIdx' may be somewhere in the middle.
|
||||
unsigned startCharIdx = [lm characterIndexForGlyphAtIndex:startGlyphIdx];
|
||||
unsigned i, numberOfLines = 0, firstLine = 0;
|
||||
NSRange firstLineRange = { 0, 0 };
|
||||
if (startCharIdx >= [text length])
|
||||
return;
|
||||
|
||||
#if MM_USE_LINERANGE
|
||||
// Find the first line and its range, and count the number of lines. (This
|
||||
// info could also be gleaned from MMTextStorage, but we do it here anyway
|
||||
// to make absolutely sure everything is right.)
|
||||
for (i = 0; i < textLen; numberOfLines++) {
|
||||
NSRange lineRange = [text lineRangeForRange:NSMakeRange(i, 0)];
|
||||
if (NSLocationInRange(startCharIdx, lineRange)) {
|
||||
firstLine = numberOfLines;
|
||||
firstLineRange = lineRange;
|
||||
}
|
||||
[lm setTextContainer:tc forGlyphRange:
|
||||
[lm glyphRangeForCharacterRange:NSMakeRange(0, [text length])
|
||||
actualCharacterRange:nil]];
|
||||
|
||||
i = NSMaxRange(lineRange);
|
||||
//
|
||||
// STEP 1: Locate the line containing 'startCharIdx'.
|
||||
//
|
||||
MMRowCacheEntry *cache = [ts rowCache];
|
||||
unsigned lineIdx = 0, nextLineIdx = 0;
|
||||
int actualRows = [ts actualRows];
|
||||
int line = 0;
|
||||
|
||||
for (; line < actualRows; ++line, ++cache) {
|
||||
lineIdx = nextLineIdx;
|
||||
nextLineIdx += cache->length;
|
||||
if (startCharIdx < nextLineIdx)
|
||||
break;
|
||||
}
|
||||
#else
|
||||
unsigned stride = 1 + [ts actualColumns];
|
||||
numberOfLines = [ts actualRows];
|
||||
firstLine = (unsigned)(startCharIdx/stride);
|
||||
firstLineRange.location = firstLine * stride;
|
||||
unsigned len = [text length] - firstLineRange.location;
|
||||
firstLineRange.length = len < stride ? len : stride;
|
||||
#endif
|
||||
|
||||
// Perform line fragment generation one line at a time.
|
||||
NSRange lineRange = firstLineRange;
|
||||
unsigned endGlyphIdx = startGlyphIdx;
|
||||
for (i = 0; i < maxNumLines && lineRange.length; ++i) {
|
||||
NSRange glyphRange = [lm glyphRangeForCharacterRange:lineRange
|
||||
actualCharacterRange:nil];
|
||||
NSRect lineRect = { 0, (firstLine+i)*cellSize.height,
|
||||
cellSize.width*(lineRange.length-1), cellSize.height };
|
||||
unsigned endLineIdx = NSMaxRange(lineRange);
|
||||
NSPoint glyphPt = { 0, cellSize.height+baseline };
|
||||
unsigned j;
|
||||
//
|
||||
// STEP 2: Generate line fragment rects one line at a time until there are
|
||||
// no more lines in the text storage, or until 'maxNumLines' have been
|
||||
// exhausted. (There is no point in just laying out one line, the layout
|
||||
// manager will keep calling this method until there are no more lines in
|
||||
// the text storage.)
|
||||
//
|
||||
|
||||
endGlyphIdx = NSMaxRange(glyphRange);
|
||||
// NOTE: With non-zero linespace the baseline is adjusted so that the text
|
||||
// is centered within a line.
|
||||
float baseline = [font descender] - floor(.5*[ts linespace])
|
||||
+ [[NSUserDefaults standardUserDefaults]
|
||||
floatForKey:MMBaselineOffsetKey];
|
||||
NSSize cellSize = [ts cellSize];
|
||||
NSPoint glyphPt = { 0, cellSize.height+baseline };
|
||||
|
||||
NSRange lineRange = { lineIdx, 0 };
|
||||
NSRange glyphRange = { startGlyphIdx, 0 };
|
||||
NSRect lineRect = { 0, line*cellSize.height,
|
||||
[ts actualColumns]*cellSize.width, cellSize.height };
|
||||
int endLine = line + maxNumLines;
|
||||
if (endLine > actualRows)
|
||||
endLine = actualRows;
|
||||
|
||||
for (; line < endLine; ++line, ++cache) {
|
||||
lineRange.length = cache->length;
|
||||
|
||||
glyphRange = [lm glyphRangeForCharacterRange:lineRange
|
||||
actualCharacterRange:nil];
|
||||
|
||||
[lm setTextContainer:tc forGlyphRange:glyphRange];
|
||||
[lm setLineFragmentRect:lineRect forGlyphRange:glyphRange
|
||||
usedRect:lineRect];
|
||||
[lm setLocation:glyphPt forStartOfGlyphRange:glyphRange];
|
||||
|
||||
// Hide end-of-line and non-zero space characters (there is one after
|
||||
// every wide character).
|
||||
for (j = lineRange.location; j < endLineIdx; ++j) {
|
||||
unichar ch = [text characterAtIndex:j];
|
||||
if (ch == 0x200b || ch == '\n') {
|
||||
NSRange range = { j, 1 };
|
||||
range = [lm glyphRangeForCharacterRange:range
|
||||
actualCharacterRange:nil];
|
||||
[lm setNotShownAttribute:YES forGlyphAtIndex:range.location];
|
||||
}
|
||||
}
|
||||
lineRange.location += lineRange.length;
|
||||
lineRect.origin.y += cellSize.height;
|
||||
|
||||
#if MM_USE_LINERANGE
|
||||
lineRange = [text lineRangeForRange:NSMakeRange(endLineIdx, 0)];
|
||||
#else
|
||||
lineRange.location = endLineIdx;
|
||||
len = [text length] - lineRange.location;
|
||||
if (len < lineRange.length)
|
||||
lineRange.length = len;
|
||||
#endif
|
||||
// Hide EOL character (otherwise a square will be rendered).
|
||||
[lm setNotShownAttribute:YES forGlyphAtIndex:lineRange.location-1];
|
||||
}
|
||||
|
||||
if (nextGlyph)
|
||||
*nextGlyph = endGlyphIdx;
|
||||
*nextGlyph = NSMaxRange(glyphRange);
|
||||
}
|
||||
|
||||
@end // MMTypesetter
|
||||
@end // MMTypesetter2
|
||||
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
@implementation MMTypesetter (Private)
|
||||
|
||||
- (NSCharacterSet *)hiddenCharSet
|
||||
{
|
||||
static NSCharacterSet *hiddenCharSet = nil;
|
||||
|
||||
if (!hiddenCharSet) {
|
||||
NSString *string = [NSString stringWithFormat:@"%C\n", 0x200b];
|
||||
hiddenCharSet = [NSCharacterSet
|
||||
characterSetWithCharactersInString:string];
|
||||
[hiddenCharSet retain];
|
||||
}
|
||||
|
||||
return hiddenCharSet;
|
||||
}
|
||||
|
||||
@end // MMTypesetter (Private)
|
||||
#endif
|
||||
|
||||
@@ -56,5 +56,3 @@
|
||||
timeout:(NSTimeInterval)timeout;
|
||||
- (void)addVimInput:(NSString *)string;
|
||||
@end
|
||||
|
||||
// vim: set ft=objc:
|
||||
|
||||
@@ -7,6 +7,21 @@
|
||||
* Do ":help credits" in Vim to see a list of people who contributed.
|
||||
* See README.txt for an overview of the Vim source code.
|
||||
*/
|
||||
/*
|
||||
* MMVimController
|
||||
*
|
||||
* Coordinates input/output to/from backend. Each MMBackend communicates
|
||||
* directly with a MMVimController.
|
||||
*
|
||||
* MMVimController does not deal with visual presentation. Essentially it
|
||||
* should be able to run with no window present.
|
||||
*
|
||||
* Output from the backend is received in processCommandQueue:. Input is sent
|
||||
* to the backend via sendMessage:data: or addVimInput:. The latter allows
|
||||
* execution of arbitrary stings in the Vim process, much like the Vim script
|
||||
* function remote_send() does. The messages that may be passed between
|
||||
* frontend and backend are defined in an enum in MacVim.h.
|
||||
*/
|
||||
|
||||
#import "MMVimController.h"
|
||||
#import "MMWindowController.h"
|
||||
@@ -716,6 +731,21 @@ static NSTimeInterval MMResendInterval = 0.5;
|
||||
[windowController setFont:font];
|
||||
|
||||
[name release];
|
||||
} else if (SetWideFontMsgID == msgid) {
|
||||
const void *bytes = [data bytes];
|
||||
float size = *((float*)bytes); bytes += sizeof(float);
|
||||
int len = *((int*)bytes); bytes += sizeof(int);
|
||||
if (len > 0) {
|
||||
NSString *name = [[NSString alloc]
|
||||
initWithBytes:(void*)bytes length:len
|
||||
encoding:NSUTF8StringEncoding];
|
||||
NSFont *font = [NSFont fontWithName:name size:size];
|
||||
[windowController setWideFont:font];
|
||||
|
||||
[name release];
|
||||
} else {
|
||||
[windowController setWideFont:nil];
|
||||
}
|
||||
} else if (SetDefaultColorsMsgID == msgid) {
|
||||
const void *bytes = [data bytes];
|
||||
unsigned bg = *((unsigned*)bytes); bytes += sizeof(unsigned);
|
||||
@@ -846,12 +876,13 @@ static NSTimeInterval MMResendInterval = 0.5;
|
||||
[textStorage deleteLinesFromRow:row lineCount:count
|
||||
scrollBottom:bot left:left right:right
|
||||
color:[NSColor colorWithArgbInt:color]];
|
||||
} else if (ReplaceStringDrawType == type) {
|
||||
} else if (DrawStringDrawType == type) {
|
||||
int bg = *((int*)bytes); bytes += sizeof(int);
|
||||
int fg = *((int*)bytes); bytes += sizeof(int);
|
||||
int sp = *((int*)bytes); bytes += sizeof(int);
|
||||
int row = *((int*)bytes); bytes += sizeof(int);
|
||||
int col = *((int*)bytes); bytes += sizeof(int);
|
||||
int cells = *((int*)bytes); bytes += sizeof(int);
|
||||
int flags = *((int*)bytes); bytes += sizeof(int);
|
||||
int len = *((int*)bytes); bytes += sizeof(int);
|
||||
NSString *string = [[NSString alloc]
|
||||
@@ -876,12 +907,13 @@ static NSTimeInterval MMResendInterval = 0.5;
|
||||
// shape:MMInsertionPointBlock
|
||||
// color:color];
|
||||
}
|
||||
[textStorage replaceString:string
|
||||
atRow:row column:col
|
||||
withFlags:flags
|
||||
foregroundColor:[NSColor colorWithRgbInt:fg]
|
||||
backgroundColor:[NSColor colorWithArgbInt:bg]
|
||||
specialColor:[NSColor colorWithRgbInt:sp]];
|
||||
|
||||
[textStorage drawString:string
|
||||
atRow:row column:col cells:cells
|
||||
withFlags:flags
|
||||
foregroundColor:[NSColor colorWithRgbInt:fg]
|
||||
backgroundColor:[NSColor colorWithArgbInt:bg]
|
||||
specialColor:[NSColor colorWithRgbInt:sp]];
|
||||
|
||||
[string release];
|
||||
} else if (InsertLinesDrawType == type) {
|
||||
|
||||
+15
-2
@@ -7,6 +7,11 @@
|
||||
* Do ":help credits" in Vim to see a list of people who contributed.
|
||||
* See README.txt for an overview of the Vim source code.
|
||||
*/
|
||||
/*
|
||||
* MMVimView
|
||||
*
|
||||
* A view class with a tabline, scrollbars, and text view.
|
||||
*/
|
||||
|
||||
#import "MMVimView.h"
|
||||
|
||||
@@ -86,8 +91,12 @@ enum {
|
||||
|
||||
NSString *typesetterString = [[NSUserDefaults standardUserDefaults]
|
||||
stringForKey:MMTypesetterKey];
|
||||
if (![typesetterString isEqual:@"NSTypesetter"]) {
|
||||
MMTypesetter *typesetter = [[MMTypesetter alloc] init];
|
||||
if ([typesetterString isEqual:@"MMTypesetter"]) {
|
||||
NSTypesetter *typesetter = [[MMTypesetter alloc] init];
|
||||
[lm setTypesetter:typesetter];
|
||||
[typesetter release];
|
||||
} else if ([typesetterString isEqual:@"MMTypesetter2"]) {
|
||||
NSTypesetter *typesetter = [[MMTypesetter2 alloc] init];
|
||||
[lm setTypesetter:typesetter];
|
||||
[typesetter release];
|
||||
} else {
|
||||
@@ -96,6 +105,10 @@ enum {
|
||||
setFloat:1.0 forKey:MMCellWidthMultiplierKey];
|
||||
}
|
||||
|
||||
// The characters in the text storage are in display order, so disable
|
||||
// bidirectional text processing (this call is 10.4 only).
|
||||
[[lm typesetter] setBidiProcessingEnabled:NO];
|
||||
|
||||
[tc setWidthTracksTextView:NO];
|
||||
[tc setHeightTracksTextView:NO];
|
||||
[tc setLineFragmentPadding:0];
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
identifier:(long)ident;
|
||||
- (void)setDefaultColorsBackground:(NSColor *)back foreground:(NSColor *)fore;
|
||||
- (void)setFont:(NSFont *)font;
|
||||
- (void)setWideFont:(NSFont *)font;
|
||||
- (void)processCommandQueueDidFinish;
|
||||
- (void)popupMenu:(NSMenu *)menu atRow:(int)row column:(int)col;
|
||||
- (void)showTabBar:(BOOL)on;
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
* Do ":help credits" in Vim to see a list of people who contributed.
|
||||
* See README.txt for an overview of the Vim source code.
|
||||
*/
|
||||
/*
|
||||
* MMWindowController
|
||||
*
|
||||
* Handles resizing of windows, acts as an mediator between MMVimView and
|
||||
* MMVimController.
|
||||
*/
|
||||
|
||||
#import "MMWindowController.h"
|
||||
#import <PSMTabBarControl.h>
|
||||
@@ -261,6 +267,11 @@ NSMutableArray *buildMenuAddress(NSMenu *menu)
|
||||
[self updateResizeIncrements];
|
||||
}
|
||||
|
||||
- (void)setWideFont:(NSFont *)font
|
||||
{
|
||||
[[vimView textStorage] setWideFont:font];
|
||||
}
|
||||
|
||||
- (void)processCommandQueueDidFinish
|
||||
{
|
||||
// XXX: If not in live resize and vimview's desired size differs from actual
|
||||
|
||||
+5
-4
@@ -136,6 +136,7 @@ enum {
|
||||
SetScrollbarThumbMsgID,
|
||||
ScrollbarEventMsgID,
|
||||
SetFontMsgID,
|
||||
SetWideFontMsgID,
|
||||
VimShouldCloseMsgID,
|
||||
SetDefaultColorsMsgID,
|
||||
ExecuteActionMsgID,
|
||||
@@ -157,11 +158,13 @@ enum {
|
||||
};
|
||||
|
||||
|
||||
#define DRAW_WIDE 0x40 /* draw wide text */
|
||||
|
||||
enum {
|
||||
ClearAllDrawType = 1,
|
||||
ClearBlockDrawType,
|
||||
DeleteLinesDrawType,
|
||||
ReplaceStringDrawType,
|
||||
DrawStringDrawType,
|
||||
InsertLinesDrawType,
|
||||
DrawCursorDrawType
|
||||
};
|
||||
@@ -206,6 +209,7 @@ extern NSString *MMBaselineOffsetKey;
|
||||
extern NSString *MMTranslateCtrlClickKey;
|
||||
extern NSString *MMTopLeftPointKey;
|
||||
extern NSString *MMOpenFilesInTabsKey;
|
||||
extern NSString *MMNoFontSubstitutionKey;
|
||||
|
||||
|
||||
|
||||
@@ -220,6 +224,3 @@ ATSFontContainerRef loadFonts();
|
||||
@interface NSString (MMExtras)
|
||||
- (NSString *)stringByEscapingSpecialFilenameCharacters;
|
||||
@end
|
||||
|
||||
|
||||
// vim: set ft=objc:
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
* Do ":help credits" in Vim to see a list of people who contributed.
|
||||
* See README.txt for an overview of the Vim source code.
|
||||
*/
|
||||
/*
|
||||
* MacVim.m: Code shared between Vim and MacVim.
|
||||
*/
|
||||
|
||||
#import "MacVim.h"
|
||||
|
||||
@@ -46,6 +49,7 @@ char *MessageStrings[] =
|
||||
"SetScrollbarThumbMsgID",
|
||||
"ScrollbarEventMsgID",
|
||||
"SetFontMsgID",
|
||||
"SetWideFontMsgID",
|
||||
"VimShouldCloseMsgID",
|
||||
"SetDefaultColorsMsgID",
|
||||
"ExecuteActionMsgID",
|
||||
@@ -86,6 +90,7 @@ NSString *MMBaselineOffsetKey = @"MMBaselineOffset";
|
||||
NSString *MMTranslateCtrlClickKey = @"MMTranslateCtrlClick";
|
||||
NSString *MMTopLeftPointKey = @"MMTopLeftPoint";
|
||||
NSString *MMOpenFilesInTabsKey = @"MMOpenFilesInTabs";
|
||||
NSString *MMNoFontSubstitutionKey = @"MMNoFontSubstitution";
|
||||
|
||||
|
||||
|
||||
|
||||
+125
-81
@@ -7,6 +7,11 @@
|
||||
* Do ":help credits" in Vim to see a list of people who contributed.
|
||||
* See README.txt for an overview of the Vim source code.
|
||||
*/
|
||||
/*
|
||||
* gui_macvim.m
|
||||
*
|
||||
* Hooks for the Vim gui code. Mainly passes control on to MMBackend.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MMBackend.h"
|
||||
@@ -19,7 +24,14 @@
|
||||
// gui_mch_update()).
|
||||
static NSTimeInterval MMUpdateTimeoutInterval = 0.1f;
|
||||
|
||||
// NOTE: The default font is bundled with the application.
|
||||
static NSString *MMDefaultFontName = @"DejaVu Sans Mono";
|
||||
static float MMDefaultFontSize = 12.0f;
|
||||
static float MMMinFontSize = 6.0f;
|
||||
static float MMMaxFontSize = 100.0f;
|
||||
|
||||
|
||||
static NSFont *gui_macvim_font_with_name(char_u *name);
|
||||
static BOOL gui_macvim_is_valid_action(NSString *action);
|
||||
|
||||
|
||||
@@ -259,8 +271,8 @@ gui_mch_draw_string(int row, int col, char_u *s, int len, int flags)
|
||||
}
|
||||
#endif
|
||||
|
||||
[[MMBackend sharedInstance] replaceString:(char*)s length:len
|
||||
row:row column:col flags:flags];
|
||||
[[MMBackend sharedInstance] drawString:(char*)s length:len row:row
|
||||
column:col cells:len flags:flags];
|
||||
|
||||
#ifdef FEAT_MBYTE
|
||||
if (conv_str)
|
||||
@@ -272,22 +284,12 @@ gui_mch_draw_string(int row, int col, char_u *s, int len, int flags)
|
||||
int
|
||||
gui_macvim_draw_string(int row, int col, char_u *s, int len, int flags)
|
||||
{
|
||||
//
|
||||
// Output chars until a wide char found. If a wide char is found, output a
|
||||
// zero-width space after it so that a wide char looks like two chars to
|
||||
// MMTextStorage. This way 1 char corresponds to 1 column.
|
||||
//
|
||||
|
||||
int c;
|
||||
int cn;
|
||||
int cl;
|
||||
int i;
|
||||
int c, cn, cl, i;
|
||||
int start = 0;
|
||||
int endcol = col;
|
||||
int startcol = col;
|
||||
BOOL outPad = NO;
|
||||
BOOL wide = NO;
|
||||
MMBackend *backend = [MMBackend sharedInstance];
|
||||
static char ZeroWidthSpace[] = { 0xe2, 0x80, 0x8b };
|
||||
#ifdef FEAT_MBYTE
|
||||
char_u *conv_str = NULL;
|
||||
|
||||
@@ -298,74 +300,34 @@ gui_macvim_draw_string(int row, int col, char_u *s, int len, int flags)
|
||||
}
|
||||
#endif
|
||||
|
||||
// Loop over each character and output text when it changes from normal to
|
||||
// wide and vice versa.
|
||||
for (i = 0; i < len; i += cl) {
|
||||
c = utf_ptr2char(s + i);
|
||||
cl = utf_ptr2len(s + i);
|
||||
cn = utf_char2cells(c);
|
||||
|
||||
if (!utf_iscomposing(c)) {
|
||||
if (outPad) {
|
||||
outPad = NO;
|
||||
#if 0
|
||||
NSString *string = [[NSString alloc]
|
||||
initWithBytesNoCopy:(void*)(s+start)
|
||||
length:i-start
|
||||
encoding:NSUTF8StringEncoding
|
||||
freeWhenDone:NO];
|
||||
NSLog(@"Flushing string=%@ len=%d row=%d col=%d end=%d",
|
||||
string, i-start, row, startcol, endcol);
|
||||
[string release];
|
||||
#endif
|
||||
[backend replaceString:(char*)(s+start) length:i-start
|
||||
row:row column:startcol flags:flags];
|
||||
if ((cn > 1 && !wide) || (cn <= 1 && wide)) {
|
||||
// Changed from normal to wide or vice versa.
|
||||
[backend drawString:(char*)(s+start) length:i-start
|
||||
row:row column:startcol
|
||||
cells:endcol-startcol
|
||||
flags:(wide ? flags|DRAW_WIDE : flags)];
|
||||
|
||||
start = i;
|
||||
startcol = endcol;
|
||||
#if 0
|
||||
NSLog(@"Padding len=%d row=%d col=%d", sizeof(ZeroWidthSpace),
|
||||
row, endcol-1);
|
||||
#endif
|
||||
[backend replaceString:ZeroWidthSpace
|
||||
length:sizeof(ZeroWidthSpace)
|
||||
row:row column:endcol-1 flags:flags];
|
||||
}
|
||||
|
||||
wide = cn > 1;
|
||||
endcol += cn;
|
||||
}
|
||||
|
||||
if (cn > 1) {
|
||||
#if 0
|
||||
NSLog(@"Wide char detected! (char=%C hex=%x cells=%d)", c, c, cn);
|
||||
#endif
|
||||
outPad = YES;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (row < 1) {
|
||||
NSString *string = [[NSString alloc]
|
||||
initWithBytesNoCopy:(void*)(s+start)
|
||||
length:len-start
|
||||
encoding:NSUTF8StringEncoding
|
||||
freeWhenDone:NO];
|
||||
NSLog(@"Output string=%@ len=%d row=%d col=%d", string, len-start, row,
|
||||
startcol);
|
||||
[string release];
|
||||
}
|
||||
#endif
|
||||
|
||||
// Output remaining characters.
|
||||
[backend replaceString:(char*)(s+start) length:len-start
|
||||
row:row column:startcol flags:flags];
|
||||
|
||||
if (outPad) {
|
||||
#if 0
|
||||
NSLog(@"Padding len=%d row=%d col=%d", sizeof(ZeroWidthSpace), row,
|
||||
endcol-1);
|
||||
#endif
|
||||
[backend replaceString:ZeroWidthSpace
|
||||
length:sizeof(ZeroWidthSpace)
|
||||
row:row column:endcol-1 flags:flags];
|
||||
}
|
||||
[backend drawString:(char*)(s+start) length:len-start
|
||||
row:row column:startcol cells:endcol-startcol
|
||||
flags:(wide ? flags|DRAW_WIDE : flags)];
|
||||
|
||||
#ifdef FEAT_MBYTE
|
||||
if (conv_str)
|
||||
@@ -829,6 +791,10 @@ gui_mch_show_toolbar(int showit)
|
||||
gui_mch_free_font(font)
|
||||
GuiFont font;
|
||||
{
|
||||
if (font != NOFONT) {
|
||||
//NSLog(@"gui_mch_free_font(font=0x%x)", font);
|
||||
[(NSFont*)font release];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -840,7 +806,15 @@ gui_mch_get_font(char_u *name, int giveErrorIfMissing)
|
||||
{
|
||||
//NSLog(@"gui_mch_get_font(name=%s, giveErrorIfMissing=%d)", name,
|
||||
// giveErrorIfMissing);
|
||||
return 0;
|
||||
|
||||
NSFont *font = gui_macvim_font_with_name(name);
|
||||
if (font)
|
||||
return (GuiFont)[font retain];
|
||||
|
||||
if (giveErrorIfMissing)
|
||||
EMSG2(_(e_font), name);
|
||||
|
||||
return NOFONT;
|
||||
}
|
||||
|
||||
|
||||
@@ -852,8 +826,9 @@ gui_mch_get_font(char_u *name, int giveErrorIfMissing)
|
||||
char_u *
|
||||
gui_mch_get_fontname(GuiFont font, char_u *name)
|
||||
{
|
||||
//NSLog(@"gui_mch_get_fontname(font=%d, name=%s)", font, name);
|
||||
return 0;
|
||||
if (name == NULL)
|
||||
return NULL;
|
||||
return vim_strsave(name);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -867,21 +842,30 @@ gui_mch_init_font(char_u *font_name, int fontset)
|
||||
{
|
||||
//NSLog(@"gui_mch_init_font(font_name=%s, fontset=%d)", font_name, fontset);
|
||||
|
||||
// HACK! This gets called whenever the user types :set gfn=fontname, so
|
||||
// for now we set the font here.
|
||||
// TODO! Proper font handling, the way Vim expects it.
|
||||
if (font_name && STRCMP(font_name, "*") == 0) {
|
||||
// :set gfn=* shows the font panel.
|
||||
do_cmdline_cmd((char_u*)":macaction orderFrontFontPanel:");
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
#ifdef FEAT_MBYTE
|
||||
font_name = CONVERT_TO_UTF8(font_name);
|
||||
#endif
|
||||
NSFont *font = gui_macvim_font_with_name(font_name);
|
||||
if (font) {
|
||||
[(NSFont*)gui.norm_font release];
|
||||
gui.norm_font = (GuiFont)font;
|
||||
|
||||
BOOL ok = [[MMBackend sharedInstance] setFontWithName:(char*)font_name];
|
||||
// NOTE: MacVim keeps separate track of the normal and wide fonts.
|
||||
// Unless the user changes 'guifontwide' manually, they are based on
|
||||
// the same (normal) font. Also note that each time the normal font is
|
||||
// set, the advancement may change so the wide font needs to be updated
|
||||
// as well (so that it is always twice the width of the normal font).
|
||||
[[MMBackend sharedInstance] setFont:font];
|
||||
[[MMBackend sharedInstance] setWideFont:
|
||||
(NOFONT == gui.wide_font ? font : (NSFont*)gui.wide_font)];
|
||||
|
||||
#ifdef FEAT_MBYTE
|
||||
CONVERT_TO_UTF8_FREE(font_name);
|
||||
#endif
|
||||
return OK;
|
||||
}
|
||||
|
||||
return ok;
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
|
||||
@@ -891,9 +875,69 @@ gui_mch_init_font(char_u *font_name, int fontset)
|
||||
void
|
||||
gui_mch_set_font(GuiFont font)
|
||||
{
|
||||
// Font selection is done inside MacVim...nothing here to do.
|
||||
}
|
||||
|
||||
|
||||
NSFont *
|
||||
gui_macvim_font_with_name(char_u *name)
|
||||
{
|
||||
NSFont *font = nil;
|
||||
NSString *fontName = MMDefaultFontName;
|
||||
float size = MMDefaultFontSize;
|
||||
BOOL parseFailed = NO;
|
||||
|
||||
#ifdef FEAT_MBYTE
|
||||
name = CONVERT_TO_UTF8(name);
|
||||
#endif
|
||||
|
||||
if (name) {
|
||||
fontName = [NSString stringWithUTF8String:(char*)name];
|
||||
|
||||
NSArray *components = [fontName componentsSeparatedByString:@":"];
|
||||
if ([components count] == 2) {
|
||||
NSString *sizeString = [components lastObject];
|
||||
if ([sizeString length] > 0
|
||||
&& [sizeString characterAtIndex:0] == 'h') {
|
||||
sizeString = [sizeString substringFromIndex:1];
|
||||
if ([sizeString length] > 0) {
|
||||
size = [sizeString floatValue];
|
||||
fontName = [components objectAtIndex:0];
|
||||
}
|
||||
} else {
|
||||
parseFailed = YES;
|
||||
}
|
||||
} else if ([components count] > 2) {
|
||||
parseFailed = YES;
|
||||
}
|
||||
|
||||
if (!parseFailed) {
|
||||
// Replace underscores with spaces.
|
||||
fontName = [[fontName componentsSeparatedByString:@"_"]
|
||||
componentsJoinedByString:@" "];
|
||||
}
|
||||
}
|
||||
|
||||
if (!parseFailed && [fontName length] > 0) {
|
||||
if (size < MMMinFontSize) size = MMMinFontSize;
|
||||
if (size > MMMaxFontSize) size = MMMaxFontSize;
|
||||
|
||||
font = [NSFont fontWithName:fontName size:size];
|
||||
|
||||
if (!font && MMDefaultFontName == fontName) {
|
||||
// If for some reason the MacVim default font is not in the app
|
||||
// bundle, then fall back on the system default font.
|
||||
font = [NSFont userFixedPitchFontOfSize:0];
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef FEAT_MBYTE
|
||||
CONVERT_TO_UTF8_FREE(name);
|
||||
#endif
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
// -- Scrollbars ------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user