This is MiscArrowButtonCell.m in view mode; [Download] [Up]
/* MiscArrowButtonCell.m 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 "wraps.h" #import "MiscArrowButtonCell.h" // These #defines are used for versioning instances of this class, so that // you will always be able to read any previously archived version. If you // make changes to the class's ivars, bump the version number up one and // make the appropriate changes to the initWithCoder: method. I left the // version number the same as it's pre-OpenStep days because no ivars // changed. It's been tested and it will read nibs with pre-OpenStep // MiscArrowButton instances in them. #define MISC_ABC_VERSION 1 #define MISC_ABC_CLASSNAME "MiscArrowButtonCell" // Hack to stop the compiler from complaining. I used to import // <InterfaceBuilder/InterfaceBuilder.h> but that's not good either. // This is used is drawWithFrame:inView:. @interface NSApplication (StopComplainingCompiler) - (BOOL) isTestingInterface; @end @implementation MiscArrowButtonCell /*" With the help of MiscArrowButton, this is a recreation of the button (well, it was a View there) from Websters.app (in the Preferences panel). It's basically made to be used off of it's palette but you could always use it programmatically if you so choose. There's just a few differences from using an NSButtonCell: I created a new designated initializer #initTextCell:alternateTitle:, since it's convenient to be able to set both sides of the button at once. Also, since stringValue does not return anything useful (the string "0" or "1" depending upon it's current state), I made it return the the selected title instead. "*/ + (void) initialize /*" Sets our class version for archiving purposes. "*/ { if (self == [MiscArrowButtonCell class]) { [self setVersion:MISC_ABC_VERSION]; } } - (id) init /*" Calls our designated initializer (#initTextCell:alternateTitle:) with default titles ("Left" and "Right"). "*/ { return [self initTextCell:@"Left" alternateTitle:@"Right"]; } - (id) initTextCell:(NSString*)aString /*" Calls our designated initializer. aString will become our left title. The right title will be set to "Right". "*/ { return [self initTextCell:aString alternateTitle:@"Right"]; } - (id) initTextCell:(NSString*)aString alternateTitle:(NSString*)altString /*" Our designated initializer. Sets both our title and alternate title. "*/ { BOOL error = ([super initTextCell:aString] == nil); if (!error) { [self setAlternateTitle:altString]; [self setState:0]; [self setBordered:NO]; [self setShowsStateBy:NSNoCellMask]; // We don't want to highlight ourself. [self setHighlightsBy:NSNoCellMask]; [self setArrowAlignment:MiscArrowAlignmentAbsolute]; // We should probably override type to always be toggle. [self setType:NSToggleButton]; } return error ? nil : self; } - (NSString*) stringValue /*" Since #stringValue in Button does not do much but return a string with a "0" or "1" depending upon our state, I overrode it to return the currently selected title. "*/ { return ([self state] == 0) ? [self title] : [self alternateTitle]; } - (MiscArrowAlignment) arrowAlignment /*" Returns MiscArrowAlignmentAbsolute or MiscArrowAlignmentRelative depending upon how alignment is currently being handled. See #setArrowAlignment: for more details. "*/ { return arrowAlignment; } - (void) setArrowAlignment:(MiscArrowAlignment)alignment /*" Sets the alignment of the arrow. MiscArrowAlignmentAbsolute aligns the arrow in the center of the cellFrame, where MiscArrowAlignmentRelative centers the arrow between the left and right titles. "*/ { arrowAlignment = alignment; } - (NSSize) cellSizeForBounds:(NSRect)theRect /*" I believe this method is supposed to calculate the minimum size needed to fit the currently displayed contents (contents, altContents, and the arrow) in the cellframe. "*/ { NSSize theSize; float contentsWidth = [[self font] widthOfString:[self title]]; float altContentsWidth = [[self font] widthOfString:[self alternateTitle]]; // first calculate the width needed to draw all text and the arrow theSize.width = 0.0; if ([self arrowAlignment] == MiscArrowAlignmentRelative) { theSize.width += contentsWidth; theSize.width += altContentsWidth; } else { // absolute alignment if (contentsWidth > altContentsWidth) { theSize.width += contentsWidth * 2.0; } else { theSize.width += altContentsWidth * 2.0; } } theSize.width += cellHeight + 10.0; // now the height (which will usually be cellHeight) theSize.height = cellHeight; if ([ [self font] pointSize] > cellHeight) { theSize.height = [ [self font] pointSize]; } return theSize; } - (NSRect) titleRectForBounds:(NSRect)theRect /*" As far as I can tell (which may not be all that far) is that this method is only used by the NSButton's IBEditor to tell how large the editor should be. "*/ { float size = [ [self font] pointSize]; float theY; float maxWidth = theRect.size.width/2.0; // this is a hack so you can double click on the altTitle in IB if ([self state] == 1) { return [self alternateTitleRectForBounds:theRect]; } [self setAlignment:NSLeftTextAlignment]; // used for the IBEditor theY = theRect.origin.y + theRect.size.height/2.0 - size/2.0; if ([self arrowAlignment] == MiscArrowAlignmentRelative) { maxWidth = [[self font] widthOfString:[self title]] + 10.0; } theRect = NSMakeRect(theRect.origin.x, theY-2.0, maxWidth, size+2.0); return theRect; } - (NSRect) alternateTitleRectForBounds:(NSRect)theRect /*" If the state is 1 (altContents selected), then this method will be called. It returns the location for editor to appear when editing the altContents. NOTE: This stopped working with OpenStep for Mach Prerelease 2. The button's IBEditors have changed. "*/ { float size = [[self font] pointSize]; float newX; float newY; float newWidth; [self setAlignment:NSRightTextAlignment]; // used for the IBEditor newX = theRect.origin.x + theRect.size.width/2.0; newY = theRect.origin.y + theRect.size.height/2.0 - size/2.0; newWidth = theRect.size.width/2.0; return NSMakeRect(newX, newY-2.0, newWidth, size+2.0); } - (void) drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView /*" This method is used to draw only the parts of the cell that do change when the state changes. Therefore, only the black arrow and the text are drawn here. "*/ { float gray; float size = [ [self font] pointSize]; // draw the arrow if ([self arrowAlignment] == MiscArrowAlignmentRelative) { NSRect relativeFrame; float contentsWidth = [[self font] widthOfString:[self title]]; float altContentsWidth = [[self font] widthOfString:[self alternateTitle]]; // since it the arrow is drawn relative to the text, a little // calculation is needed relativeFrame.origin.x = cellFrame.origin.x + contentsWidth; relativeFrame.size.width = cellFrame.size.width - (contentsWidth + altContentsWidth); PSABdrawarrow (relativeFrame.origin.x, cellFrame.origin.y + 3.0, relativeFrame.size.width, cellFrame.size.height - 6.0, (int)[self state], [self isEnabled]); } // draw the arrow in the center of the cellFrame else { PSABdrawarrow (cellFrame.origin.x, cellFrame.origin.y + 3.0, cellFrame.size.width, cellFrame.size.height - 6.0, (int)[self state], [self isEnabled]); } // draw the left and right hand text [[self font] set]; if ([self title] != nil) { float theY; gray = ([self state] || ![self isEnabled]) ? NSDarkGray : 0.0; // calculate the placement of the contents and print it theY = cellFrame.origin.y + cellFrame.size.height/2.0 + size/3.0; PSABshowstring (cellFrame.origin.x + 3.0, theY, gray, [[self title] cString]); } if ([self alternateTitle] != nil) { float theX, theY; float strWidth = [[self font] widthOfString:[self alternateTitle]]; gray = ([self state] && [self isEnabled]) ? 0.0 : NSDarkGray; // calculate the placement of the altContents and print it theX = cellFrame.origin.x+(cellFrame.size.width - strWidth - 3.0); theY = cellFrame.origin.y + cellFrame.size.height/2.0 + size/3.0; PSABshowstring (theX, theY, gray, [[self alternateTitle] cString]); } } - (void) drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView /*" This part of the drawing displays only the parts that don't change often, which includes the diamond that encloses the arrow, and the border when I get around to adding one. "*/ { // if transparent draw the background white (when in IB) and // same as the background when in an app (or testing interface) if ([self isTransparent]) { if ([NSApp respondsToSelector:@selector(isTestingInterface)]) { if ([NSApp isTestingInterface]) { PSsetgray (NSLightGray); } else { PSsetgray (1.0); } } else { PSsetgray (NSLightGray); } NSRectFill(cellFrame); return; } // set cellHeight since some of the calculations that are in other // methods have to know the height of the cell (this is probably the // wrong way to go about this) cellHeight = cellFrame.size.height; // draw the border or bezel, then call drawInside if ([self isBordered]) { NSDrawButton(cellFrame , cellFrame); } PSgsave(); // have to flip drawing if we are drawing into a flipped view if ([controlView isFlipped]) { PSABflipme (cellFrame.size.height + (cellFrame.origin.y * 2.0)); } if ([self arrowAlignment] == MiscArrowAlignmentRelative) { NSRect relativeFrame; float contentsWidth = [[self font] widthOfString:[self title]]; float altContentsWidth = [[self font] widthOfString:[self alternateTitle]]; relativeFrame.origin.x = cellFrame.origin.x + contentsWidth; relativeFrame.size.width = cellFrame.size.width - (contentsWidth + altContentsWidth); PSABdrawdiamond (relativeFrame.origin.x, cellFrame.origin.y + 3.0, relativeFrame.size.width, cellFrame.size.height - 6.0); } else { PSABdrawdiamond (cellFrame.origin.x, cellFrame.origin.y + 3.0, cellFrame.size.width, cellFrame.size.height - 6.0); } PSgrestore(); // draw the rest of the cell [self drawInteriorWithFrame:cellFrame inView:controlView]; } // Archiving methods - (id) initWithCoder:(NSCoder *)aDecoder /*" With luck this will still unarchive pre-OpenStep palettized code. "*/ { int version; [super initWithCoder:aDecoder]; version = [aDecoder versionForClassName:[NSString stringWithCString:MISC_ABC_CLASSNAME]]; switch (version) { case 0: // First version had no ivars, but set any new ivars to a useable // value. arrowAlignment = MiscArrowAlignmentAbsolute; break; case 1: // Current version. [aDecoder decodeValueOfObjCType:@encode(unsigned int) at:&arrowAlignment]; break; default: break; } return self; } - (void) encodeWithCoder:(NSCoder *)aCoder /*" Encodes our instance variables. "*/ { [super encodeWithCoder:aCoder]; // You can also keep versioning here just in case you would like // to back to an older version, but I don't. [aCoder encodeValueOfObjCType:@encode(unsigned int) at:&arrowAlignment]; } @end /****************************************************************** CHANGES: 1. Now use setArrowAlignment/arrowAlignment to set/get whether the the arrow is relative or absolutely aligned. Added arrowAlignment instance var. October 1, 1994: 2. Added archive versioning (+initialize). May 16, 1996 3. Converted to OpenStep and cleaned up somewhat. August 4, 1996 4. Cleaned up a little more and touched up the autodoc comments. *******************************************************************/
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.