home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2001 / MacHack 2001.toast / pc / The Hacks / HelloTree / main.c < prev   
Encoding:
C/C++ Source or Header  |  2001-06-23  |  11.1 KB  |  412 lines

  1. #include <Carbon/Carbon.h>
  2.  
  3. #define kTreeAppSignature 'tree'
  4. #define kDrawCommand 'draw'
  5. #define kTreeResizeCommand 'rsiz'
  6. #define kDrawContinuousCommand 'cont'
  7. #define kDepthSliderID 128
  8. #define kContinuousCheckboxID 129
  9. #define kTreeOpenAboutWindowCommand 'abut'
  10. #define kTreeVersionInfoID 132
  11.  
  12. static const int kInitialSize = 128;
  13. static Point gStart = {500,500};
  14. static const UInt32 kControlToTreePadding = 12;
  15. static UInt32 gDepth = 1;
  16. static Boolean gContinuousDraw = 1;
  17. static EventLoopTimerRef gDrawTimer;
  18. static UInt32 gFlushTicks;
  19.  
  20. EventTypeSpec    gTreeEvents[] = { 
  21.     { kEventClassWindow, kEventWindowClose },
  22.     { kEventClassWindow, kEventWindowDrawContent },
  23.     { kEventClassWindow, kEventWindowBoundsChanged },
  24.     { kEventClassWindow, kEventWindowClose},
  25.     { kEventClassCommand, kEventCommandProcess},
  26.         };
  27. EventTypeSpec closeSpec = {kEventClassWindow, kEventWindowClose};
  28.  
  29. WindowRef gTreeWindow, gAboutWindow;
  30.  
  31. pascal OSStatus TreeAboutWindowEventHandler(EventHandlerCallRef handlerRef, EventRef event, void* userData)
  32. {
  33.     OSStatus result = eventNotHandledErr;
  34.     UInt32 eventKind;
  35.     
  36.     eventKind = GetEventKind(event);
  37.     if (eventKind==kEventWindowClose)
  38.     {
  39.         HideWindow((WindowRef) userData);
  40.         result = noErr;
  41.     }
  42.     return result;
  43. }
  44.  
  45. pascal void TreeAboutWindowCommandHandler(WindowRef window)
  46. {
  47.     CFStringRef text;
  48.     CFBundleRef bundle;
  49.     ControlID versionInfoID = {kTreeAppSignature, kTreeVersionInfoID};
  50.     ControlRef versionControl;
  51.     ControlFontStyleRec controlStyle;
  52.     
  53.     bundle = CFBundleGetMainBundle();
  54.     text = (CFStringRef) CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("CFBundleGetInfoString"));
  55.     if ((text == CFSTR(" ")) || text == NULL)
  56.         text = CFSTR("Nameless Application.");
  57.     GetControlByID(window, &versionInfoID, &versionControl);
  58.     SetControlData(versionControl, kControlLabelPart, 
  59.         kControlStaticTextCFStringTag, sizeof(CFStringRef), &text);
  60.     controlStyle.flags = kControlUseJustMask;
  61.     controlStyle.just = teCenter;
  62.     SetControlFontStyle( versionControl, &controlStyle);
  63.     ShowWindow(window);
  64.     SelectWindow(window);
  65. }
  66.  
  67. typedef struct BranchVector {
  68.     int x;
  69.     int y;
  70.     } BranchVector;
  71.  
  72. // Couldn't remember how to spell the Abs function, so I wrote my own...
  73. static int Abs(int x)
  74. {
  75.     if (x<0) return -x;
  76.     return x;
  77. }
  78.  
  79. static void DoubleVector(BranchVector* v)
  80. {
  81.     v->x += v->x;
  82.     v->y += v->y;
  83. }
  84.  
  85. // This will cause a rounding problem if we ever get odd numbers
  86. //  so powers of two should be used for the initial tree size. 
  87. static void HalfVector(BranchVector* v)
  88. {
  89.     v->x >>= 1; // shift right one to divide by two fast.
  90.     v->y >>= 1;
  91. }
  92.  
  93. // Is a vector "extra long"?
  94. // We cheat when turning 45 degrees...
  95. //  diagonal vectors are inherently longer
  96. //  than the non diagonals, which automatically
  97. //  scales a vector up or down any time you turn it 
  98. //  by an odd multiple of 45 degrees.
  99. static Boolean LongVector(BranchVector* v)
  100. {
  101.     return (Abs(v->x) == Abs(v->y));
  102. }
  103.  
  104. static void Turn180(BranchVector* v)
  105. {
  106.     // flip both signs
  107.     v->x = -v->x;
  108.     v->y = -v->y;
  109. }
  110.  
  111. #ifdef TurnLeft45TruthTable
  112. // I used this table to figure out a 45 degree left turn algorithem.
  113. 0,1    -1,1
  114. 1,1    0,1
  115. 1,0    1,1
  116. 1,-1    1,0
  117. 0,-1    1,-1
  118. -1,-1    0,-1
  119. -1,0    -1,-1
  120. -1,1    -1,0
  121. #endif
  122.  
  123. static void Left45(BranchVector* v)
  124. {
  125.     // make a first guess, values don't change
  126.     //  and just fix the component that is wrong
  127.     if (v->x == v->y)         // 1,1 or -1,-1
  128.         v->x = 0;
  129.     else if (Abs(v->x) == Abs(v->y))    // 1,-1 or -1,1
  130.         v->y = 0;
  131.     else if (v->x == 0)        // 0,1 or 0,-1
  132.         v->x = -v->y;
  133.     else            // 1,0 or -1,0
  134.         v->y = v->x;
  135. }
  136.  
  137. #ifdef TurnRight90TruthTable
  138. // I used this table to figure out a 90 degree right turn algorithem.
  139. 0,1    1,0
  140. 1,1    1,-1
  141. 1,0    0,-1
  142. 1,-1    -1,-1
  143. 0,-1    -1,0
  144. -1,-1    -1,1
  145. -1,0    0,1
  146. -1,1    1,1
  147. #endif
  148.  
  149. static void Right90(BranchVector* v)
  150. {
  151.     // flip x and y, switching the x sign, to turn right
  152.     int temp = v->x;
  153.     v->x = v->y;
  154.     v->y = -temp;
  155. }
  156.  
  157. static void DrawVector(BranchVector* v)
  158. {
  159.     Line(v->x, -v->y);
  160. }
  161.  
  162. static void Left45Smaller(BranchVector* v)
  163. {
  164.         Left45(v);            // diagonal vectors are inherently longer...
  165.     if (LongVector(v))        // if it got longer, make it smaller
  166.             HalfVector(v);         // otherwise it already is smaller than the diagonal original
  167. }
  168.  
  169. static void Left45Bigger(BranchVector* v)
  170. {
  171.     DoubleVector(v);
  172.     Left45Smaller(v);
  173. }
  174.  
  175. static void Right135Bigger(BranchVector* v)
  176. {
  177.     Turn180(v);
  178.     Left45Bigger(v);
  179. }
  180.  
  181. static void Right135Smaller(BranchVector* v)
  182. {
  183.     Turn180(v);
  184.     Left45Smaller(v);
  185. }
  186.  
  187. static Rect TreeBounds(WindowRef window)
  188. {
  189.     Rect bounds;
  190.     
  191.     SetPortWindowPort(window);
  192.     GetWindowPortBounds(window, &bounds);
  193.     bounds.bottom = gStart.v + 1;     // leave room for the controls!
  194.     return bounds;
  195. }
  196.  
  197. static void FlushWindowNow(WindowRef window)
  198. {
  199.     Rect bounds = TreeBounds(window);
  200.     RgnHandle boundsRgn = NewRgn();
  201.     RectRgn(boundsRgn,&bounds);
  202.     QDFlushPortBuffer(GetWindowPort(window), boundsRgn);
  203.     gFlushTicks = TickCount();
  204. }
  205.  
  206. static void MayFlushWindow(WindowRef window)
  207. {
  208.     if (TickCount() != gFlushTicks)
  209.         FlushWindowNow(window);
  210. }
  211.  
  212. static void SubTree(WindowRef window, BranchVector* v, UInt32 howDeep)
  213. {
  214.     DrawVector(v);        // node left side
  215.     if (howDeep < 1)
  216.     {
  217.         // Leaf node, just square off the node
  218.         Right90(v);
  219.         DrawVector(v);
  220.         Right90(v);
  221.     }
  222.     else
  223.     {
  224.         // branch node, draw two smaller subbranches
  225.         Left45Smaller(v); // turn left and scale down
  226.         SubTree(window, v, howDeep-1);        // draw subtree
  227.         
  228.         // draw triangle connector
  229.         DrawVector(v);
  230.         Right135Bigger(v);
  231.         DrawVector(v);
  232.         Right135Smaller(v);
  233.         DrawVector(v);
  234.         
  235.         SubTree(window, v, howDeep-1);        // right branch
  236.         Left45Bigger(v);
  237.     }
  238.     DrawVector(v);
  239.     MayFlushWindow(window);
  240. }
  241.  
  242. void UpdateTree(WindowRef window)
  243. {
  244.     BranchVector initialvector = {0, kInitialSize};
  245.     Rect treeBounds = TreeBounds(window);
  246.     EraseRect(&treeBounds);
  247.     FlushWindowNow(window);
  248.     
  249.     MoveTo(gStart.h, gStart.v);        // draw base line
  250.     Line(kInitialSize,0);
  251.     MoveTo(gStart.h, gStart.v);
  252.     SubTree(window, &initialvector, gDepth);    // draw a tree
  253. }
  254.  
  255. static ControlRef DepthSlider(WindowRef window)
  256. {
  257.   ControlRef     depthSlider; 
  258.   ControlID  depthControlID = {kTreeAppSignature, kDepthSliderID};
  259.  
  260.   GetControlByID (window, &depthControlID, &depthSlider);
  261.   return depthSlider;
  262. }
  263.  
  264. static void PositionTree(WindowRef window)
  265. {
  266.     Rect bounds;
  267.         GetControlBounds(DepthSlider(window), &bounds);
  268.         gStart.h = bounds.left;     
  269.         gStart.v = bounds.top - kControlToTreePadding;
  270. }
  271.  
  272. static void TreeResizeCommandHandler (WindowRef window)
  273. {
  274.   gDepth = GetControlValue (DepthSlider(window));
  275.   UpdateTree(window);
  276. }
  277.  
  278. static void UpdateTreeTimer(EventLoopTimerRef inTimer, void *inUserData)
  279. {
  280.     UpdateTree((WindowRef) inUserData);
  281. }
  282.  
  283. static OSStatus SetContinuousDrawMode(WindowRef window, Boolean continuous)
  284. {
  285.     OSStatus status = noErr;
  286.     
  287.     if (continuous)
  288.     {
  289.         status = InstallEventLoopTimer(GetMainEventLoop(),
  290.         0.1, // initial delay
  291.         1.0, // time between fires
  292.         NewEventLoopTimerUPP(UpdateTreeTimer),
  293.         window,
  294.         &gDrawTimer);
  295.     } 
  296.     else
  297.     {
  298.         status = RemoveEventLoopTimer(gDrawTimer);
  299.     }
  300.     return status;
  301. }
  302.  
  303. static OSStatus DrawContinuousCommandHandler (WindowRef window)
  304. {
  305.   ControlRef checkbox;
  306.   OSStatus status = noErr;
  307.   ControlID  contControlID = {kTreeAppSignature, kContinuousCheckboxID};
  308.  
  309.   GetControlByID (window, &contControlID, &checkbox);
  310.   gContinuousDraw = GetControlValue (checkbox);
  311.   if (gContinuousDraw)
  312.   {
  313.     UpdateTree(window);
  314.     status = SetContinuousDrawMode(window, gContinuousDraw);
  315.   }
  316.   return status;
  317. }
  318.  
  319. pascal OSStatus TreeWindowEventHandler(EventHandlerCallRef myHandler, EventRef event, void* userData)
  320. {
  321.     OSStatus result=eventNotHandledErr;
  322.     HICommand command;
  323.     
  324.     if (GetEventKind(event) == kEventWindowDrawContent)
  325.     {
  326.         UpdateTree(gTreeWindow); // should use GetEventData?
  327.         result = noErr;
  328.     }
  329.     else
  330.     {
  331.     GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, 
  332.                 sizeof(command), NULL, &command);
  333.         switch(command.commandID)
  334.         {
  335.             case kTreeOpenAboutWindowCommand:
  336.                 TreeAboutWindowCommandHandler(gAboutWindow);
  337.                 result = noErr;
  338.                 break;
  339.                 
  340.             case kDrawCommand:
  341.                 UpdateTree(gTreeWindow);
  342.                 result = noErr;
  343.                 break;
  344.                 
  345.             case kTreeResizeCommand:
  346.                 TreeResizeCommandHandler(gTreeWindow);
  347.                 result = noErr;
  348.                 break;
  349.                 
  350.             case kDrawContinuousCommand:
  351.                 result = DrawContinuousCommandHandler(gTreeWindow);
  352.                 break;
  353.                 
  354.            default:
  355.                 printf("unknown command %ld\n", command.commandID);
  356.                 break;
  357.         }
  358.     }
  359.     return result;
  360. }
  361.  
  362. int main(int argc, char* argv[])
  363. {
  364.     IBNibRef         nibRef;    
  365.     OSStatus        err;
  366.  
  367.     // Create a Nib reference passing the name of the nib file (without the .nib extension)
  368.     // CreateNibReference only searches into the application bundle.
  369.     err = CreateNibReference(CFSTR("main"), &nibRef);
  370.     require_noerr( err, CantGetNibRef );
  371.     
  372.     // Once the nib reference is created, set the menu bar. "MainMenu" is the name of the menu bar
  373.     // object. This name is set in InterfaceBuilder when the nib is created.
  374.     err = SetMenuBarFromNib(nibRef, CFSTR("MainMenu"));
  375.     require_noerr( err, CantSetMenuBar );
  376.     
  377.     // Then create a window. "MainWindow" is the name of the window object. This name is set in 
  378.     // InterfaceBuilder when the nib is created.
  379.     err = CreateWindowFromNib(nibRef, CFSTR("MainWindow"), &gTreeWindow);
  380.     require_noerr( err, CantCreateWindow );
  381.  
  382.     err = CreateWindowFromNib(nibRef, CFSTR("AboutWindow"), &gAboutWindow);
  383.     require_noerr( err, CantCreateWindow );
  384.  
  385.     // set up the geometry
  386.     PositionTree(gTreeWindow);
  387.     
  388.     // We don't need the nib reference anymore.
  389.     DisposeNibReference(nibRef);
  390.     
  391.     {
  392.         UInt32 numSpecs = sizeof(gTreeEvents)/sizeof(EventTypeSpec);
  393.         InstallWindowEventHandler(gTreeWindow, NewEventHandlerUPP(TreeWindowEventHandler), 
  394.                     numSpecs, gTreeEvents, (void*) gTreeWindow, NULL);
  395.                 
  396.         InstallWindowEventHandler(gAboutWindow, NewEventHandlerUPP(TreeAboutWindowEventHandler), 
  397.                     1, &closeSpec, (void*) gAboutWindow, NULL);
  398.      }
  399.    
  400.     // The window was created hidden so show it.
  401.     ShowWindow( gTreeWindow );
  402.     
  403.     // Call the event loop
  404.     RunApplicationEventLoop();
  405.  
  406. CantCreateWindow:
  407. CantSetMenuBar:
  408. CantGetNibRef:
  409.     return err;
  410. }
  411.  
  412.