home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 …ember: Reference Library / Apple Developer Reference Library (December 1999) (Disk 1).iso / pc / technical documentation / develop / develop issue 28 / develop issue 28 code / macapp debugging / twistdownlists / !readme next >
Encoding:
Text File  |  1996-09-16  |  12.4 KB  |  313 lines

  1. Twist Down Lists Read Me
  2. Author: Conrad Kopala
  3.  
  4. Compile Notes:
  5.  
  6. Twist Down Lists has been compiled for the cases listed below:
  7.  
  8. PPC debug
  9. MABuild "Twist Down Lists" -cp MrC -MrC "-opt off" -debug -names -modelfar -autobuild
  10.  
  11. PPC nodebug
  12. MABuild "Twist Down Lists" -cp MrC -names -modelfar -autobuild
  13.  
  14. 68K debug
  15. MABuild "Twist Down Lists" -classic -debug -names -sym -modelFar -noMALibrary -autobuild
  16.  
  17. 68K nodebug
  18. MABuild "Twist Down Lists" -classic -names -modelFar -noMALibrary -autobuild
  19.  
  20. All compiles were done on a Power Mac 7500/100 with 80 mb of memory.
  21. All compiles were done using ETO20 MacApp 3.3.1, MPW 3.4.1.
  22.  
  23. Both debug and nodebug compiles must be done using the modified files:
  24.  
  25. UPlatformMemory.h
  26. UPlatformMemory.cp
  27.  
  28. In order to do a debug compile you must also use the substitute files for
  29.  
  30. UObject.h
  31. UObject.cp.
  32.  
  33.  
  34. Testing:
  35.  
  36. The most extensive testing was done on the Power Mac. 
  37.  
  38. Less extensive 68K testing was done on a Macintosh IIci.
  39.  
  40. All testing was done under System 7.5.3 update 2.
  41.  
  42.  
  43. Partition Size:
  44. rewrite this note
  45. The Twist Down Lists SIZE resource sets the partition size such that the application just runs out of memory 
  46.  
  47. a. when alternating open and expand all commands attempting to open the last 100Mb disk partition on my 1Gb disk results in the out of memory failure
  48.  
  49. b. when first opening all disk partitions and then attempting to do the expand all command on each of them, will result tin the out of memory failure.
  50.  
  51.  
  52. Problems:
  53.  
  54. Several MacApp related problems were encountered. They are described in detail here.
  55.  
  56. 1. TApplication::GetContainedObject
  57.  
  58. When testing an application, you should deliberately attempt to break it. One way of accomplishing that is to ask the application to do dumb things that no sane person would ask it to do. My attitude is, if I can think to do it, there surely is at least one other person who will also do it.
  59.  
  60. In this case, the dumb thing to do is make a script ask the application to access a document when there are no open documents. The result is a fatal access error.
  61.  
  62. The difficulty arises from the following code snippet from TApplication::GetContainedObject
  63.  
  64.     else if (desiredType == cDocument && selectionForm == formName)
  65.     {
  66.         // We know we're going after a document with a 
  67.         // specific name so just scan the document list.
  68.         CStr255  theDocName;
  69.         CNoGhostDocsIterator  iter(this);
  70.         TDocument*  aDocument = iter.FirstDocument();
  71.         
  72.         selectionData.GetString(theDocName);
  73.         do
  74.         {
  75.           if ((aDocument->GetOMClass() == desiredType) 
  76.                                 && (aDocument->fTitle == theDocName))
  77.             result = aDocument;
  78.           aDocument = iter.NextDocument();
  79.         } while (result == NULL && iter.More());
  80.     }
  81.  
  82.  
  83. As can be seen, it assumes that there is at least one document and when it asks the non-existent document to GetOMClass(), an access error results.
  84.  
  85. This problem was solved in Twist Down Lists with the following code snippet in TTwistDownLists::GetContainedObject
  86.  
  87.     else if (desiredType == cDocument && selectionForm == formName)
  88.     {
  89. //Note: MacApp 3.3.1 TApplication::GetContainedObject dose not work if a script
  90. //attempts to get a document that does not exist. In the case where there are
  91. //no open documents, the result is an access fault. Therefore, I'm using the
  92. //following substitute code.
  93.         CStr255    theName;
  94.         selectionData.GetString(theName);        
  95.         CNoGhostDocsIterator    iter(this);
  96.         
  97.         for (TDocument* aDocument = iter.FirstDocument(); iter.More(); aDocument = iter.NextDocument())
  98.             {
  99.                 if (aDocument != NULL)
  100.                     {
  101.                         CStr255 name = gEmptyString;
  102.                         aDocument -> GetTitle(name);
  103.                         if (name == theName)
  104.                             {                    
  105.                                 theTwistDownDocument = (TTwistDownDocument*)aDocument;
  106.                                 result = theTwistDownDocument;
  107.                                 return result;
  108.                             }
  109.                     }
  110.             }
  111.     }
  112.  
  113.  
  114. 2. TODocCommand and low memory
  115.  
  116. When a script asks an application to open a document and the application runs out of space in its object heap, the command will fail. Unfortunately, the failure results in a fatal access fault.
  117.  
  118. The following example script causes the problem:
  119.  
  120. tell application "Twist Down Lists"
  121.     activate
  122.     open {alias "MPW:"}
  123.     expand all document "MPW"
  124.     open {alias "General:"}
  125.     expand all document "General"
  126.     open {alias "Data2:"}
  127.     expand all document "Data2"
  128.     open {alias "Data1:"}
  129.     expand all document "Data1"
  130.     open {alias "Data:"}
  131.     expand all document "Data"
  132.     open {alias "System:"}
  133.     expand all document "System"
  134. end tell
  135.  
  136. It is nothing but an alternating sequence of opens and expand alls. When Twist Down Lists runs out of space in the object heap, TCommandHandler::DoPerformCommand catches it and signals TODocCommand to 
  137.  
  138. void TServerCommand::SetValidationError(OSErr error)
  139. {
  140.     // The finder passes a null reply with required events
  141.     // so don't try to stuff any info into the reply
  142.     if (fReply && fReply->fMessage.descriptorType != typeNull)
  143.         fReply->WriteOSError(error);
  144.     Inherited::SetValidationError(error);
  145. }
  146.  
  147. which, in turn signals fReply to
  148.  
  149. void TAppleEvent::WriteOSError(OSErr error)
  150. {
  151.     if (this->fMessage.descriptorType != typeNull && this->fMessage.dataHandle)
  152.     {    
  153.         CStr255 errStr;
  154.         this->WriteLong(keyErrorNumber, errAEEventFailed);
  155.         if (!LookupErrString(error, errReasonID, errStr))
  156.         {
  157.             // No error string was found, substitute the generic one.
  158.             CStr255 errNumStr;
  159.             LookupErrString(errGenericErrorWithNum, errReasonID, errStr);
  160.             NumToString(error, errNumStr);
  161.             MAParamText("^ENUM", errNumStr);
  162.             MAReplaceText(errStr);
  163.         }
  164.         
  165.         this->WriteString(keyErrorString, errStr);
  166.     }
  167. }
  168.  
  169. and when it attempts to actually write the error into fReply
  170.  
  171. void TAppleEvent::WriteLong(const AEKeyword theKey,long theData)
  172. {
  173.     AppleEvent theMessage = fMessage;
  174.  
  175.     FailOSErr(AEPutParamPtr(&theMessage, theKey, typeLongInteger,
  176.                             & theData, sizeof(theData)));
  177. } // TAppleEvent::WriteLong
  178.  
  179. we fail, again.
  180.  
  181. This is a nasty one, because now you get the fatal access fault. Something bad has happened to fReply and at this point I do not know exactly what it is but it appears that whatever the bad thing is happened to theMessage. The error generated just before the access fault is:
  182.  
  183. memWZErr = -111,    /*WhichZone failed (applied to free block)*/.
  184.  
  185. However, the TServerCommand constructor sets the field fSuspendTheEvent = FALSE. If it is set to TRUE, there is no problem. I believe the default value for fSuspendTheEvent should be TRUE. If you choose to set it to TRUE, you must do it before IServerCommand is executed becaus it is IServerCommand that suspends the event. 
  186.  
  187. Note that the example application Icon Edit exhibits the same behavior when a script tells it to open enough documents to cause it to run out of space in its object heap.
  188.  
  189.  
  190.  
  191. 3. TSetPropertyEvent memory leak
  192.  
  193. Initially, I used as a template for handling the TSetPropertyEvent the code given in the Icon Edit expample, excerpted below:
  194.  
  195.     case cSetColor:
  196.         {
  197.             CRGBColor    newColor;
  198.             CStr255        thePrompt = "Pick a new color";
  199.             if (GetColor(kBestSystemLocation, thePrompt, fColor, newColor))
  200.             {
  201.                 if (TOSADispatcher::fgDispatcher->GetDefaultTarget()->IsRecordingOn())
  202.                 {
  203.                     TSetPropertyEvent *appleEvent = new TSetPropertyEvent;
  204.                     appleEvent->ISetPropertyEvent(gServerAddress, kAENoReply, this, pColor);
  205.                     CTempDesc theNewColor;
  206.                     theNewColor.PutRGBColor(newColor);
  207.                     appleEvent->WriteParameter(keyAEData, theNewColor);
  208.                     appleEvent->Send();
  209.                 }
  210.                 else
  211.                 {
  212.                     TSetColorCommand *aSetColorCommand = new TSetColorCommand();
  213.                     aSetColorCommand->ISetColorCommand(this, newColor);
  214.                     PostCommand(aSetColorCommand);
  215.                 }
  216.             }
  217.         }
  218.     break;
  219.  
  220. Unfortunately, the appleEvent is never freed and the appleEvent->Send(); results in a replyEvent being created and returned. That reply event is also not freed. This is a very clear case where object counting is very helpful. Without it, you may not discover this memory leak until well after the application has shipped. With object counting, you discover the problem very quickly. With object display, you you quickly discover that you have two Apple events that have not been freed. Then, it is a question of deciding how to solve the problem.
  221.  
  222. The following code snippet shows how to solve this problem.
  223.  
  224.     case cSetColor:
  225.         {
  226.             CRGBColor    newColor;
  227.             CStr255        thePrompt = "Pick a new color";
  228.             if (GetColor(kBestSystemLocation, thePrompt, fColor, newColor))
  229.             {
  230.                 if (TOSADispatcher::fgDispatcher->GetDefaultTarget()->IsRecordingOn())
  231.                 {
  232.                     TSetPropertyEvent *appleEvent = new TSetPropertyEvent;
  233.                     appleEvent->ISetPropertyEvent(gServerAddress, kAENoReply, this, pColor);
  234.                     CTempDesc theNewColor;
  235.                     theNewColor.PutRGBColor(newColor);
  236.                     appleEvent->WriteParameter(keyAEData, theNewColor);
  237.                     TAppleEvent * theReply = appleEvent -> Send();
  238.                     FreeIfObject(appleEvent);
  239.                     FreeIfObject(theReply);                
  240.                 }
  241.                 else
  242.                 {
  243.                     TSetColorCommand *aSetColorCommand = new TSetColorCommand();
  244.                     aSetColorCommand->ISetColorCommand(this, newColor);
  245.                     PostCommand(aSetColorCommand);
  246.                 }
  247.             }
  248.         }
  249.     break;
  250.  
  251.  
  252. Other Insights:
  253.  
  254. 1. TTwistDownControl::DoMouseUp
  255.  
  256. DoMouseUp is somewhat analogous to DoMouseCommand. The difference is that DoMouseCommand knows if the click was a first click and can ignore a first click. DoMouseUp doesn’t know anything about clicks. In the case of Twist Down Lists, a mouseDown followed by a mouseUp in a twistDownControl in a behind window results in the window being brought to the front and the twistDownControl being expanded or collapsed as the case may be. That’s a little disconcerting.
  257.  
  258. In Twist Down Lists, TView::HandleMouseDown is overridden in order to capture first click information as follows:
  259.  
  260. fIsFirstClick = (mouseDownType == kFirstClick_dragOnly || mouseDownType == kFirstClick_dragOrClick);
  261.  
  262. which is saved in the TTwistDownView field fIsFirstClick for use by TTwistDownControl::DoMouseUp where it is tested with:
  263.  
  264. if (fTwistDownView -> fIsFirstClick == FALSE)
  265.     {
  266.         Do this stuff if it isn’t a first click.    
  267.     }
  268.  
  269. Essentially, the mouseUp event following a first click mouseDown event is swallowed.
  270.  
  271.  
  272. HI Issues:
  273.  
  274. 1. Progress Indicator and Scripting
  275.  
  276. When Twist Down Lists is driven by a script that is opening disks, the progress indicator is put up. I am ambivalent as to whether that is the right thing to do. That is particularly so in view of the fact that the stop buttton is active and if the user were to click on it, Twist Down Lists would stop reading the volume and continue on with the next script command. At least during the debugging process it is nice to have it but, for a shipping application I don’t know.
  277.  
  278.  
  279. 2. cWritingDirection Command
  280.  
  281. Originally, there were two checked menu items, cRightToLeft and cLeftToRight. Whichever was currently being used was checked. When the other was selected, it was checked and the other one was not checked. In other words, the application toggled between two distinct menu items. 
  282.  
  283. Then I decided to use a single command and change the command name appearing in the menu depending on the current situation. That is, if the current writing direction was left-to-right, the menu would show Right-To-Left and if the current writing direction was right-to-left, the menu item would show Left-To-Right.
  284.  
  285. Having used it for a while, I no longer like it. I have to think too hard to deduce what’s really going on. I think the old way, with two menu items, was better. 
  286.  
  287. Unfortunately, it is too late to change it back.
  288.  
  289.  
  290. 3. Window zoom and resize
  291.  
  292. When the writing direction is right-to-left, the twistDwonControls and the text should remain pinned to the right side of the window. There are two situations where this does not happen. One is the case inZoomIn in TWindow::ZoomByUser. If the user zooms out, everything works. However, when the user zooms in, the view scrolls roughly to the horizontal midpoint of the view. Usually, there is nothing to see there and the scroll bars must be used to to find anything in the window.
  293.  
  294. The other is in TWindow::ResizeByUser. If the user resizes the window and makes it larger, everything remains pinned to the right side of the window as it should. But, when the user resizes the window and makes it smaller, the the stuff doesn’t remain pinned to the right side of the window.
  295.  
  296. Neither of these problems are fatal, but they sure aren’t good human interface design.
  297. At this point, deadlines prevent me from getting them fixed, so we ship.
  298.  
  299. They’re going to be interesting problems to fix. MacApp seems to be topLeft-centric and really doesn’t want to have anything to do with topRight. ResizeByUser will be a little tricky to handle because from my preliminary investigation, subviews of TWindow don’t appear to be told that the window was resized. It may be necessary to subclass TWindow.
  300.  
  301. Looks like an opportunity to me.
  302.  
  303. 7/15/96
  304.  
  305.  
  306.  
  307.  
  308.  
  309.  
  310.  
  311.  
  312.  
  313.