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.