This is MiscClipTFCell.m in view mode; [Download] [Up]
// // MiscClipTextFieldCell.h -- a cell for displaying long string values // Written and Copyright (c) 1995 by Balazs Pataki. // Version 1.0. All rights reserved. // // This notice may not be removed from this source code. // // This object is included in the MiscKit by permission from the author // and its use is governed by the MiscKit license, found in the file // "LICENSE.rtf" in the MiscKit distribution. Please refer to that file // for a list of all applicable permissions and restrictions. // #import <appkit/appkit.h> #import <misckit/MiscString.h> #import <objc/objc-runtime.h> #import "MiscClipTextFieldCell.h" #define CLASS_NAME "MiscClipTextFieldCell" #define CLASS_VERSION 1 #define DEFAULTCLIPPER "..." #define DELIMITERS [delimiters stringValue] // To make texts displayed in cells looking good inside views, the maximum // width of a cell is 4.0 points (o whatever) less than the width of the view // the cell is displayed in. So, we need this little adjustment (am I right? // - maybe not but at least it works) #define CELL_ADJUSTMENT 4.0 #define BEZELED_ADJUSTMENT 5.0 #define BORDERED_ADJUSTMENT 4.0 #define FLUSH [[[control window] reenableFlushWindow] flushWindow]; @interface MiscClipTextFieldCell (Private) - _clipStringValue:(const char *)aString; - _copyClipperObj:obj fromZone:(NXZone *)zone; - _copyDelimitersObj:obj fromZone:(NXZone *)zone; - _copyFullStringObj:obj fromZone:(NXZone *)zone; @end /* ******************************** * * * MiscClipTextFieldCell * * * ******************************** */ @implementation MiscClipTextFieldCell + initialize // Set class version { if (self == objc_lookUpClass(CLASS_NAME)) { [self setVersion:CLASS_VERSION]; } return self; } - init; // Initializes a newly allocated MiscClipTextFieldCell with default values, // that is the clipper string to "...", uses no delimiters when clipping, and // clipping happens on the right. The cell's other attributes are set for // displaying text only (no scroll, selection allowed). { return [self initTextCell:""]; } - initTextCell:(const char*)aString; { [super initTextCell:aString]; clipper = [[MiscString allocFromZone:[self zone]] initString:DEFAULTCLIPPER]; fullString = [[MiscString allocFromZone:[self zone]] init]; delimiters = nil; /* As a deafult we don't have delimiters*/ clipOnRight = YES; /* and clipping happens on the right */ clipEnabled = YES; [self setDelegate:self]; // MiscClipField is initialized for only displaying its string value [self setBezeled:YES]; [self setEditable:NO]; [self setScrollable:NO]; [self setBackgroundGray:NX_LTGRAY]; return self; } - copyFromZone:(NXZone *)zone { id obj = [super copyFromZone:zone]; [obj _copyClipperObj:clipper fromZone:zone]; [obj _copyDelimitersObj:delimiters fromZone:zone]; [obj _copyFullStringObj:fullString fromZone:(NXZone *)zone]; return obj; } - free { [fullString free]; [clipper free]; if (delimiters) [delimiters free]; return [super free]; } - setDelegate:anObject { delegate = anObject; return self; } - setClipOnRight:(BOOL) flag // If flag is YES clipping happens on the right, otherwise on the left of the // string in the cell { clipOnRight = flag; return self; } - setClipperString:(const char*) aString // Sets `aString' as the string that is displayed in place of the clipped part // of the original string { if (clipper) [clipper setStringValue:aString]; else clipper = [MiscString newWithString:aString]; return self; } - setClipDelimiters:(const char*) delimChars // Sets `delimChars' as delimiters by which the clipping has to happen { if (delimiters) [delimiters setStringValue:delimChars]; else delimiters = [MiscString newWithString:delimChars]; return self; } - setClipEnabled:(BOOL) flag // Sets whether the next `setStringValue:' message should clip the text or not { clipEnabled = flag; return self; } - setStringValue:(const char *)aString { if ([fullString cmp:aString] != 0) [fullString setStringValue:aString]; if ( clipEnabled ) return [self _clipStringValue:aString]; return [super setStringValue:aString]; } - takeStringValueFrom:sender { [self setStringValue:[sender stringValue]]; return self; } - resetStringValue:sender { if ( clipEnabled ) return [self _clipStringValue:[self fullStringValue]]; return [super setStringValue:[self fullStringValue]]; } - (const char*) fullStringValue { return [fullString stringValue]; } - (BOOL) isClipEnabled { return clipEnabled; } - (BOOL) doesClipOnRight { return clipOnRight; } - clipper { return clipper; } - delimiters { return delimiters; } - delegate { return delegate; } - (BOOL) isWrapped // Returns yes if the Cell wraps the text by word { return ( cFlags2.noWrap ? NO : YES ); } - write:(NXTypedStream *)stream { [super write:stream]; NXWriteObject(stream, fullString); NXWriteObject(stream, clipper); NXWriteObject(stream, delimiters); NXWriteObject(stream, delegate); NXWriteType(stream, @encode(BOOL), &clipOnRight); NXWriteType(stream, @encode(BOOL), &clipEnabled); return self; } - read:(NXTypedStream *)stream { [super read:stream]; fullString = NXReadObject(stream); clipper = NXReadObject(stream); delimiters = NXReadObject(stream); delegate = NXReadObject(stream); NXReadType(stream, @encode(BOOL), &clipOnRight); NXReadType(stream, @encode(BOOL), &clipEnabled); return self; } - awake // Does nothing but with speed :-) { return self; } @end /* ******************************** * * * MiscClipTextField(Private) * * * * -- Private Methods -- * * * ******************************** */ @implementation MiscClipTextFieldCell (Private) - _clipStringValue:(const char *)aString // A very long story... // { id control = nil; id temp = nil; id mainFont = [self font]; id scrnFont = [mainFont screenFont]; id theFont = (scrnFont ? scrnFont : mainFont); // try to use screen font float clipperWidth = 0; float clippedWidth = 0; float cellWidth = 0; BOOL doClip = NO; BOOL usingDelimiters = NO; NXRect biggestRect; NXSize size; int i=0; if (!aString) return self; /* More initializing */ if (![clipper stringValue]) [clipper setStringValue:DEFAULTCLIPPER]; clipperWidth = [theFont getWidthOf:[clipper stringValue]]; temp = [[MiscString alloc] initString:aString]; usingDelimiters = ( delimiters && ![delimiters emptyString] ? YES : NO ); // Figure out the initial cell width (the biggest possible cell width) // [and do some necessary adjustment too] control = [self controlView]; /* Matrix will respond ... */ if ( [control respondsTo:@selector(getCellSize:)] ) { NXSize size; [control getCellSize:&size]; cellWidth = size.width; biggestRect.size.width = size.width; biggestRect.size.height = size.height; } else { [control getFrame:&biggestRect]; cellWidth = NX_WIDTH(&biggestRect); } cellWidth -= CELL_ADJUSTMENT; if ([self isBezeled]) cellWidth -= BEZELED_ADJUSTMENT; else if ([self isBordered]) cellWidth -= BORDERED_ADJUSTMENT; // Configuring the cell [self setSelectable:NO]; /* Disable selecting and scrolling */ [self setScrollable:NO]; [[control window] disableFlushWindow]; // Check whether we need to clip or not. If we do give delegate an // opportunity to clip the string in his custom way. If `temp's string // value is still long we do the real clipping. I know its not really // sexy doing this with a for loop but ... for (i=1; i<=2; i++) { doClip = NO; if ([theFont getWidthOf:[temp stringValue]] > cellWidth) doClip = YES; /* aString is too wide so do the clip */ else { /* Try the "trick" */ /* Set the clipped string value */ [super setStringValue:[temp stringValue]]; /* Calc how much space it needed to be */ /* displayed */ [self calcCellSize:&size inRect:&biggestRect]; /* It didn't fit so its broken into many*/ /* lines (ie: the cell got higher) */ if ((size.height > NX_HEIGHT(&biggestRect))) doClip = YES; /* Do the clip */ } if (i==1 && doClip) [delegate stringWillBeClipped:temp]; } // Clip on the right if `aString' is too long if ( doClip && clipOnRight ) { BOOL cutMore=NO; // This becomes true if text doesn't fits into // the cell in the first round /* The loop cuts characters from the end and checks */ /* wheter it's short enough to be displayed */ while (YES) { clippedWidth=[theFont getWidthOf:[temp stringValue]]; /* If the string is still too long */ if ( ((clippedWidth+clipperWidth) >= cellWidth) || cutMore) { [temp removeFrom:[temp length]-1 length:1]; if ( usingDelimiters ) { /* No delimiter found means we reached the */ /* last delimited string element and it */ /* can't be shortened anymore because there*/ /* is no "next" delimter backwards */ int from = [temp rspotOfChars:DELIMITERS]; if ( from == -1 ) [temp replace:[temp stringValue] with:""]; else [temp removeFrom:from+1 length:[temp length]-1]; } /* Last element reached and still long */ if ([temp emptyString]) { [temp setStringValue:[clipper stringValue]]; FLUSH; return [super setStringValue:[temp stringValueAndFree]]; } cutMore = NO; } //End if (still too long) else { [temp concatenate:clipper]; /* Set the clipped string value */ [super setStringValue:[temp stringValue]]; /* Calc how much space it needed to be */ /* displayed */ [self calcCellSize:&size inRect:&biggestRect]; /* It didn't fit so its broken into two */ /* lines (ie: the cell got higher) */ if ( size.height > NX_HEIGHT(&biggestRect) ){ cutMore = YES; /* Force cutting some more */ /* Remove clipper string from end */ [temp removeFrom:([temp length]-[clipper length]) length:[clipper length]]; } else { /* Finished, string value can be displyed */ [temp free]; FLUSH; return self; } } // End else (check if string value can really be displayed) } // End of string shortening loop }//End if (clipOnRight) // Clip on the left if `aString' is too long // (This code is nearly identical to the right clipping one, except that // it scans the string from left to right. Can you see any way to merge // these two in a meaningful way?) if ( doClip && !clipOnRight ) { BOOL cutMore=NO; // This becomes true if text doesn't fits into // the cell in the first round /* The loop cuts characters from the beg. of temp */ /* and checks wheter it's short enough to be */ /* displayed */ while ( YES ) { // This sometimes lies, maybe because of the screenfont // - printerfont differency clippedWidth=[theFont getWidthOf:[temp stringValue]]; /* If string is still too long or we are */ /* forced to cut more ... */ if ( ((clippedWidth+clipperWidth) >= cellWidth) || cutMore) { [temp removeFrom:0 length:1]; if ( usingDelimiters ) { int to=[temp spotOfChars:DELIMITERS]; /* No delimiters found means we reached the */ /* last delimited string element and it */ /* can't be shortened anymore because there*/ /* is no "next" delimter forward */ if (to == -1) [temp replace:[temp stringValue] with:""]; else [temp removeFrom:0 to:to-1]; } /* Last element reached and still long */ if ([temp emptyString]) { [temp setStringValue:[clipper stringValue]]; FLUSH; return [super setStringValue:[temp stringValueAndFree]]; } cutMore = NO; /* Turn forcing cutting more off */ } //Enf if (still too long) else { /* Insert clipper */ [temp insertString:clipper]; /* Set the clipped string value */ [super setStringValue:[temp stringValue]]; /* Calc how much space it needed to be */ /* displayed */ [self calcCellSize:&size inRect:&biggestRect]; if ( size.height > NX_HEIGHT(&biggestRect) ){ /* It didn't fit so its broken into many*/ /* lines (ie: the cell got higher) */ cutMore = YES; /* Force cutting some more */ /* Remove clipper string from front */ [temp removeFrom:0 length:[clipper length]]; } else { /* Finished, string value can be displyed*/ [temp free]; FLUSH; return self; } } // End else (check if string value can really be displayed) } // End of string shortening loop }//End if (clip on left) FLUSH; return [super setStringValue:[temp stringValueAndFree]]; } - _copyClipperObj:obj fromZone:(NXZone *)zone { id newClipper = [obj copyFromZone:zone]; clipper = newClipper; return self; } - _copyDelimitersObj:obj fromZone:(NXZone *)zone { id newDelim = [obj copyFromZone:zone]; delimiters = newDelim; return self; } - _copyFullStringObj:obj fromZone:(NXZone *)zone { id newString = [obj copyFromZone:zone]; fullString = newString; return self; } @end @implementation Object (MiscClipDelegate) - stringWillBeClipped:theString { return self; } @end @implementation MiscClipTextFieldCell(IBStuff) - (const char *)getInspectorClassName // Return the class name of our inspector. { return "MiscClipTextFieldInspector"; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.