diff --git a/ChangeLog b/ChangeLog index 105d0d0d1..046e18551 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,27 @@ +1999-08-19 Michael Hanni + + * Headers/NSAttributedString.h + NSLayoutManager.h + NSTableView.h: modifications, mostly added ivars. + * Source/NSLayoutManager.m: implemented much code for layout and + display. + * Source/NSMenu.m: fixed some nagging problems. tearoffs should + work almost universally. Positions are saved and restored. If you + previous position exists for the main menu it goes to the upper + left corner. + * Source/NSMenuView.m: a few attempts to fix the "move to submenu" + bugs. + * Source/NSPopUpButton.m: many bug fixes and new implementations. + Too many to list here. + * Source/NSTableView.m: more code implemented, though I wouldn't + recommend trying to use this class. :-) + * Source/NSText.m: debugging code to help pinpoint which methods + can be used in NSTextView and which have to be overridden. + * Source/NSTextStorage.m: properly assigns itself to its + layoutManager. + * Source/NSTextView.m: more code additions to coincide with the + new NSLayoutManager. + 1999-08-03 Michael Hanni * Source/NSMenu.m: commented out code which did multiple menu diff --git a/Headers/gnustep/gui/NSAttributedString.h b/Headers/gnustep/gui/NSAttributedString.h index 5311ca393..5baadba88 100644 --- a/Headers/gnustep/gui/NSAttributedString.h +++ b/Headers/gnustep/gui/NSAttributedString.h @@ -56,6 +56,13 @@ enum NSSingleUnderlineStyle = 1 }; +@interface NSAttributedString(DrawingAddition) +-(NSSize) sizeRange:(NSRange) aRange; +-(void) drawRange:(NSRange) aRange atPoint:(NSPoint) aPoint; +-(void) drawRange:(NSRange) aRange inRect:(NSRect) aRect; +-(BOOL) areMultipleFontsInRange:(NSRange) aRange; +@end + @interface NSAttributedString (AppKit) - (BOOL) containsAttachments; - (NSDictionary*) fontAttributesInRange: (NSRange)range; diff --git a/Headers/gnustep/gui/NSLayoutManager.h b/Headers/gnustep/gui/NSLayoutManager.h index e363cf40c..94800b93d 100644 --- a/Headers/gnustep/gui/NSLayoutManager.h +++ b/Headers/gnustep/gui/NSLayoutManager.h @@ -58,8 +58,8 @@ @class NSView; @class NSEvent; - - +// Michael's botch list. :-) +@class GSRunStorage; // These glyph attributes are used only inside the glyph generation machinery, but must be shared between componenets. enum _NSGlyphAttribute { @@ -87,6 +87,12 @@ typedef enum { NSStorage *_containerUsedRects; NSStorage *_glyphs; + + // GS data storage. + GSRunStorage *containerRuns; + GSRunStorage *fragmentRuns; + GSRunStorage *locationRuns; + NSRunStorage *_containerRuns; NSRunStorage *_fragmentRuns; NSRunStorage *_glyphLocations; @@ -149,6 +155,7 @@ typedef enum { BOOL _showsControlChars; float _hyphenationFactor; BOOL _usesScreenFonts; + BOOL finished; } /**************************** Initialization ****************************/ diff --git a/Headers/gnustep/gui/NSTableView.h b/Headers/gnustep/gui/NSTableView.h index fa66c10ea..8eb3648b0 100644 --- a/Headers/gnustep/gui/NSTableView.h +++ b/Headers/gnustep/gui/NSTableView.h @@ -4,7 +4,6 @@ #include #include -@class NSColor; @class NSTableColumn; @class NSTableHeaderView; @class NSView; diff --git a/Source/NSLayoutManager.m b/Source/NSLayoutManager.m index a4398e89e..f44738c30 100644 --- a/Source/NSLayoutManager.m +++ b/Source/NSLayoutManager.m @@ -7,6 +7,8 @@ Author: Jonathan Gapen Date: July 1999 + Author: Michael Hanni + Date: August 1999 This file is part of the GNUstep GUI Library. @@ -27,14 +29,271 @@ */ #include +// _GSRunSearchKey is an internal class which serves as the foundation for +// all our searching. This may not be an elegant way to go about this, so +// if someone wants to optimize this out, please do. + +@interface _GSRunSearchKey : NSObject +{ +@public + NSRange glyphRange; +} +@end + +@implementation _GSRunSearchKey +- (id)init +{ + return [super init]; +} + +- (void) dealloc +{ + [super dealloc]; +} +@end + +@interface GSGlyphLocation : _GSRunSearchKey +{ +@public + NSPoint point; +} +@end + +@implementation GSGlyphLocation +- (id)init +{ + return [super init]; +} + +- (void) dealloc +{ + [super dealloc]; +} +@end + +@interface GSLineLayoutInfo : _GSRunSearchKey +{ +@public + NSRect lineFragmentRect; + NSRect usedRect; +} +@end + +@implementation GSLineLayoutInfo +- (id)init +{ + return [super init]; +} + +- (void) dealloc +{ + [super dealloc]; +} +@end + +@interface GSTextContainerLayoutInfo : _GSRunSearchKey +{ +@public + NSTextContainer *textContainer; + NSString *testString; +} +@end + +@implementation GSTextContainerLayoutInfo +- (id)init +{ + return [super init]; +} + +- (void) dealloc +{ + [super dealloc]; +} +@end + +#define GSI_ARRAY_TYPES GSUNION_OBJ + +#ifdef GSIArray +#undef GSIArray +#endif +#include + +static NSComparisonResult aSort(GSIArrayItem i0, GSIArrayItem i1) +{ + if (((_GSRunSearchKey *)(i0.obj))->glyphRange.location + < ((_GSRunSearchKey *)(i1.obj))->glyphRange.location) + return NSOrderedAscending; + else if (((_GSRunSearchKey *)(i0.obj))->glyphRange.location + >= NSMaxRange(((_GSRunSearchKey *)(i1.obj))->glyphRange)) + return NSOrderedDescending; + else + return NSOrderedSame; +} + +@interface GSRunStorage : NSObject +{ + unsigned int _count; + void *_runs; +} +- (void)insertObject:(id)anObject; +- (void)insertObject:(id)anObject atIndex:(unsigned)theIndex; +- (id)objectAtIndex:(unsigned)theIndex; +- (unsigned)indexOfObject:(id)anObject; +- (unsigned)indexOfObjectContainingLocation:(unsigned)aLocation; +- (id)objectContainingLocation:(unsigned)aLocation; +- (int)count; +@end + +@implementation GSRunStorage +- (id)init +{ + NSZone *z; + + [super init]; + + z = [self zone]; + + _runs = NSZoneMalloc(z, sizeof(GSIArray_t)); + GSIArrayInitWithZoneAndCapacity((GSIArray)_runs, z, 8); + + return self; +} + +- (void)insertObject:(id)anObject +{ + _GSRunSearchKey *aKey = [_GSRunSearchKey new]; + _GSRunSearchKey *aObject = (_GSRunSearchKey *)anObject; + int position; + + aKey->glyphRange.location = aObject->glyphRange.location; + + position = GSIArrayInsertionPosition(_runs, (GSIArrayItem)aKey, aSort); + + NSLog(@"key: %d aObject: %d position: %d", aKey->glyphRange.location, +aObject->glyphRange.location, position); + + if (position > 0) + { + _GSRunSearchKey *anKey = GSIArrayItemAtIndex(_runs, (unsigned)position - 1).obj; + + if (anKey->glyphRange.location == aObject->glyphRange.location) + { +// GSIArrayInsertSorted(_runs, (GSIArrayItem)anObject, aSort); + NSLog(@"=========> duplicated item."); + GSIArraySetItemAtIndex(_runs, (GSIArrayItem)anObject, position-1); + } + else + { + NSLog(@"=========> not duplicated item."); + GSIArrayInsertItem(_runs, (GSIArrayItem)anObject, position); + } + } + else if (position == 0) + { + NSLog(@"=========> first item (zero index)."); + GSIArrayInsertItem(_runs, (GSIArrayItem)anObject, position); +// GSIArrayInsertSorted(_runs, (GSIArrayItem)anObject, aSort); +// [self insertObject:anObject atIndex:position]; + } + else + NSLog(@"dead. VERY DEAD DEAD DEAD DEAD."); + + NSLog(@"==> %d item(s)", GSIArrayCount(_runs)); +} + +- (void)insertObject:(id)anObject + atIndex:(unsigned)theIndex +{ + NSLog(@"insertObject:atIndex: called. %d item(s)", GSIArrayCount(_runs)); + GSIArrayInsertSorted(_runs, (GSIArrayItem)anObject, aSort); +// GSIArrayInsertItem(_runs, (GSIArrayItem)anObject, theIndex); + NSLog(@"insertObject:atIndex: ended. %d item(s)", GSIArrayCount(_runs)); +} + +- (void)removeObjectAtIndex:(int)theIndex +{ + GSIArrayRemoveItemAtIndex(_runs, theIndex); +} + +- (id)objectAtIndex:(unsigned)theIndex +{ + return GSIArrayItemAtIndex(_runs, (unsigned)theIndex).obj; +} + +- (unsigned)indexOfObject:(id)anObject +{ + return NSNotFound; +} + +- (unsigned)indexOfObjectContainingLocation:(unsigned)aLocation +{ + _GSRunSearchKey *aKey = [_GSRunSearchKey new]; + int position; + + aKey->glyphRange.location = aLocation; + + position = GSIArrayInsertionPosition(_runs, (GSIArrayItem)aKey, aSort); + + if (position >= 0 && position - 1 >= 0) + { + aKey = GSIArrayItemAtIndex(_runs, (unsigned)position - 1).obj; + + if (NSLocationInRange(aLocation, aKey->glyphRange)) + { + return (position - 1); + } + } + + return -1; +} + +- (id)objectContainingLocation:(unsigned)aLocation +{ + _GSRunSearchKey *aKey = [_GSRunSearchKey new]; + int position; + + aKey->glyphRange.location = aLocation; + + position = GSIArrayInsertionPosition(_runs, (GSIArrayItem)aKey, aSort); + + if (position >= 0 && position - 1 >= 0) + { + aKey = GSIArrayItemAtIndex(_runs, (unsigned)position - 1).obj; + + if (NSLocationInRange(aLocation, aKey->glyphRange)) + { + return aKey; + } + } + + return nil; +} + +- (id)lastObject +{ + return GSIArrayItemAtIndex(_runs, GSIArrayCount(_runs) - 1).obj; +} + +- (int)count +{ + return GSIArrayCount(_runs); +} +@end + @implementation NSLayoutManager - (id) init { [super init]; + _backgroundLayout = YES; _delegate = nil; _textContainers = [[NSMutableArray alloc] initWithCapacity: 2]; + + containerRuns = [GSRunStorage new]; + fragmentRuns = [GSRunStorage new]; + locationRuns = [GSRunStorage new]; + return self; } @@ -135,6 +394,23 @@ { NSLog(@"NSLayoutManager was just notified that a change in the text storage occured."); + +/* + if (mask == NSTextStorageEditedCharacters) + { + aLayoutHole = [[NSLayoutHole alloc] +initWithCharacterRange:invalidatedRange isSoft:NO]; + } + else if (mask == NSTextStorageEditedAttributes) + { + } + else if (mask == NSTextStorageEditedCharacters | NSTextStorageEditedAttributes) + { + } +*/ + // invalidation should occure here. + + [self _doLayout]; } // @@ -220,6 +496,9 @@ storage occured."); // // Setting glyph attributes // + +// Each NSGlyph has an attribute field, yes? + - (void)setIntAttribute: (int)attribute value: (int)anInt forGlyphAtIndex: (unsigned)glyphIndex @@ -238,26 +517,55 @@ storage occured."); - (void)setTextContainer: (NSTextContainer *)aTextContainer forGlyphRange: (NSRange)glyphRange { + GSTextContainerLayoutInfo *theLine = [GSTextContainerLayoutInfo new]; + + theLine->glyphRange = glyphRange; + ASSIGN(theLine->textContainer, aTextContainer); + + [containerRuns insertObject:theLine]; } - (NSRange)glyphRangeForTextContainer: (NSTextContainer *)aTextContainer { - NSSize tcSize = [aTextContainer containerSize]; + int i; - + NSLog(@"glyphRangeForTextContainer: called. There are %d +textContainer(s) in containerRuns.", [containerRuns count]); - return NSMakeRange(0, 0); + for (i=0;i<[containerRuns count];i++) + { + GSTextContainerLayoutInfo *aNewLine = [containerRuns objectAtIndex:i]; + + NSLog(@"glyphRangeForTextContainer: (%d, %d)", +aNewLine->glyphRange.location, +aNewLine->glyphRange.length); + + if ([aNewLine->textContainer isEqual:aTextContainer]) + { + NSLog(@"glyphRangeForWantedTextContainer: (%d, %d)", +aNewLine->glyphRange.location, +aNewLine->glyphRange.length); + + return aNewLine->glyphRange; + } + } + + return NSMakeRange(NSNotFound, 0); } - (NSTextContainer *)textContainerForGlyphAtIndex: (unsigned)glyphIndex effectiveRange: (NSRange *)effectiveRange { - return nil; -} + GSTextContainerLayoutInfo *theLine = [containerRuns objectContainingLocation:glyphIndex]; -- (NSRect)usedRectForTextContainer: (NSTextContainer *)aTextContainer -{ - return NSZeroRect; + if(theLine) + { + (NSRange *)effectiveRange = &theLine->glyphRange; + return theLine->textContainer; + } + + (NSRange *)effectiveRange = NULL; + return nil; } // @@ -267,17 +575,42 @@ storage occured."); forGlyphRange: (NSRange)glyphRange usedRect: (NSRect)usedRect { + GSLineLayoutInfo *aNewLine = [GSLineLayoutInfo new]; + + aNewLine->glyphRange = glyphRange; + aNewLine->lineFragmentRect = fragmentRect; + aNewLine->usedRect = usedRect; + + [fragmentRuns insertObject:aNewLine]; } - (NSRect)lineFragmentRectForGlyphAtIndex: (unsigned)glyphIndex effectiveRange: (NSRange *)lineFragmentRange { + GSLineLayoutInfo *theLine = [fragmentRuns objectContainingLocation:glyphIndex]; + + if (theLine) + { + (NSRange *)lineFragmentRange = &theLine->glyphRange; + return theLine->lineFragmentRect; + } + + (NSRange *)lineFragmentRange = NULL; return NSZeroRect; } - (NSRect)lineFragmentUsedRectForGlyphAtIndex: (unsigned)glyphIndex effectiveRange: (NSRange *)lineFragmentRange { + GSLineLayoutInfo *theLine = [fragmentRuns objectContainingLocation:glyphIndex]; + + if (theLine) + { + (NSRange *)lineFragmentRange = &theLine->glyphRange; + return theLine->usedRect; + } + + (NSRange *)lineFragmentRange = NULL; return NSZeroRect; } @@ -315,9 +648,16 @@ storage occured."); // // Layout of glyphs // + - (void)setLocation: (NSPoint)aPoint forStartOfGlyphRange: (NSRange)glyphRange { + GSGlyphLocation *aNewLine = [GSGlyphLocation new]; + + aNewLine->glyphRange = glyphRange; + aNewLine->point = aPoint; + + [locationRuns insertObject:aNewLine]; } - (NSPoint)locationForGlyphAtIndex: (unsigned)glyphIndex @@ -327,7 +667,14 @@ storage occured."); - (NSRange)rangeOfNominallySpacedGlyphsContainingIndex: (unsigned)glyphIndex { - return NSMakeRange(0, 0); + GSLineLayoutInfo *theLine = [locationRuns objectContainingLocation:glyphIndex]; + + if (theLine) + { + return theLine->glyphRange; + } + + return NSMakeRange(NSNotFound, 0); } - (NSRect *)rectArrayForCharacterRange: (NSRange)charRange @@ -335,7 +682,43 @@ storage occured."); inTextContainer: (NSTextContainer *)aTextContainer rectCount: (unsigned *)rectCount { - return NULL; +/* + GSLineLayoutInfo *theLine = [GSLineLayoutInfo new]; + int position, lastPosition; + int i, j = 0; + + theLine->glyphRange.location = charRange.location; + + position = GSIArrayInsertionPosition(lineFragments, (GSIArrayItem)theLine, aSort); + + if (position < 0) + { + return NULL; + } + + theLine->glyphRange.location = charRange.location + charRange.length; + + lastPosition = GSIArrayInsertionPosition(lineFragments, (GSIArrayItem)theLine, aSort); + + if (lastPosition > 0) + { + _cachedRectArray = NSZoneRealloc([self zone], _cachedRectArray, + (lastPosition - position) * sizeof(NSRect)); + + _cachedRectArrayCapacity = lastPosition - position; + + for (i = position - 1; i < lastPosition - 1; i++) + { + GSLineLayoutInfo *aLine = GSIArrayItemAtIndex(lineFragments, i).obj; + + _cachedRectArray[j] = aLine->lineFragmentRect; + j++; + } + } + + (*rectCount) = (position - 1 + lastPosition - 1); + return _cachedRectArray; +*/ } - (NSRect *)rectArrayForGlyphRange: (NSRange)glyphRange @@ -343,12 +726,41 @@ storage occured."); inTextContainer: (NSTextContainer *)aTextContainer rectCount: (unsigned *)rectCount { - return NULL; + return _cachedRectArray; } - (NSRect)boundingRectForGlyphRange: (NSRange)glyphRange inTextContainer: (NSTextContainer *)aTextContainer { + +/* Returns a single bounding rectangle enclosing all glyphs and other +marks drawn in aTextContainer for glyphRange, including glyphs that draw +outside their line fragment rectangles and text attributes such as +underlining. This method is useful for determining the area that needs to +be redrawn when a range of glyphs changes. */ +/* + unsigned rectCount; + NSRect *rects = [self rectArrayForCharacterRange: [self glyphRangeForTextContainer:aTextContainer] + withinSelectedCharacterRange: NSMakeRange(0,0) + inTextContainer: aTextContainer + rectCount: &rectCount]; +// NSPoint aOrigin = [aTextContainer originPoint]; + NSRect rRect = NSZeroRect; + int i; + + for (i=0;iglyphRange.location, +info->glyphRange.length, +info->lineFragmentRect.origin.x, +info->lineFragmentRect.origin.y, +info->lineFragmentRect.size.width, +info->lineFragmentRect.size.height); +*/ + } + + firstPosition = [fragmentRuns indexOfObjectContainingLocation:glyphRange.location]; + lastPosition = [fragmentRuns +indexOfObjectContainingLocation:(glyphRange.location+glyphRange.length-2)]; + + NSLog(@"glyphRange: (%d, %d) position1: %d position2: %d", +glyphRange.location, glyphRange.length, firstPosition, lastPosition); + + if (firstPosition >= 0) + { + if (lastPosition == -1) + { + lastPosition = [fragmentRuns count]; // FIXME + NSLog(@"fixed lastPosition: %d", lastPosition); + } + + for (i = firstPosition; i < lastPosition; i++) + { + GSLineLayoutInfo *aLine = [fragmentRuns objectAtIndex:i]; +/* +NSLog(@"drawRange: (%d, %d) inRect (%f, %f) (%f, %f)", +aLine->glyphRange.location, +aLine->glyphRange.length, +aLine->lineFragmentRect.origin.x, +aLine->lineFragmentRect.origin.y, +aLine->lineFragmentRect.size.width, +aLine->lineFragmentRect.size.height); +*/ + [_textStorage drawRange:aLine->glyphRange inRect:aLine->lineFragmentRect]; + } + } } - (void)drawUnderlineForGlyphRange: (NSRange)glyphRange @@ -523,3 +988,238 @@ fractionOfDistanceThroughGlyph: (float *)partialFraction } @end /* NSLayoutManager */ + +/* Thew methods laid out here are not correct, however the code they +contain for the most part is. Therefore, my country and a handsome gift of +Ghiradelli chocolate to he who puts all the pieces together :) */ + +@interface _GNUTextScanner:NSObject +{ NSString *string; + NSCharacterSet *set,*iSet; + unsigned stringLength; + NSRange activeRange; +} ++(_GNUTextScanner*) scannerWithString:(NSString*) aStr set:(NSCharacterSet*) aSet invertedSet:(NSCharacterSet*) anInvSet; +-(void) setString:(NSString*) aString set:(NSCharacterSet*) aSet invertedSet:(NSCharacterSet*) anInvSet; +-(NSRange) _scanCharactersInverted:(BOOL) inverted; +-(NSRange) scanSetCharacters; +-(NSRange) scanNonSetCharacters; +-(BOOL) isAtEnd; +-(unsigned) scanLocation; +-(void) setScanLocation:(unsigned) aLoc; +@end + +@implementation NSLayoutManager (Private) +- (int)_rebuildLayoutForTextContainer:(NSTextContainer *)aContainer + startingAtGlyphIndex:(int)glyphIndex +{ + NSSize cSize = [aContainer containerSize]; + float i = 0.0; + NSMutableArray *lines = [NSMutableArray new]; + int indexToAdd; + _GNUTextScanner *lineScanner; + _GNUTextScanner *paragraphScanner; + BOOL lastLineForContainerReached = NO; + NSString *aString; + int previousScanLocation; + int endScanLocation; + int startIndex; + NSRect firstProposedRect; + NSRect secondProposedRect; + NSFont *default_font = [NSFont systemFontOfSize: 12.0]; + int widthOfString; + NSSize rSize; + NSCharacterSet *selectionParagraphGranularitySet = [NSCharacterSet characterSetWithCharactersInString:@"\n"]; + NSCharacterSet *selectionWordGranularitySet = [NSCharacterSet characterSetWithCharactersInString:@" "]; + NSCharacterSet *invSelectionWordGranularitySet = [selectionWordGranularitySet invertedSet]; + NSCharacterSet *invSelectionParagraphGranularitySet = [selectionParagraphGranularitySet invertedSet]; + NSRange paragraphRange; + NSRange leadingSpacesRange; + NSRange currentStringRange; + NSRange trailingSpacesRange; + NSRange leadingNlRange; + NSSize lSize; + float lineWidth = 0.0; + float ourLines = 0.0; + + NSLog(@"rebuilding Layout at index: %d.\n", glyphIndex); + + // 1.) figure out how many glyphs we can fit in our container by + // breaking up glyphs from the first unlaid out glyph and breaking it + // into lines. + // + // 2.) + // a.) set the range for the container + // b.) for each line in step 1 we need to set a lineFragmentRect and + // an origin point. + + + // Here we go at part 1. + + startIndex = glyphIndex; + +// lineScanner = [NSScanner scannerWithString:[_textStorage string]]; + + paragraphScanner = [_GNUTextScanner scannerWithString:[_textStorage string] + set:selectionParagraphGranularitySet invertedSet:invSelectionParagraphGranularitySet]; + + [paragraphScanner setScanLocation:startIndex]; + + NSLog(@"length of textStorage: %d", [[_textStorage string] length]); + +// NSLog(@"buffer: %@", [_textStorage string]); + + // This scanner eats one word at a time, we should have it imbeded in + // another scanner that snacks on paragraphs (i.e. lines that end with + // \n). Look in NSText. + + while (![paragraphScanner isAtEnd]) + { +// leadingNlRange=[paragraphScanner scanSetCharacters]; + paragraphRange = [paragraphScanner scanNonSetCharacters]; + leadingNlRange=[paragraphScanner scanSetCharacters]; + + if (leadingNlRange.length) + currentStringRange = NSUnionRange (leadingNlRange,paragraphRange); + + NSLog(@"paragraphRange: (%d, %d)", paragraphRange.location, paragraphRange.length); + + NSLog(@"======> begin paragraph"); + + lineScanner = [_GNUTextScanner scannerWithString:[[_textStorage string] substringWithRange:paragraphRange] + set:selectionWordGranularitySet invertedSet:invSelectionWordGranularitySet]; + + while(![lineScanner isAtEnd]) + { + previousScanLocation = [lineScanner scanLocation]; + // snack next word + leadingSpacesRange = [lineScanner scanSetCharacters]; // leading spaces: only first time + currentStringRange = [lineScanner scanNonSetCharacters]; + trailingSpacesRange= [lineScanner scanSetCharacters]; + + if (leadingSpacesRange.length) + currentStringRange = NSUnionRange (leadingSpacesRange,currentStringRange); + if (trailingSpacesRange.length) + currentStringRange = NSUnionRange (trailingSpacesRange,currentStringRange); + + lSize = [_textStorage sizeRange:currentStringRange]; + + if (lineWidth + lSize.width < cSize.width) + { + if ([lineScanner isAtEnd]) + { + NSLog(@"we are at end before finishing a line: %d.\n", [lineScanner scanLocation]); + [lines addObject:[NSNumber numberWithInt:(int)[lineScanner scanLocation] + paragraphRange.location]]; + } + + lineWidth += lSize.width; + NSLog(@"lineWidth: %f", lineWidth); + } + else + { + if (ourLines > cSize.height) + { + lastLineForContainerReached = YES; + break; + } + + [lineScanner setScanLocation:previousScanLocation]; + indexToAdd = previousScanLocation + paragraphRange.location; + ourLines += 14.0; + lineWidth = 0; + + NSLog(@"indexToAdd: %d\tourLines: %f", indexToAdd, ourLines); + + [lines addObject:[NSNumber numberWithInt:indexToAdd]]; + } + } + + if (lastLineForContainerReached) + break; + } + + endScanLocation = [lineScanner scanLocation] + paragraphRange.location; + + NSLog(@"endScanLocation: %d", endScanLocation); + + // set this container for that glyphrange + + [self setTextContainer:aContainer + forGlyphRange:NSMakeRange(startIndex, endScanLocation - startIndex)]; + + NSLog(@"ok, move on to step 2."); + + // step 2. break the lines up and assign rects to them. + + for (i=0;i<[lines count];i++) + { + NSRect aRect, *bRect; + float padding = [aContainer lineFragmentPadding]; + NSRange ourRange; + + NSLog(@"\t\t===> %d", [[lines objectAtIndex:i] intValue]); + + if (i == 0) + { + ourRange = NSMakeRange (startIndex, + [[lines objectAtIndex:i] intValue] - startIndex); + } + else + { + ourRange = NSMakeRange ([[lines objectAtIndex:i-1] intValue], +[[lines objectAtIndex:i] intValue] - [[lines objectAtIndex:i-1] +intValue]); + } + + firstProposedRect = NSMakeRect (0, i * 14, cSize.width, 14); + + // ask our textContainer to fix our lineFragment. + + secondProposedRect = [aContainer lineFragmentRectForProposedRect:firstProposedRect + sweepDirection: NULL + movementDirection: NULL + remainingRect: &bRect]; + + // set the line fragmentRect for this range. + + [self setLineFragmentRect: secondProposedRect + forGlyphRange: ourRange + usedRect: aRect]; + + // set the location for this string to be 'show'ed. + + [self setLocation:NSMakePoint(secondProposedRect.origin.x + padding, + secondProposedRect.origin.y + padding) + forStartOfGlyphRange: ourRange]; + } + +// bloody hack. +// if (moreText) +// [delegate layoutManager:self +// didCompleteLayoutForTextContainer:[textContainers objectAtIndex:i] +// atEnd:NO]; +// else +// [delegate layoutManager:self +// didCompleteLayoutForTextContainer:[textContainers objectAtIndex:i] +// atEnd:YES]; + + [lines release]; + + return endScanLocation; +} + +- (void)_doLayout +{ + int i; + BOOL moreText; + int gIndex = 0; + + NSLog(@"doLayout called.\n"); + + for (i=0;i<[_textContainers count];i++) + { + gIndex = [self _rebuildLayoutForTextContainer:[_textContainers objectAtIndex:i] + startingAtGlyphIndex:gIndex]; + } +} +@end diff --git a/Source/NSMenu.m b/Source/NSMenu.m index 703a4656a..2d030db8d 100644 --- a/Source/NSMenu.m +++ b/Source/NSMenu.m @@ -50,9 +50,12 @@ #include #include #include +#include static NSZone *menuZone = NULL; +static NSString* NSMenuLocationsKey = @"NSMenuLocations"; + @implementation NSMenu // Class Methods @@ -174,12 +177,10 @@ static NSZone *menuZone = NULL; [[aWindow contentView] addSubview:titleView]; [titleView setMenu: self]; -/* [defaultCenter addObserver: self selector: @selector(_showTornOffMenuIfAny:) name: NSApplicationWillFinishLaunchingNotification object: theApp]; -*/ [defaultCenter addObserver: self selector: @selector(_deactivate:) name: NSApplicationWillResignActiveNotification @@ -383,7 +384,7 @@ static NSZone *menuZone = NULL; - (int) indexOfItem: (id )anObject { if (![(NSMenuItemCell *)anObject isKindOfClass: [NSMenuItemCell class]] - || [(id)anObject isKindOfClass: [NSPopUpButtonCell class]]) + || ![(id)anObject isKindOfClass: [NSPopUpButtonCell class]]) { NSLog(@"You must use an NSMenuItemCell, or a derivative thereof.\n"); return -1; @@ -429,11 +430,23 @@ static NSZone *menuZone = NULL; return i; } } + return -1; } - (int) indexOfItemWithSubmenu: (NSMenu *)anObject { + int i; + + for (i=0;i<[menu_items count];i++) + { + if ([[[menu_items objectAtIndex:i] title] + isEqual:[anObject title]]) + { + return i; + } + } + return -1; } @@ -586,6 +599,9 @@ static NSZone *menuZone = NULL; if (shouldBeEnabled != wasEnabled) { [cell setEnabled: shouldBeEnabled]; + [[self window] flushWindow]; +// [menu_view setNeedsDisplay:YES]; +// [menu_view setNeedsDisplayInRect:[menu_view rectOfItemAtIndex:i]]; // FIXME // [menuCells setNeedsDisplayInRect: [menuCells cellFrameAtRow: i]]; } @@ -703,10 +719,38 @@ static NSZone *menuZone = NULL; [titleView setFrameOrigin: NSMakePoint(0, mFrame.size.height)]; [titleView setFrameSize: NSMakeSize (mFrame.size.width,21)]; size.height = mFrame.size.height+21; - [bWindow setFrame: NSMakeRect(300,300,size.width,size.height) - display: NO]; - [aWindow setFrame: NSMakeRect(300,300,size.width,size.height) - display: YES]; + [aWindow setContentSize: size]; + [bWindow setContentSize: size]; + +/* + if (menu_supermenu) + { + frame.origin = [supermenu locationForSubmenu: self]; + } + else + { + NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; + NSDictionary* menuLocations = [defaults + objectForKey: NSMenuLocationsKey]; + NSString* key; + NSArray* array; + + if ([[NSApplication sharedApplication] mainMenu] == self) + key = @"Main menu"; + else + key = [self title]; + + if (key) + { + array = [menuLocations objectForKey: key]; + if (array && [array isKindOfClass: [NSArray class]]) + { + frame.origin.x = [[array objectAtIndex: 0] floatValue]; + frame.origin.y = [[array objectAtIndex: 1] floatValue]; + } + } + } +*/ } else { @@ -754,6 +798,30 @@ static NSZone *menuZone = NULL; [aWindow orderOut:nil]; } +- (void)_showTornOffMenuIfAny: (NSNotification*)notification +{ +NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; +NSDictionary* menuLocations = [defaults objectForKey: NSMenuLocationsKey]; +NSString* key; +NSArray* array; + + if ([[NSApplication sharedApplication] mainMenu] == self) + key = nil; // Ignore the main menu + else + key = [self title]; + + if (key) + { + array = [menuLocations objectForKey: key]; + if (array && [array isKindOfClass: [NSArray class]]) + { + [titleView windowBecomeTornOff]; + [self _setTornOff:YES]; + [self display]; + } + } +} + - (BOOL) isFollowTransient { return menu_follow_transient; @@ -762,27 +830,90 @@ static NSZone *menuZone = NULL; - (void) _setTornOff:(BOOL)flag { menu_is_tornoff = flag; + [[[self supermenu] menuView] setHighlightedItemIndex:-1]; + +/* + if (flag) + { + if (menu_supermenu) + { + menu_supermenu->menu_attached_menu = nil; + menu_supermenu = nil; + } + } +*/ } - (void) _performMenuClose:(id)sender { + NSUserDefaults* defaults; + NSMutableDictionary* menuLocations; + NSString* key; + [self _setTornOff:NO]; [self close]; [titleView _releaseCloseButton]; + + defaults = [NSUserDefaults standardUserDefaults]; + menuLocations = [[[defaults objectForKey: NSMenuLocationsKey] + mutableCopy] autorelease]; + + key = [self title]; // Remove window's position$ + if (key) // info from defaults db + { + [menuLocations removeObjectForKey: key]; + [defaults setObject: menuLocations forKey: NSMenuLocationsKey]; + [defaults synchronize]; + } } - (void) display { if (menu_changed) [self sizeToFit]; - if (menu_supermenu) // query super menu for - { // position + + if (menu_supermenu && ![self isTornOff]) // query super menu for + { // position NSPoint location = [menu_supermenu locationForSubmenu: self]; [aWindow setFrameOrigin: location]; menu_supermenu->menu_attached_menu = self; - } + } + else + { + NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; + NSDictionary* menuLocations = [defaults + objectForKey: NSMenuLocationsKey]; + NSString* key; + NSArray* array; + NSPoint origin; + + if ([[NSApplication sharedApplication] mainMenu] == self) + key = @"Main menu"; + else + key = [self title]; + + if (key) + { + array = [menuLocations objectForKey: key]; + if (array && [array isKindOfClass: [NSArray class]]) + { + origin.x = [[array objectAtIndex: 0] floatValue]; + origin.y = [[array objectAtIndex: 1] floatValue]; + [aWindow setFrameOrigin: origin]; + } + else + { + float aPoint = [[NSScreen mainScreen] frame].size.height - [aWindow frame].size.height; + + [aWindow setFrameOrigin:NSMakePoint(0,aPoint)]; + [bWindow setFrameOrigin:NSMakePoint(0,aPoint)]; + } + } + } + + [self submenuAction: nil]; menu_is_visible = YES; [aWindow orderFront:nil]; @@ -883,6 +1014,13 @@ static NSZone *menuZone = NULL; - (void) mouseDown: (NSEvent*)theEvent { + NSUserDefaults *defaults; + NSMutableDictionary *menuLocations; + NSMenu *appMainMenu; + NSPoint origin; + NSArray* array; + NSString* key; + NSPoint lastLocation; NSPoint location; unsigned eventMask = NSLeftMouseUpMask | NSLeftMouseDownMask @@ -958,7 +1096,27 @@ static NSZone *menuZone = NULL; } } [NSEvent stopPeriodicEvents]; + // save position code goes here. FIXME. + appMainMenu = [NSApp mainMenu]; + defaults = [NSUserDefaults standardUserDefaults]; + menuLocations = [[[defaults objectForKey: NSMenuLocationsKey] mutableCopy] autorelease]; + + if (!menuLocations) + menuLocations = [NSMutableDictionary dictionaryWithCapacity: 2]; + origin = [[menu window] frame].origin; + array = [NSArray arrayWithObjects: + [[NSNumber numberWithInt: origin.x] stringValue], + [[NSNumber numberWithInt: origin.y] stringValue], nil]; + + if (menu == appMainMenu) + key = @"Main menu"; + else + key = [menu title]; // Save menu window pos + + [menuLocations setObject: array forKey: key]; // in defaults databa + [defaults setObject: menuLocations forKey: NSMenuLocationsKey]; + [defaults synchronize]; } - (void) windowBecomeTornOff diff --git a/Source/NSMenuView.m b/Source/NSMenuView.m index 0ab3388f2..654145496 100644 --- a/Source/NSMenuView.m +++ b/Source/NSMenuView.m @@ -595,10 +595,18 @@ static float GSMenuBarHeight = 25.0; // a guess. } else { - [self setHighlightedItemIndex: -1]; - lastIndex = index; - weWereOut = YES; - [window flushWindow]; + if (![[menuv_items_link objectAtIndex: lastIndex] hasSubmenu]) + { + [self setHighlightedItemIndex: -1]; + lastIndex = index; + weWereOut = YES; + [window flushWindow]; + } + else + { + weLeftMenu = YES; + done = YES; + } } } diff --git a/Source/NSPopUpButton.m b/Source/NSPopUpButton.m index 0f2a832b0..b31176af6 100644 --- a/Source/NSPopUpButton.m +++ b/Source/NSPopUpButton.m @@ -158,10 +158,14 @@ { int i; + [[popb_menu itemArray] removeAllObjects]; + +/* for (i=0;i<[self numberOfItems];i++) { [popb_menu removeItemAtIndex:i]; } +*/ [self synchronizeTitleAndSelectedItem]; } @@ -196,7 +200,9 @@ - (int)indexOfSelectedItem { -// FIXME + if (popb_selectedItem >= 0) + return popb_selectedItem; + return -1; } @@ -304,8 +310,25 @@ return -1; } -- (int)setTitle:(NSString *)aString +- (void)setTitle:(NSString *)aString { + if (!popb_pullsDown) + { + int aIndex = [self indexOfItemWithTitle:aString]; + + if (aIndex >= 0) + popb_selectedItem = aIndex; + else + { + [self addItemWithTitle:aString]; + popb_selectedItem = [self indexOfItemWithTitle:aString]; + [self setNeedsDisplay:YES]; + } + } + else + { + [self setNeedsDisplay:YES]; + } } - (SEL)action @@ -330,25 +353,33 @@ - (void)_buttonPressed:(id)sender { - if (!popb_pullsDown) - popb_selectedItem = [self indexOfItemWithRepresentedObject:[sender representedObject]]; - else - popb_selectedItem = 0; + popb_selectedItem = [self indexOfItemWithRepresentedObject:[sender representedObject]]; [self synchronizeTitleAndSelectedItem]; - - [self lockFocus]; - [self drawRect:[self frame]]; - [self unlockFocus]; + [self setNeedsDisplay:YES]; if (pub_target && pub_action) [pub_target performSelector:pub_action withObject:self]; + + if (popb_pullsDown) + popb_selectedItem = 0; } - (void)synchronizeTitleAndSelectedItem { // urph + if (popb_selectedItem > [self numberOfItems] - 1) + { + popb_selectedItem = 0; + } + + [self sizeToFit]; +} + +- (void)sizeToFit +{ + [[popb_menu menuView] sizeToFit]; } - (void)_popup:(NSNotification*)notification diff --git a/Source/NSTableView.m b/Source/NSTableView.m index 7958991f6..801c64721 100644 --- a/Source/NSTableView.m +++ b/Source/NSTableView.m @@ -6,6 +6,14 @@ #include @implementation NSTableView ++ (void) initialize +{ + if (self == [NSTableView class]) + { + [self setVersion: 1]; + } +} + - (id)initWithFrame:(NSRect)frameRect { self = [super initWithFrame:frameRect]; diff --git a/Source/NSText.m b/Source/NSText.m index a6d90c662..7da48f05c 100644 --- a/Source/NSText.m +++ b/Source/NSText.m @@ -252,12 +252,14 @@ static NSRange MakeRangeFromAbs(int a1,int a2) // end: _GNUTextScanner implementation-------------------------------------- - +/* @interface NSAttributedString(DrawingAddition) -(NSSize) sizeRange:(NSRange) aRange; -(void) drawRange:(NSRange) aRange atPoint:(NSPoint) aPoint; +-(void) drawRange:(NSRange) aRange inRect:(NSRect) aRect; -(BOOL) areMultipleFontsInRange:(NSRange) aRange; @end +*/ @implementation NSAttributedString(DrawingAddition) -(NSSize) sizeRange:(NSRange) lineRange @@ -291,6 +293,16 @@ static NSRange MakeRangeFromAbs(int a1,int a2) } } +-(void) drawRange:(NSRange) aRange inRect:(NSRect) aRect; +{ + NSString *substring=[[self string] substringWithRange:aRange]; + + [substring drawInRect:aRect withAttributes:[NSDictionary dictionaryWithObjectsAndKeys: +[NSFont systemFontOfSize:12.0],NSFontAttributeName, +[NSColor blueColor],NSForegroundColorAttributeName, +nil]]; +} + -(BOOL) areMultipleFontsInRange:(NSRange) aRange { NSRange longestRange; [self attribute:NSFontAttributeName atIndex:aRange.location longestEffectiveRange:&longestRange inRange:aRange]; @@ -431,10 +443,15 @@ static NSRange MakeRangeFromAbs(int a1,int a2) [self setSelectedRange:NSMakeRange(0,0)]; return self; } + -(NSDictionary*) defaultTypingAttributes -{ return [NSDictionary dictionaryWithObjectsAndKeys: default_font,NSFontAttributeName, - text_color,NSForegroundColorAttributeName, - nil]; +{ + +return [NSDictionary dictionaryWithObjectsAndKeys: +default_font,NSFontAttributeName, +text_color,NSForegroundColorAttributeName, +nil]; + } /* diff --git a/Source/NSTextStorage.m b/Source/NSTextStorage.m index f07ace122..025596a3c 100644 --- a/Source/NSTextStorage.m +++ b/Source/NSTextStorage.m @@ -97,7 +97,10 @@ changeInLength: [attributedString length] - aRange.length]; - (void) addLayoutManager: (NSLayoutManager*)obj { if ([layoutManagers indexOfObjectIdenticalTo: obj] == NSNotFound) - [layoutManagers addObject: obj]; + { + [layoutManagers addObject: obj]; + [obj setTextStorage:self]; + } } - (void) removeLayoutManager: (NSLayoutManager*)obj diff --git a/Source/NSTextView.m b/Source/NSTextView.m index b5e1ba207..5bef93a43 100644 --- a/Source/NSTextView.m +++ b/Source/NSTextView.m @@ -58,6 +58,7 @@ textStorage = [[NSTextStorage alloc] init]; layoutManager = [[NSLayoutManager alloc] init]; + [textStorage addLayoutManager:layoutManager]; [layoutManager release]; @@ -141,7 +142,15 @@ - (void)setNeedsDisplayInRect:(NSRect)aRect avoidAdditionalLayout:(BOOL)flag { - // FIXME. +/* + NSRange glyphsToDraw = [layoutManager +glyphRangeForTextContainer:textContainer]; + + [self lockFocus]; + [layoutManager drawGlyphsForGlyphRange:glyphsToDraw + atPoint:[self frame].origin]; + [self unlockFocus]; +*/ } /* We override NSView's setNeedsDisplayInRect: */ @@ -284,7 +293,7 @@ aRect.size.height); - (void)setSelectedRange:(NSRange)charRange { - NSLog(@"setSelectedRange"); + NSLog(@"setSelectedRange (%d, %d)", charRange.location, charRange.length); /* [[NSNotificationCenter defaultCenter] postNotificationName:NSTextViewDidChangeSelectionNotification @@ -906,7 +915,15 @@ container, returning the modified location. */ initWithString: string attributes: [self typingAttributes]] autorelease]; - [textStorage insertAttributedString:aString atIndex:0]; +// [textStorage replaceRange:NSMakeRange(0, [string length]) +// withString:aString]; + + [textStorage setAttributedString: aString]; + +//replaceCharactersInRange:NSMakeRange(0, [string length]) +// withAttributedString: aString]; + +// [textStorage insertAttributedString:aString atIndex:0]; } -(void) setText:(NSString *)string {[self setString:string];} @@ -916,8 +933,8 @@ container, returning the modified location. */ NSLog(@"%@", aString); if (![aString isKindOfClass:[NSAttributedString class]]) - aString = [[[NSAttributedString alloc] initWithString:aString - attributes:[self typingAttributes]] autorelease]; + aString = [[NSAttributedString alloc] initWithString:aString + attributes:[self typingAttributes]]; [textStorage replaceCharactersInRange:[self selectedRange] withAttributedString:(NSAttributedString *)aString]; @@ -930,7 +947,14 @@ container, returning the modified location. */ - (void)drawRect:(NSRect)aRect { - [textStorage drawRange:[self selectedRange] atPoint:aRect.origin.x]; + if(tv_backGroundColor) + { + [tv_backGroundColor set]; + NSRectFill (aRect); + } + + [layoutManager drawGlyphsForGlyphRange:[layoutManager glyphRangeForTextContainer: textContainer] + atPoint: [self frame].origin]; } /*