home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / MiscKit1.7.1 / MiscKit / Palettes / MiscThreeStateButton / MiscThreeStateButton.subproj / MiscTSBCell.m < prev    next >
Encoding:
Text File  |  1995-04-13  |  11.7 KB  |  415 lines

  1. //
  2. //    MiscThreeStateButtonCell.m -- cell class for the three state button
  3. //        Written by Don Yacktman Copyright (c) 1993 by Don Yacktman.
  4. //                Version 1.0.  All rights reserved.
  5. //
  6. //        This notice may not be removed from this source code.
  7. //
  8. //        This is a free object!  Contact the author for the latest version.
  9. //        Don Yacktman, 4279 N. Ivy Lane, Provo, UT, 84604
  10. //        e-mail:  Don_Yacktman@byu.edu
  11. //
  12. //    This object is included in the MiscKit by permission from the author
  13. //    and its use is governed by the MiscKit license, found in the file
  14. //    "LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
  15. //    for a list of all applicable permissions and restrictions.
  16. //    
  17.  
  18. #import "MiscThreeStateButtonCell.h"
  19. #import "MiscThreeStateButton.h"
  20.  
  21. // This would be a hell of a lot easier with NeXT's source code.  Since
  22. // I don't have it, but I want to make sure that the same drawing mechanisms
  23. // are used to render the third state of the button, I do a nasty hack.
  24. // Whenever I want to go to the third state, I fake it and make the button
  25. // think it's in the alternate state (of a normal button) but whenever it
  26. // attempts to get the title or image, I swap in the third state button/image
  27. // before the render and then switch back the true alt image/title upon
  28. // finishing the render.  I use the same trick when the button is being queried
  29. // about it's sizes for drawing areas, etc.  I swap icons and titles in and get
  30. // the size they'd use, then I get the size of the correct icons, and then
  31. // return the MAX of the two.  Without knowing exactly how NeXT does the
  32. // calculations, I have to do a hack like this.  Since I'm essentially re-using
  33. // their code when I take my measurements and do my rendering, that means that
  34. // if it changes in the future, this class will follow the changes and keep
  35. // doing the right thing, whatever that is.  I still think it's ugly, but it is
  36. // a cute trick.
  37.  
  38. // As usual, ***** denotes an unfinished method.  This class is not at all
  39. // done.  It won't do what it is supposed to do yet, at all!
  40.  
  41. // Allowing NX_CONTENTS ("show alternate icon/title") as a highlighting mode
  42. // just creates a ton of problems, so it needs to be disallowed.  Below is the
  43. // set of allowable highlight modes to be ANDed with whatever highlight mode
  44. // is sent in, which should remove NX_CONTENTS as a possibility.   -Carl
  45. #define ALLOWABLE_HIGHLIGHTS (NX_NONE|NX_PUSHIN|NX_CHANGEGRAY|NX_CHANGEBACKGROUND)
  46.  
  47. @implementation MiscThreeStateButtonCell
  48.  
  49. - init
  50. {
  51.     id ret = [super init];
  52.     thirdTitle = NULL;
  53.     _thirdImage = nil;
  54.     isCyclic = YES;
  55.     thirdState = NO;
  56.  
  57.     altClicked = NO;        // Set these hacks correctly :-)  -Carl
  58.     dontIncrement = NO;
  59.  
  60.     // Disallow NX_CONTENTS as highlight mode  -Carl
  61.     [super setHighlightsBy:([self highlightsBy] & ALLOWABLE_HIGHLIGHTS)];
  62.     return ret;
  63. }
  64.  
  65. - copyFromZone:(NXZone *)zone
  66. {
  67.     id newClass = [super copyFromZone:zone];
  68.     // copy these two strings so they won't get freed twice or more...
  69.     thirdTitle =
  70.         NXCopyStringBufferFromZone([newClass thirdTitle], [self zone]);
  71.     trueAltTitle =
  72.         NXCopyStringBufferFromZone([newClass altTitle], [self zone]);
  73.     return newClass;
  74. }
  75.  
  76. - free
  77. {
  78.     if (thirdTitle) free(thirdTitle);
  79. //    [_thirdImage free];  // should I do this?
  80.     return [super free];
  81. }
  82.  
  83. - (const char *)altTitle { return trueAltTitle; }
  84.  
  85. - setAltTitle:(const char *)aString
  86. {
  87.     if (trueAltTitle) free(trueAltTitle);
  88.     trueAltTitle = NXCopyStringBufferFromZone(aString, [self zone]);
  89.     return self;
  90. }
  91.  
  92. - (const char *)altIcon { return [_trueAltImage name]; }
  93.  
  94. - setAltIcon:(const char *)iconName
  95. {
  96.     return [self setAltImage:[NXImage findImageNamed:iconName]];
  97. }
  98.  
  99. - altImage { return _trueAltImage; }
  100.  
  101. - setAltImage:image
  102. {
  103. //    [_trueAltImage free];  // should I do this?
  104.     _trueAltImage = image;
  105.     return self;
  106. }
  107.  
  108. - (const char *)thirdTitle { return thirdTitle; }
  109.  
  110. - setThirdTitle:(const char *)aString
  111. {
  112.     if (thirdTitle) free(thirdTitle);
  113.     thirdTitle = NXCopyStringBufferFromZone(aString, [self zone]);
  114.     return self;
  115. }
  116.  
  117. - (const char *)thirdIcon { return [_thirdImage name]; }
  118.  
  119. - setThirdIcon:(const char *)iconName
  120. {
  121.     return [self setThirdImage:[NXImage findImageNamed:iconName]];
  122. }
  123.  
  124. - thirdImage { return _thirdImage; }
  125.  
  126. - setThirdImage:image
  127. {
  128. //    [_thirdImage free];  // should I do this?
  129.     _thirdImage = image;
  130.     return self;
  131. }
  132.  
  133. - setType:(int)aType
  134. {
  135.     switch (aType) {
  136.         case MISC_CYCLIC_THREE_STATE: isCyclic = YES; break;
  137.         case MISC_PLAIN_THREE_STATE:  isCyclic = NO;  break;
  138.         default: return [super setType:aType];
  139.     }
  140.     return self;
  141. }
  142.  
  143. - (BOOL)isCyclic { return isCyclic; }
  144.  
  145. - (const char *)stringValue
  146. {
  147.     if (thirdState) return "alt";
  148.     return [super stringValue];
  149. }
  150.  
  151. - setStringValue:(const char *)aString
  152. {
  153.     thirdState = NO;
  154.     if (aString) if (strlen(aString)) thirdState = YES;
  155.     return [super setStringValue:aString];
  156. }
  157.  
  158. - setStringValueNoCopy:(const char *)aString
  159. {
  160.     thirdState = NO;
  161.     if (aString) if (strlen(aString)) thirdState = YES;
  162.     return [super setStringValueNoCopy:aString];
  163. }
  164.  
  165. - (int)intValue
  166. {
  167.     if (thirdState) return 2;
  168.     return [super intValue];
  169. }
  170.  
  171. - setIntValue:(int)anInt
  172. {
  173.     if (anInt == 2) thirdState = YES;
  174.     else thirdState = NO;
  175.     return [super setIntValue:anInt];
  176. }
  177.  
  178. - (float)floatValue
  179. {
  180.     if (thirdState) return 2.0;
  181.     return [super floatValue];
  182. }
  183.  
  184. - setFloatValue:(float)aFloat
  185. {
  186.     if ((aFloat > 1.0) && (aFloat <= 2.0)) thirdState = YES;
  187.     else thirdState = NO;
  188.     return [super setFloatValue:aFloat];
  189. }
  190.  
  191. - (double)doubleValue
  192. {
  193.     if (thirdState) return 2.0;
  194.     return [super doubleValue];
  195. }
  196.  
  197. - setDoubleValue:(double)aDouble
  198. {
  199.     if ((aDouble > 1.0) && (aDouble <= 2.0)) thirdState = YES;
  200.     else thirdState = NO;
  201.     return [super setDoubleValue:aDouble];
  202. }
  203.  
  204. // Here's where the fun begins with us intercepting and fooling the button.
  205. - getDrawRect:(NXRect *)theRect
  206. {    // perform the super method for both alt and third state, then merge rects
  207.     // Note, this may not really be necessary, since the NeXT implementation
  208.     // may use the two methods below to give the answer to this method.  If
  209.     // that's the case, then this method doesn't need the override, since the
  210.     // two methods below take the third state into account.  If they don't we
  211.     // need this method.  The docs don't say one way or the other, and I don't
  212.     // feel like disassembling the Appkit, so at worst, we'll be inefficient
  213.     // here.  This is a good reason for why NeXT should let us look at source,
  214.     // or make MAJOR improvements in the docs!
  215.     NXRect tempRect  = { { 0.0, 0.0 }, { 0.0, 0.0 } };
  216.     icon.bmap.alternate = _thirdImage;
  217.     [self replaceAltTitle:thirdTitle];
  218.     [super getDrawRect:&tempRect];
  219.     [self replaceAltTitle:trueAltTitle];
  220.     icon.bmap.alternate = _trueAltImage;
  221.     [super getDrawRect:theRect];
  222.     if (thirdState) {
  223.         icon.bmap.alternate = _thirdImage;
  224.         [self replaceAltTitle:thirdTitle];
  225.     }
  226.     NXUnionRect(&tempRect, theRect);
  227.     return self;
  228. }
  229.  
  230. - getTitleRect:(NXRect *)theRect
  231. { // perform the super method for both alt and third state, then merge rects
  232.     NXRect tempRect  = { { 0.0, 0.0 }, { 0.0, 0.0 } };
  233.     icon.bmap.alternate = _thirdImage;
  234.     [self replaceAltTitle:thirdTitle];
  235.     [super getTitleRect:&tempRect];
  236.     [self replaceAltTitle:trueAltTitle];
  237.     icon.bmap.alternate = _trueAltImage;
  238.     [super getTitleRect:theRect];
  239.     if (thirdState) {
  240.         icon.bmap.alternate = _thirdImage;
  241.         [self replaceAltTitle:thirdTitle];
  242.     }
  243.     NXUnionRect(&tempRect, theRect);
  244.     return self;
  245. }
  246.  
  247. - getIconRect:(NXRect *)theRect
  248. { // perform the super method for both alt and third state, then merge rects
  249.     NXRect tempRect  = { { 0.0, 0.0 }, { 0.0, 0.0 } };
  250.     icon.bmap.alternate = _thirdImage;
  251.     [self replaceAltTitle:thirdTitle];
  252.     [super getIconRect:&tempRect];
  253.     [self replaceAltTitle:trueAltTitle];
  254.     icon.bmap.alternate = _trueAltImage;
  255.     [super getIconRect:theRect];
  256.     if (thirdState) {
  257.         icon.bmap.alternate = _thirdImage;
  258.         [self replaceAltTitle:thirdTitle];
  259.     }
  260.     NXUnionRect(&tempRect, theRect);
  261.     return self;
  262. }
  263.  
  264.  
  265. - calcCellSize:(NXSize *)theSize inRect:(const NXRect *)aRect
  266. {
  267.     NXSize tempSize  = { 0.0, 0.0 };
  268.     icon.bmap.alternate = _thirdImage;
  269.     [self replaceAltTitle:thirdTitle];
  270.     [super calcCellSize:&tempSize inRect:aRect];
  271.     [self replaceAltTitle:trueAltTitle];
  272.     icon.bmap.alternate = _trueAltImage;
  273.     [super calcCellSize:theSize inRect:aRect];
  274.     if (thirdState) {
  275.         icon.bmap.alternate = _thirdImage;
  276.         [self replaceAltTitle:thirdTitle];
  277.     }
  278.     theSize->width  = MAX(theSize->width,  tempSize.width);
  279.     theSize->height = MAX(theSize->height, tempSize.height);
  280.     return self;
  281. }
  282.  
  283. - drawInside:(const NXRect *)aRect inView:controlView
  284. {
  285.     if (thirdState) {
  286.         icon.bmap.alternate = _thirdImage;
  287.         [self replaceAltTitle:thirdTitle];
  288.     } else {
  289.         [self replaceAltTitle:trueAltTitle];
  290.         icon.bmap.alternate = _trueAltImage;
  291.     }
  292.     return [super drawInside:aRect inView:controlView];
  293. }
  294.  
  295. - highlight:(const NXRect *)cellFrame inView:controlView lit:(BOOL)flag
  296. {
  297.     if (thirdState) {
  298.         icon.bmap.alternate = _thirdImage;
  299.         [self replaceAltTitle:thirdTitle];
  300.     } else {
  301.         [self replaceAltTitle:trueAltTitle];
  302.         icon.bmap.alternate = _trueAltImage;
  303.     }
  304.     return [super highlight:cellFrame inView:controlView lit:flag];
  305. }
  306.  
  307. #define ALTKEY (theEvent->flags & NX_ALTERNATEMASK)
  308.  
  309. - (BOOL)trackMouse:(NXEvent *)theEvent inRect:(const NXRect *)cellFrame
  310.         ofView:controlView
  311. {
  312.     altClicked = (ALTKEY)? YES:NO;
  313.     return [super trackMouse:theEvent inRect:cellFrame ofView:controlView];
  314. }
  315.  
  316. - replaceTitle:(const char *)aString
  317. {
  318.     if (contents) free(contents);
  319.     contents = NXCopyStringBufferFromZone(aString, [self zone]);
  320.     return self;
  321. }
  322.  
  323. - replaceAltTitle:(const char *)aString
  324. {
  325.     if (altContents) free(altContents);
  326.     altContents = NXCopyStringBufferFromZone(aString, [self zone]);
  327.     return self;
  328. }
  329.  
  330. - performClick:sender
  331. {
  332.     return [super performClick:sender];
  333. }
  334.  
  335. - performAltClick:sender
  336. {
  337.     altClicked = YES;
  338.  
  339.     // This is a total hack...  For some reason, whenever -performClick
  340.     // or -performAltClick was called and the current state was 0,
  341.     // -incrementState was called *twice*.  This did not happen when using
  342.     // the mouse, nor did it happen when state was 1 or 2.
  343.     // This did not seem to affect -performClick -- I have no idea why --
  344.     // but this was a problem with -performAltClick.  Therefore, I ask
  345.     // -incrementState to not execute on the first call to it after this
  346.     // method by way of the special case ivar, 'dontIncrement'.   -Carl
  347.     if ([self intValue] == 0) dontIncrement = YES;
  348.  
  349.     return [super performClick:sender];
  350. }
  351.  
  352. - write:(NXTypedStream *)stream
  353. {
  354.     id ret = [super write:stream];
  355.     NXWriteTypes(stream, "**cc", &thirdTitle, &trueAltTitle,
  356.             &isCyclic, &thirdState);
  357.     NXWriteObject(stream, _thirdImage);
  358.     NXWriteObject(stream, _trueAltImage);
  359.     return ret;
  360.     // altClicked and dontIncrement don't really need to be
  361.     // written... they are always NO at this point.  See -read.
  362. }
  363.  
  364. - read:(NXTypedStream *)stream
  365. {
  366.     id ret = [super read:stream];
  367.     NXReadTypes(stream, "**cc", &thirdTitle, &trueAltTitle,
  368.             &isCyclic, &thirdState);
  369.     _thirdImage = NXReadObject(stream);
  370.     _trueAltImage =  NXReadObject(stream);
  371.  
  372.     // Disallow NX_CONTENTS as highlight mode  -Carl
  373.     [super setHighlightsBy:([self highlightsBy] & ALLOWABLE_HIGHLIGHTS)];
  374.  
  375.     // The following two ivars are used to inform -incrementState
  376.     // of a couple things.  They are only YES for very brief periods
  377.     // of time, so they really don't need to be archived in -write.
  378.     // Someone would have to be rather diabolical to manage to have
  379.     // -write called when they were YES... :-)   -Carl
  380.     altClicked = NO;
  381.     dontIncrement = NO;
  382.     return ret;
  383. }
  384.  
  385. - incrementState
  386. {
  387.     int next = 0;
  388.     int curState = [self intValue];
  389.     
  390.     if (dontIncrement) {  //see note in -performAltClick
  391.       dontIncrement = NO;
  392.       return self;
  393.     }
  394.     switch (curState) {
  395.         case 0 : next = 1; break;
  396.         case 1 : if (isCyclic) next = 2;
  397.                 else next = 0;
  398.              break;
  399.         case 2 : next = 0; break;
  400.         default: break;
  401.     }
  402.     if (altClicked && (curState < 2)) next = 2;
  403.     [self setIntValue:next];  //disable flushWindow around this??
  404.     altClicked = NO;
  405.   return self;
  406. }
  407.  
  408. - setHighlightsBy:(int)aType;
  409. {
  410.     // Disallow NX_CONTENTS as highlight mode  -Carl
  411.     return [super setHighlightsBy:(aType & ALLOWABLE_HIGHLIGHTS)];
  412. }
  413.  
  414. @end
  415.