home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1994 June / NEBULA_SE.ISO / SourceCode / MiscKit / Palettes / MiscThreeStateButton / MiscThreeStateButton.subproj / MiscThreeStateButtonCell.m < prev    next >
Encoding:
Text File  |  1994-01-06  |  11.3 KB  |  409 lines

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