This is MiscTSBCell.m in view mode; [Download] [Up]
// // MiscThreeStateButtonCell.m -- cell class for the three state button // Written by Don Yacktman Copyright (c) 1993 by Don Yacktman. // Version 1.0. All rights reserved. // // This notice may not be removed from this source code. // // This is a free object! Contact the author for the latest version. // Don Yacktman, 4279 N. Ivy Lane, Provo, UT, 84604 // e-mail: Don_Yacktman@byu.edu // // 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 "MiscThreeStateButtonCell.h" #import "MiscThreeStateButton.h" // This would be a hell of a lot easier with NeXT's source code. Since // I don't have it, but I want to make sure that the same drawing mechanisms // are used to render the third state of the button, I do a nasty hack. // Whenever I want to go to the third state, I fake it and make the button // think it's in the alternate state (of a normal button) but whenever it // attempts to get the title or image, I swap in the third state button/image // before the render and then switch back the true alt image/title upon // finishing the render. I use the same trick when the button is being queried // about it's sizes for drawing areas, etc. I swap icons and titles in and get // the size they'd use, then I get the size of the correct icons, and then // return the MAX of the two. Without knowing exactly how NeXT does the // calculations, I have to do a hack like this. Since I'm essentially re-using // their code when I take my measurements and do my rendering, that means that // if it changes in the future, this class will follow the changes and keep // doing the right thing, whatever that is. I still think it's ugly, but it is // a cute trick. // As usual, ***** denotes an unfinished method. This class is not at all // done. It won't do what it is supposed to do yet, at all! // Allowing NX_CONTENTS ("show alternate icon/title") as a highlighting mode // just creates a ton of problems, so it needs to be disallowed. Below is the // set of allowable highlight modes to be ANDed with whatever highlight mode // is sent in, which should remove NX_CONTENTS as a possibility. -Carl #define ALLOWABLE_HIGHLIGHTS (NX_NONE|NX_PUSHIN|NX_CHANGEGRAY|NX_CHANGEBACKGROUND) @implementation MiscThreeStateButtonCell - init { id ret = [super init]; thirdTitle = NULL; _thirdImage = nil; isCyclic = YES; thirdState = NO; altClicked = NO; // Set these hacks correctly :-) -Carl dontIncrement = NO; // Disallow NX_CONTENTS as highlight mode -Carl [super setHighlightsBy:([self highlightsBy] & ALLOWABLE_HIGHLIGHTS)]; return ret; } - copyFromZone:(NXZone *)zone { id newClass = [super copyFromZone:zone]; // copy these two strings so they won't get freed twice or more... thirdTitle = NXCopyStringBufferFromZone([newClass thirdTitle], [self zone]); trueAltTitle = NXCopyStringBufferFromZone([newClass altTitle], [self zone]); return newClass; } - free { if (thirdTitle) free(thirdTitle); // [_thirdImage free]; // should I do this? return [super free]; } - (const char *)altTitle { return trueAltTitle; } - setAltTitle:(const char *)aString { if (trueAltTitle) free(trueAltTitle); trueAltTitle = NXCopyStringBufferFromZone(aString, [self zone]); return self; } - (const char *)altIcon { return [_trueAltImage name]; } - setAltIcon:(const char *)iconName { return [self setAltImage:[NXImage findImageNamed:iconName]]; } - altImage { return _trueAltImage; } - setAltImage:image { // [_trueAltImage free]; // should I do this? _trueAltImage = image; return self; } - (const char *)thirdTitle { return thirdTitle; } - setThirdTitle:(const char *)aString { if (thirdTitle) free(thirdTitle); thirdTitle = NXCopyStringBufferFromZone(aString, [self zone]); return self; } - (const char *)thirdIcon { return [_thirdImage name]; } - setThirdIcon:(const char *)iconName { return [self setThirdImage:[NXImage findImageNamed:iconName]]; } - thirdImage { return _thirdImage; } - setThirdImage:image { // [_thirdImage free]; // should I do this? _thirdImage = image; return self; } - setType:(int)aType { switch (aType) { case MISC_CYCLIC_THREE_STATE: isCyclic = YES; break; case MISC_PLAIN_THREE_STATE: isCyclic = NO; break; default: return [super setType:aType]; } return self; } - (BOOL)isCyclic { return isCyclic; } - (const char *)stringValue { if (thirdState) return "alt"; return [super stringValue]; } - setStringValue:(const char *)aString { thirdState = NO; if (aString) if (strlen(aString)) thirdState = YES; return [super setStringValue:aString]; } - setStringValueNoCopy:(const char *)aString { thirdState = NO; if (aString) if (strlen(aString)) thirdState = YES; return [super setStringValueNoCopy:aString]; } - (int)intValue { if (thirdState) return 2; return [super intValue]; } - setIntValue:(int)anInt { if (anInt == 2) thirdState = YES; else thirdState = NO; return [super setIntValue:anInt]; } - (float)floatValue { if (thirdState) return 2.0; return [super floatValue]; } - setFloatValue:(float)aFloat { if ((aFloat > 1.0) && (aFloat <= 2.0)) thirdState = YES; else thirdState = NO; return [super setFloatValue:aFloat]; } - (double)doubleValue { if (thirdState) return 2.0; return [super doubleValue]; } - setDoubleValue:(double)aDouble { if ((aDouble > 1.0) && (aDouble <= 2.0)) thirdState = YES; else thirdState = NO; return [super setDoubleValue:aDouble]; } // Here's where the fun begins with us intercepting and fooling the button. - getDrawRect:(NXRect *)theRect { // perform the super method for both alt and third state, then merge rects // Note, this may not really be necessary, since the NeXT implementation // may use the two methods below to give the answer to this method. If // that's the case, then this method doesn't need the override, since the // two methods below take the third state into account. If they don't we // need this method. The docs don't say one way or the other, and I don't // feel like disassembling the Appkit, so at worst, we'll be inefficient // here. This is a good reason for why NeXT should let us look at source, // or make MAJOR improvements in the docs! NXRect tempRect = { { 0.0, 0.0 }, { 0.0, 0.0 } }; icon.bmap.alternate = _thirdImage; [self replaceAltTitle:thirdTitle]; [super getDrawRect:&tempRect]; [self replaceAltTitle:trueAltTitle]; icon.bmap.alternate = _trueAltImage; [super getDrawRect:theRect]; if (thirdState) { icon.bmap.alternate = _thirdImage; [self replaceAltTitle:thirdTitle]; } NXUnionRect(&tempRect, theRect); return self; } - getTitleRect:(NXRect *)theRect { // perform the super method for both alt and third state, then merge rects NXRect tempRect = { { 0.0, 0.0 }, { 0.0, 0.0 } }; icon.bmap.alternate = _thirdImage; [self replaceAltTitle:thirdTitle]; [super getTitleRect:&tempRect]; [self replaceAltTitle:trueAltTitle]; icon.bmap.alternate = _trueAltImage; [super getTitleRect:theRect]; if (thirdState) { icon.bmap.alternate = _thirdImage; [self replaceAltTitle:thirdTitle]; } NXUnionRect(&tempRect, theRect); return self; } - getIconRect:(NXRect *)theRect { // perform the super method for both alt and third state, then merge rects NXRect tempRect = { { 0.0, 0.0 }, { 0.0, 0.0 } }; icon.bmap.alternate = _thirdImage; [self replaceAltTitle:thirdTitle]; [super getIconRect:&tempRect]; [self replaceAltTitle:trueAltTitle]; icon.bmap.alternate = _trueAltImage; [super getIconRect:theRect]; if (thirdState) { icon.bmap.alternate = _thirdImage; [self replaceAltTitle:thirdTitle]; } NXUnionRect(&tempRect, theRect); return self; } - calcCellSize:(NXSize *)theSize inRect:(const NXRect *)aRect { NXSize tempSize = { 0.0, 0.0 }; icon.bmap.alternate = _thirdImage; [self replaceAltTitle:thirdTitle]; [super calcCellSize:&tempSize inRect:aRect]; [self replaceAltTitle:trueAltTitle]; icon.bmap.alternate = _trueAltImage; [super calcCellSize:theSize inRect:aRect]; if (thirdState) { icon.bmap.alternate = _thirdImage; [self replaceAltTitle:thirdTitle]; } theSize->width = MAX(theSize->width, tempSize.width); theSize->height = MAX(theSize->height, tempSize.height); return self; } - drawInside:(const NXRect *)aRect inView:controlView { if (thirdState) { icon.bmap.alternate = _thirdImage; [self replaceAltTitle:thirdTitle]; } else { [self replaceAltTitle:trueAltTitle]; icon.bmap.alternate = _trueAltImage; } return [super drawInside:aRect inView:controlView]; } - highlight:(const NXRect *)cellFrame inView:controlView lit:(BOOL)flag { if (thirdState) { icon.bmap.alternate = _thirdImage; [self replaceAltTitle:thirdTitle]; } else { [self replaceAltTitle:trueAltTitle]; icon.bmap.alternate = _trueAltImage; } return [super highlight:cellFrame inView:controlView lit:flag]; } #define ALTKEY (theEvent->flags & NX_ALTERNATEMASK) - (BOOL)trackMouse:(NXEvent *)theEvent inRect:(const NXRect *)cellFrame ofView:controlView { altClicked = (ALTKEY)? YES:NO; return [super trackMouse:theEvent inRect:cellFrame ofView:controlView]; } - replaceTitle:(const char *)aString { if (contents) free(contents); contents = NXCopyStringBufferFromZone(aString, [self zone]); return self; } - replaceAltTitle:(const char *)aString { if (altContents) free(altContents); altContents = NXCopyStringBufferFromZone(aString, [self zone]); return self; } - performClick:sender { return [super performClick:sender]; } - performAltClick:sender { altClicked = YES; // This is a total hack... For some reason, whenever -performClick // or -performAltClick was called and the current state was 0, // -incrementState was called *twice*. This did not happen when using // the mouse, nor did it happen when state was 1 or 2. // This did not seem to affect -performClick -- I have no idea why -- // but this was a problem with -performAltClick. Therefore, I ask // -incrementState to not execute on the first call to it after this // method by way of the special case ivar, 'dontIncrement'. -Carl if ([self intValue] == 0) dontIncrement = YES; return [super performClick:sender]; } - write:(NXTypedStream *)stream { id ret = [super write:stream]; NXWriteTypes(stream, "**cc", &thirdTitle, &trueAltTitle, &isCyclic, &thirdState); NXWriteObject(stream, _thirdImage); NXWriteObject(stream, _trueAltImage); return ret; // altClicked and dontIncrement don't really need to be // written... they are always NO at this point. See -read. } - read:(NXTypedStream *)stream { id ret = [super read:stream]; NXReadTypes(stream, "**cc", &thirdTitle, &trueAltTitle, &isCyclic, &thirdState); _thirdImage = NXReadObject(stream); _trueAltImage = NXReadObject(stream); // Disallow NX_CONTENTS as highlight mode -Carl [super setHighlightsBy:([self highlightsBy] & ALLOWABLE_HIGHLIGHTS)]; // The following two ivars are used to inform -incrementState // of a couple things. They are only YES for very brief periods // of time, so they really don't need to be archived in -write. // Someone would have to be rather diabolical to manage to have // -write called when they were YES... :-) -Carl altClicked = NO; dontIncrement = NO; return ret; } - incrementState { int next = 0; int curState = [self intValue]; if (dontIncrement) { //see note in -performAltClick dontIncrement = NO; return self; } switch (curState) { case 0 : next = 1; break; case 1 : if (isCyclic) next = 2; else next = 0; break; case 2 : next = 0; break; default: break; } if (altClicked && (curState < 2)) next = 2; [self setIntValue:next]; //disable flushWindow around this?? altClicked = NO; return self; } - setHighlightsBy:(int)aType; { // Disallow NX_CONTENTS as highlight mode -Carl return [super setHighlightsBy:(aType & ALLOWABLE_HIGHLIGHTS)]; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.