Quadratic Plotters
Volume Number: | | 5
|
Issue Number: | | 8
|
Column Tag: | | MacApp Workshop
|
A Tale of Two Quadratic Plotters
By Chuck McMath, Carl Nelson, MacApp Developer's Assoc.
Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.
[Authors Note: This article is NOT about MacApp. We do not cover the basics which you can get elsewhere. For example, this article does not discuss the concepts behind Object programming and Object Pascal syntax, nor do we explain the organization of all the MacApp classes. The December 1987 MacTutor, Kurt Schmuckers excellent book Object Oriented Programming for the Macintosh, the January 1989 APDALog, the newsletter of the MacApp Developers Association (Frameworks ) or the MacApp documentation itself are good reference sources. You may understand some of this article without knowing the basics behind Object Pascal or MacApp, but dont count on it! The programs discussed in this article were written using MacApp 2.0ß8. By the time this is published, the final MacApp 2.0 should be available. As far as we know, no changes to this code should be necessary to compile, link, and run it under the final version of MacApp 2.0, but one never knows ]
Introduction
This article is about the conversion of Dave Kellys and Dave Smiths Plot Program into a MacApp program. The program originally appeared as part of the Multifinder Friendly MacDraw Plotter article in the February 1988 MacTutor. Actually, we will discuss two conversions, one written by Chuck McMath (this month) which attempts to literally translate the original program into MacApp and the other a conversion (next month), by Carl Nelson, ports the intent of the original program into a MacApp program. We review the original article and program, then expose the mechanics and decisions involved in doing both conversions. Along the way, we will show you the basics of physically creating a MacApp program and give you some of our reasoning and insight as to why we did what we did. Hopefully you can use this wisdom to do the same as you start writing programs using MacApp. Most of us using MacApp have learned the hard way - hours of working on projects until we reach enlightment on the techniques needed to take advantage of object programming languages.
The motive of this article is not to present the blinding vision of enlightenment on how to learn MacApp but rather a pragmatic approach, an exploration of two ways that might work. We know the starting point (the original program) and the ending point (the original program rewritten using MacApp). Lets travel along the path from start to end with two programs, one a simple clean translation and the other a program that produces the same effect but is internally quite different -- the low road (direct translation) and the high road (conversion and expansion). Two points of view, and two results. From both of these you can gain insight into the mechanics of MacApp and a brief look at how Objects and MacApp change the way you look at writing programs.
Introduction Revisited
While Carls viewpoint is the elegance and power that MacApp gives you in terms of code reusability, my perspective is somewhat different. The main impression that I had when I looked at the Multifinder Friendly Plotter was hey -- this program is all user-interface. Although it does spend some time solving a quadratic equation and fooling around with picture comments, most of the code is involved with the plot parameters dialog box, putting the window up, handling events correctly, etc. Of course, you might say those features are necessary in any Macintosh program, right? Absolutely! These common user interface features are what MacApp is all about. My point of view is that MacApp provides a full-blown user interface, freeing you from having to worry about those details, and letting you concentrate on what makes your application different. You can turn your application out more quickly, and have it be of higher quality and greater reliability. The bottom line: less work!
I took the design of the plotter application and decided to implement it just as it was. I didnt make any changes to the overall design to take advantage of MacApp, but (and this is an important but) I also did not limit the program to take away any of MacApps benefits. For instance, the original plotter program puts up a dialog box, waits for you to enter parameters, and then plots the quadratic equation in a new window. If you replot the equation, you get a plot in the same window -- i.e., only one window is on the screen at a time. MacApp, however, implements multiple windows as a given; instead of throwing out MacApps multiple window feature, I allowed the new plotter application to take advantage of that capability. The result is that the new plotter application allows you to produce as many plot windows as memory allows -- and better yet -- this was done with very little work on my part.
The Original Program
The original plotting program was written just after the new Macintosh ROMs became widely available. Since MacTutor likes to keep on the cutting edge, the two Daves decided to write a program which implements as many features as they could cram into a coherent whole. While the explicit purpose of the program is to solve quadratic equations, the real purpose was to showcase some of the new features in the Mac ][/SE ROMs. The new features being exhibited included:
- calling SysEnvirons to determine if the machine supported the required new features
- resources to specify colors in menus/windows
- hierarchical menus used to select colors for parts of the plot
- Multifinder support through calling WaitNextEvent and acting on Multifinder events
- capability to save plots as PICT files, and the use of PicComments to group logically related items
- hairlines on the LaserWriter through using PicComments.
The operation of the program is fairly simple: when Plot is selected from the File menu, a modal dialog box appears. The user types numbers in for the quadratic equation parameters and some plot parameters, then clicks the OK button (there is no Cancel button). The dialog goes away, and a plot window appears, with the solution to the quadratic equation plotted in the window. The plot is sized so as to always fill the entire window (minus allowance for the scroll bars, of course). A Graph menu lets the user change the color of the axis, the plot, and the background items. A Print Options menu allows the user to specify whether the printed output should be actual size (i.e., the window size) or a full page size. The Graph menu features three hierarchical menus displaying the colors which may be chosen for the three elements of the plot which can be colored (the axes, the plot itself, and the background). The colors available are the 8 supported in the so-called Old QuickDraw model. The window can be resized, dragged, zoomed, and closed. The contents of the window may be saved, and if saved, the file written out is a PICT file which can be opened in MacDraw. If the file is opened in MacDraw, individual items can be manipulated, as if the plot were drawn in MacDraw itself. Selecting Plot again while a plot window is up brings up the modal dialog again, but when it is dismissed, the plot is redrawn in the existing window (only one window is allowed on the screen).
This is only an overview of the original program and this short description cannot do it justice. If youd like to go read the original article, we wont be offended (just come back to this one.) And now, back to Carl.
MacApp Provides Features
As Chuck said, the original article and program were written to show new features introduced into the Macintosh environment in early 1988 and not as the best or even the right way to do plotting of equations. The original article and the example program show a style for teaching and exploring programming. In order to accomplish their task, the two Daves drew source code from Professional Programmers Extender, past issues of MacTutor, Dave Wilsons Examples and Steve Sheets Code, Inside Macintosh, and no less than 13 Apple Tech Notes. Several times in the article you can find a Dave saying: It is much faster to cut and paste source code from someone else, than to reinvent the wheel yourself. The theme of this article, and many MacTutor articles, could just as well have been learning through reuse and extension. Programming by extending and reusing code is one of premises of an application framework and a key feature of languages containing Objects. We show how reuse and extension can occur when bringing the MacTutor Plot program into the MacApp framework. Writing this a year later and having version 2.0ß8 of MacApp in hand, many of the concerns presented in the article are non-issues. To be specific:
Update Events & Growing the Window
MacApp users in general (and specifically for a program the size and complexity of Plot) never worry about update events. The MacApp architecture nicely handles setting things up for you so that all you have to do is draw when asked to by MacApp. MacApp programmers seldom worry about events, the event loop, Desk Accessories or switching in and out under Multifinder. There is a very robust event handling architecture provided by MacApp and it has been our experience that as the MacOS changes, Apple has enhanced MacApp to match the changes.
Multifinder
Being Multifinder friendly and having to deal with WaitNextEvent are non-issues. MacApp properly handles all of it as long as the SIZE resource is properly set up. If background processing is needed the right objects should be overridden.
Palette Manager
The MacApp DrawShapes Example program provides us with an example of how to setup a custom Palette color with a single call to the toolbox GetColor routine.
Menus (Color, hierarchical or otherwise)
MacApp has a very interesting and useful scheme for handling menus and the actions to be taken when menu selections are made. MacApp uses a resource named cmnu which is the same as a regular MENU but adds an integer to the end. This integer serves as a unique command number for that entry. The cmnu resources are post-processed by a utility called PostRez. It splits apart cmnus and creates a regular MENU and a table that maps these command numbers to menu items for use in your program. MacApp uses these unique command numbers to inform you of menu selections as well as handling the standard kind of menu functions like New, Open and Save. Command numbers 0 through 1200 are reserved for use by MacApp; the rest are available for use in your program. The nice side effect of this is that you can rearrange menus without regard to where they reside in the MENU BAR or their item number. All you ever deal with is the unique command number. Of course there are good examples of custom Menus in both the MacApp Developers Association Goodies Disk and the MacApp samples shipped by Apple.
Saving
MacApp provides all the code surrounding New, Save and Save As. If all of your data fits into memory at once, you will have to implement only two procedures, DoNeedDiskSpace and DoWrite, to save your data. You do not have to worry about temp files, disk space and errors, as MacApp provides a powerful error handling mechanism to handle general case failures so you do not have to.
Printing
By creating a standard print handler object and assigning it to a view (objects which display things in MacApp) a view can be made can be made to print as well as display on the screen. Because of this mechanism, you usually do not have to worry if you are drawing to the screen or a printer port.
Machine/System Determination
MacApp provides compile time variables (qNeedsScriptManager, qNeedsROM128K, qNeedsHierarchialMenus, qNeedsStyleTextEdit, qNeedsWaitNextEvent, qNeedsColorQD, qNeedsMC68020, qNeedsMC68030, qNeedsFPU) that you can use to configure your application so that if your combination of features is not present, your application will safely exit.
With all of the above support, I will not dwell on these features of MacApp [Chucks note: I will dwell on them to some extent.]. We will take this support as given and refer to them as appropriate in our discussions of our conversions. Again, you can find ample documentation as to how these work by reading the MacApp 2.0 final documentation, browsing the sources which are supplied with MacApp or looking through the 1988 and 1989 issues of Frameworks which discuss the MacApp 2.0 Architecture.
With all this out of the way we can move on to discuss our conversion efforts. Chuck gets to go first and discuss his efforts then I will talk about my conversion.
Overall Program Design
The crucial point of creating a MacApp program comes before any code has been written: design is the key, for if you have a good object design the code becomes obvious (well, if not obvious, then maybe more clear). A good design can be understood by many people. A bad design is usually not even understood by its creator after a short time passes. The major design of our objects is easy. This is because we are using MacApp. By using MacApp, you sign a pact to become a member of the MacApp community. This agreement says that you will accept all of the wonderful features MacApp provides for your application; in exchange, you agree to be a good citizen and write your application according to a particular structure. This structure enables you to take advantage of the MacApp code, and in a sense, drop your code into MacApp and have it work. The difficult part of object design is deciding where the things you are familiar with fit in. When you start out, you will often be asking yourself which feature and parameters belong to which object. As well see, determining this is usually easy, once you understand the objects MacApp provides for us, and how they relate to the pieces of your application. In this particular case, the design is made easier in that we are simply converting an existing program to MacApp, and I am following the original design as much as possible. This means that many of the decisions have been made for us.
MacApp has a number of predefined object classes which equate to the different portions of an application. These predefined objects have the generic Mac thing behavior already implemented. For instance, there is an object which corresponds to the overall application -- TApplication (incidentally, MacApp object types all begin with a capital T. This is, I suppose, to remind you that an object is simply a declared type in Pascal). An object of type TApplication knows how to do all of the things that a good application must -- handle desk accessories, put up an about box, work under MultiFinder, launch when double-clicked upon in the Finder, open a document when the document is double-clicked, etc. Therefore, you dont have to do anything to your application to have it exhibit this behavior. Instead, you worry about the part of your application that is different from a standard application. One aspect of MacApp programming that you will become accustomed to is that you dont do all of the work -- you let MacApp do it for you. This can make you somewhat confused, for in many cases, you only implement a few methods for an object -- yet it seems to be quite a sophisticated object. This is because you are taking advantage of the predefined MacApp object behavior. Dont worry, you will quickly get used to not doing all of the work yourself -- and you might even like it.
MacApp Function Subclass
TApplication Manages Documents TQPlotApplication
launch, open, etc.
TDocument holds data TQPlotDocument
stored on disk
TView Displays data in TQPlotView
windows
Table 1: MacApp Objects and QPlot Descendents
Table 1 shows a simplified description of the predefined MacApp objects, what they are responsible for, and the descendent objects defined in the plotter application. Keep in mind that when you define an object and modify its behavior, you can be doing one of two things: adding some behavior that does not exist, or changing some existing behavior. Our TQPlotApplication is an example object which was created for both of these reasons. Behavior has to be added to it to handle the colorful hierarchical menus, for example. And some behavior also has to be changed -- and this is where experience with MacApp becomes necessary -- because the generic MacApp behavior of an application is to open a blank document when you double-click on the application in the Finder. In our case, this behavior is not what we want to happen, so, I decided to override the method which produces the blank document. This requires behavior modification for our TApplication object.
As you can see from the table, this application requires only a few simple objects (remember, my goal was to do a quick conversion and not to make the application into the ultimate MacApp program). There is, of course, an application object, since every MacApp program has to have an application object -- TQPlotApplication. There is a document, since we are concerned with storing data on the disk -- TQPlotDocument. There is a view, since we are displaying data in a window -- TQPlotView. And, last, but not least, there is the dialog view, since we have a dialog which requests the plot parameters -- TDialogView. Note that the dialog view is not a specialized view we defined. Why is that? Well, when you look at what a typical Macintosh dialog view lets you do, you will realize that MacApps dialog functions are ample to do what we want, especially since our dialog is a simple one. The other objects, though, warrant some explanation to show what they provide outside of the generic MacApp structure. Which leads us to the question:
How does anything get done?
Good question, Chuck! Things get done in MacApp by having one of the conceptualcontrol chains take over. The most prevalent chain used in MacApp is the inheritance chain. Many parts of MacApp use this sort of chain to pass control on from child (subclass) to parent (superclass). Knowing where to hook in by OVERRIDEing is the key to using this and the other chains. The other chains are:
1) The command chain routes system generated events to objects that want to handle them.
2) The idle chain is traversed during idle to give selected objects periodic chances to execute.
3) The choice chain informs a view objects parent views that its state has changed.
Knowing these chains exist help you to determine where as well as who does the work and gives you conceptual frameworks around which to build your application. Now what were you going to say about the plotter application, Chuck?
In the plotter application, nothing happens unless the user selects something using the mouse, so we will only consider mouse down events, which are part of the command chain (incidentally, you dont need to know much about these chains in order to program in MacApp, although knowing what is going on under the hood helps you to understand when things turn out differently from what you expect. But back to mouse downs ) In addition, Ill classify the mouse downs into either menu bar hits or other. Initially I will discuss menu bar hits and how you translate from a menu selection to some activity.
In a non-MacApp program menu enabling and disabling is tied quite closely in with activate and deactivate events. Of course, this makes perfect sense, because when you activate a window you want that windows menu items enabled, and vice versa when the window is being deactivated. There are a few inherent problems with this approach, however. First, you need to have special code in your activate or deactivate event handling procedure to eat up successive activate/deactivate events (the way Chernikoffs MiniEdit does it) or else you will end up in the wrong state -- with the wrong items enabled. Second, this technique is not easy to extend or modify, and if you end up adding a new window type, then you add volumes to the code.
MacApp and Menus
In MacApp, menus are handled quite differently, as Carl has described above. MacApp tries to unhook the connection between an item in a menu and an object which handles that item. Instead of hard-coding menu processing in a gigantic procedure, MacApp introduces the concept of commands and command numbers. Think of each menu item as representing a different command, and each command having its unique number. Some commands will best be handled by the application -- New, Open, and Quit for example. Other command are the domain of the document -- Save, SaveAs and Close. Still other commands should be handled by the object which draws the data -- turning grids on and off, aligning objects, perhaps cutting and pasting. So you can see that instead of a central processing repository for all menu actions, MacApp farms the commands out the different objects. In fact, this is a crucial tenet of object programming: an object will only deal with information it knows how to process. Therefore, in MacApp, each object is given a stab at a menu selection. If the object knows how to handle that command, it does whatever it is supposed to. If, however, the object doesnt recognize that command as something it knows about, the object just passes the request along. Eventually either some object somewhere handled the menu choice, or MacApp senses that no object handled the menu choice, and that choice is simply thrown away.
Menu enabling is handled in the same distributed way as menu selection: there is a special method defined for objects in MacApp where they enable or disable the menu items they are concerned with. This ensures your program will never see a menu item enabled if it shouldnt be, because the only way that item is enabled is if its corresponding object exists. In fact, whenever MacApp determines that the menu bar could have changed, it disables every item in all of the menus. Only after all items have been disabled do the individual existing objects get a chance to enable their functions.
Using a Command to Do Work
When a selection is made from a menu, the object that chooses to handle the menu selection can do one of two things: go ahead and perform whatever action was called for (in easy or trivial cases), or create a command object to perform the command and pass it back to MacApp to be executed (more difficult cases). Command objects are the technique MacApp uses to implement an action and its associated Undo and Redo. Since all of the actions in the plotting program were simple and could be undone easily, my version of the plotter application does not have any command objects. [Carls Note: My version does]
What about mouse downs that arent in the menu bar? They are normally handled by a method called DoMouseCommand (pretty obvious name, eh? Most MacApp methods attempt to be as obvious) but since the plotter application is a simple one, we dont have anything to do in this method. All of the normal Macintosh behavior is already provided for us by MacApp: dragging the window around; clicking and dragging in the grow box, and resizing the window when we release the button; clicking in the close box to indicate that we want to put the window away; clicking in the zoom box to zoom or unzoom the window -- all of these normal, everyday Macintosh features are there -- yet we didnt do a thing about them. This is the power of MacApp.
The Application Object
The application object, as we have mentioned before, is responsible for the things a good application does -- handling chores at startup and quitting time, creating and opening documents, handling desk accessories, and of course handling and sending events to the appropriate object. What does the object we created for this program do? Remember, that we can either add functionality to our object, or override existing functionality to change the behavior of our customized application object. Outside of initialization code, my application does one thing: handle choices from the Graph menu, which allows the user to choose the color of the axis, plot, and background. Since we want to be able to set a default for the next plot, the application object has three variables which will correspond to those colors. Selecting from these menus only changes the applications variable. When a plot is created it uses the current set of default colors. Once a plot is created, its colors cannot be changed.
The Document Object
The document object is principally responsible for only one thing: saving and restoring its data. While there are certainly other things the document must be concerned with, most of the documents methods will involve either saving and restoring, or manipulating its data structure -- the document is the object which is responsible for maintaining the data that our application requires. In addition, each document will usually have a window associated with it, and it will be used to display data from the document.
The standard document knows that it must be able to save and retrieve itself from disk, however, theres no way the generic document can know how you want to store your data. So, if you were to look in the MacApp source code, in the section that dealt with saving the document, you would find that the DoWrite method is called. However, looking at the DoWrite methods would not help you, since the TDocument.DoWrite method is empty. So whats going on here? Well, this design takes advantage of the inheritance chain Carl spoke of earlier. When you define your document class, you will implement your save routine for the DoWrite method that OVERRIDEs (replaces) TDocuments DoWrite (this is where you need to understand the concept of OVERRIDE). When Save is selected from the File menu, DoWrite is called for the document (your document) and its DoWrite method gets called -- all because of the hook put in by MacApp. You dont have to write any code to handle disk errors, not enough space, etc., because MacApp does it for you by calling DoWrite at the right time and under the right conditions. MacApp is full of these hooks -- calling a placeholder procedure that serves only to occupy space until you OVERRIDE it to provide the needed functionality. In many cases you are only defining these needed override procedures. Sometimes it is frustrating and confusing or seems quite magical as to where to find these magic place holders that you can override to get the work done. The only advice we offer here is patience. Reading the documentation sometimes helps, looking at source code of sample programs that behave the way you want is also a good way to discover these placeholders and of course reading articles like this one.
So what does the plotter document do? It has its own initialization code where it clears its copy of the quadratic equation parameters, and creates the window and view. Since the document does this work in my application, it has a method which will present the dialog box to the user and allow the user to type in parameters for the quadratic equation. Solving the equation is another function the document performs (note: the application object could have been chosen to solve the equation; I chose the document object since each document carries its own set of equation variables). And of course we need methods to write the plot out to disk. Since my plotter application does not read its data back in, there are no methods to read the PICT file, only ones to write it.
The View Object
The view exists to display the data in a window. Since the application draws quadratic equations, this is the main function of the view. However, there are some other functions our view must perform. Since good object design says data should be encapsulated, the view is the only object which knows its size, it is responsible for calculating how large it is. In the plotter application, the view can be one of two sizes: either the view is the entire size of the window or it is the size of a full page. When the window changes size, MacApp sends a message to the view, telling it to recalculate its size. Our view must know whether it is full page size or not, and depending on that status, must calculate its size. Since the view size can be changed by a menu choice, the view must have the necessary code to handle that menu choice. And of course, there must be code to enable the menu choices that the view can handle.
One big difference between the MacApp and the original version of the plotter program is that the original plotter explicitly passed which device was being drawn on -- the screen or the LaserWriter -- while the MacApp program doesnt know. The original program did this to optimize operations for the LaserWriter (as all good Macintosh programmers do): for instance, when drawing on the LaserWriter, the original program did not fill rectangles with white (since on the LaserWriter this operation is just a waste of time). My MacApp version did not follow this approach because in the MacApp structure, printing is just the same as drawing on the screen -- the view doesnt know and shouldnt care which is being done. Although, I could have queried the global variable gPrinting to find out if the view was printing. MacApp takes care of all the device-specific setup, and our view just draws.
The Dialog Object
The dialog which initially appears is a simple one, but it serves to illustrate just how much MacApp does for us, and how much we dont have to worry about. Think about all the steps required to put up a good-looking dialog that simply requests some values: after the resources have been created we need to get the dialog (its invisible), install a procedure for the UserItem (to outline the default button), stuff initial values, make the dialog visible, drop into a ModalDialog loop, if the default button was pressed then grab values from the dialog items and convert them back into numbers. The design of dialogs in MacApp incorporates much of this tedious process into a complete package that again gets done for us. Sure, MacApp dialogs arent free, but as they get more complex, MacApps contribution becomes more and more significant.
Just what is a MacApp dialog?
While in the normal Macintosh environment a dialog is a special item, in MacApp, a dialog is simply a view (i.e., a place where data gets drawn) with a bunch of views within it. Every item in the DITL that can handle some interaction is a view, and these view types correspond pretty intuitively to the DITL types: TCheckBox, TRadioButton, TButton, TEditText, and TNumberText are the major types. As you may by now assume, these classes respond to mouse clicks in the way wed want them to: for instance, when the check box is clicked on, the TCheckBox class flips an internal variable and checks the control. So we dont have to do anything special in this dialog to get the kind of interaction we want (in fact, MacApp also does the little things like highlighting the default button, so we have even less work to do).
Our dialog box is pretty simple, since the only items in it that can respond to events are the six parameter EditText items. Each of these items is represented as a TEditText object in MacApp. By now you should realize that each of these dialog object types has default behavior that corresponds to what we expect when we are in a dialog -- in our case, the TEditText objects respond to clicks and keystrokes. Hitting a tab while in a field causes the succeeding field (in DITL order) to be highlighted. This behavior is what we want, for the most part.
One important sidelight is that MacApp 2.0 does not use the Dialog Manager for its dialogs. This has a number of implications: dialogs are handled like other windows -- in fact, you may have noticed that when a modal dialog comes up, the menus all get disabled except for the Edit menu. With MacApp, you can use the Edit menu while your modal dialog is up. Another interesting feature is that since these modal dialogs are not real dialogs, your application can be switched out under MultiFinder while a modal dialog is on top. But back to our description
You may have noted that there is a TNumberText object, yet we used TEditText objects for numeric fields. Why? Well, TNumberText is only used for LongInts, and we want real numbers, so we will just take the strings and convert them to real numbers ourselves (Unlike Carl, I wasnt aware of Calvin Cocks TExtendedText unit that handles Reals, which he discusses later, so I (ugh) reinvented the wheel. Talk about an advertisement for being in touch.).
What goes where?
One of the decisions you must make when writing a MacApp program is which object a method belongs to. In many cases, its just not clear which object should have the responsibility. Take cut and paste for example. On the one hand, you could consider that cut and paste change the data structure associated with the document, so they should be document-level methods. On the other hand, you could say that cutting and pasting change the appearance of the items on the screen, so they should be view-level methods. One way to tell that a method is in the wrong place is that you are constantly (i.e., more than two or three times) having to refer to another objectss fields to accomplish even a simple task. You probably have the method in the wrong class. Putting a method in the wrong class makes the method look messier or more complicated than it is. You will usually find yourself doing something like:
a := TMyDoc.fMyView.fMyViewData.fGetaValue;
Another way to tell if a method is misassigned is to see if you rely on information that isnt plainly available to your object to enable or disable the menu item associated with that command. If, for instance, you have cut and paste associated with the document, and you need to ask the view if any items are highlighted so you can tell if you should enable the menu items, then you either have a bad design or you have the method in the wrong class.
Discussion of the program
At this point, Id like to move through the source files and point out where you will find the pieces I have described. The source files consist of three types of files: Pascal (MacApp) source, resources, and the makefile. The source files will merit most of the discussion, so lets start with them.
Main program file (MQPlot.p)
All MacApp main programs look pretty much alike. After the necessary junk at the beginning at the file, the main program starts. Main programs are boring, because so much of the Macintosh functionality is rolled up into the application object. A MacApp main program does the following: initialize the Toolbox Managers and other needed functions, create the application object, call its initializing method, then call its Run method. Thats it, and thats usually always it; when Run returns the program execution ends.
Unit file (UQPlot.p and UQPlot.inc1.p)
MacApp source files have a strange arrangement forced upon them by MacApp. The .inc1 source file is organized by object hierarchy: first the application, then the document, and last the view (remember, the dialog view does not have a customized object because the standard dialog handling was sufficient for our purposes).
As we mentioned before, the application object is a simple one, since there is not too much to do. In order to handle the menu choices relating to the color selections, we need to override DoSetupMenus and DoMenuCommand, and both of those methods are pretty easy to understand. Remember, the DoSetupMenus method is called by MacApp frequently (you wont believe just how frequently) so there shouldnt be any lengthy calculations here. If you need to do some complicated calculations to determine if some menu item should be enabled, you should do it somewhere else (like in the idle chain) and set a flag or value in your object; that flag can then be used for the menu enabling. The only other method of interest is a real short one: HandleFinderRequest. MacApps default behavior for applications is that when an application is launched from the Finder by double clicking on the application icon, a blank document opens. Well, we dont want that to happen, since our document is the plot, and we wouldnt want a blank plot window on the screen. So, to modify this behavior, we just override HandleFinderRequest and do nothing. Now when the application is launched, our HandleFinderRequest is called instead of the default one, and we dont get a empty document. This method is a demonstration of the small things you sometimes have to do to make your application perfect -- and note that its not difficult, it just requires knowing whats going on and where to look. [Carls Note: In many ways this typifies the MacApp design - the methods are there to do the kind of things you will need to do - you just have to look for them.]
The document methods can be characterized into three categories: initialization, solving the quadratic equation, and saving the PICT data. Initialization methods are not very interesting, except to note that the DoMakeViews and DoMakeWindows methods are some more of the methods which you are required to OVERRIDE or else your program will not work (Actually thats not really the truth -- with MacApp 2.0 you can omit these overrides and your program will work, its just that youll get the default view and window size/location). The method titles are fairly self explanatory. Solving the quadratic equation involves a few methods which contain a lot of code lifted directly from the original plotter application. All I did for these methods was put the method syntax before and after them, and change the variable references to refer to the document objects variables instead of some global variables. The last few methods deal with saving the data to disk. As mentioned before, there are really two methods necessary to save data on disk: DoNeedDiskSpace -- which calculates if the file will fit on the disk, and DoWrite -- which actually writes out the data. If there seems to be a lot missing from the implementation of the disk handling, remember that MacApp does most of the work for us -- opening the file and checking for errors. Again, all we have to do is handle our applications specific needs, which in this case is saving the PICT.
The view object contains some initialization methods (which should be quite familiar by now). After that is a method called CalcMinSize. This method is called (by MacApp of course) when the size of the view needs to be determined. Remember, the view can be either the size of the window or the size of an entire page. So we need to have a flag (fOnePage) which tells us which is the case. If we are an entire page, we tell MacApp that our size is the size of our print handlers page (since we want the printout to be one page). If we are not the size of a page, we tell MacApp our size is the size of our superview (our superview is the view which we are inside. In the case of a simple view, our superview is the window itself). After the menu handling routines (DoSetupMenus and DoMenuCommand) we have a large method which prints the plot on the LaserWriter (again, this method was stolen directly from the original plotter application and had some method syntax wrapped around it). The next two methods were written to handle the case where the window is resized. In SuperViewChangedSize, we check to see if we are displaying the plot in full page mode. If not, when the window changes size we need to change the size of the view (since the view mirrors the window size). The next method, Resize, has been overridden because when the view size changes we want to force the view to be redrawn (i.e., when switching between full page and window size display modes). The last view method just draws the view, and it is also pretty simple.
Resource file (QPlot.r)
Resources for a MacApp program are almost identical to those for a non-MacApp program. There are only two differences (and they are big differences): cmnu instead of MENU and view resources. cmnu resources (probably stands for Command Menu) are used to associate menu items with command numbers. Carl and I have both spoken of the usefulness of command numbers, and I dont need to reiterate it here. The view resource is different, and is a crucial resource in your MacApp program. Carl will speak about view resources in greater detail, but let me say that the view resource allows you to specify the contents of your windows (in functional areas). You can (and will) use views to specify your dialogs in MacApp, because these dialogs are build out of views, as we mentioned earlier. The benefit of using a view resource is the same as you get anywhere else in using resources: you can change the resource without having to recompile the source code. While it may seem that this benefit is limited with regard to views, Im sure you can attest to the value of resources in other areas, and isnt it nice to be able to tweak a resource quickly?
One other thing you should know about MacApp resource files is that MacApp has a default number for the about box. If you can live with a boring about box (i.e., an ALRT) then you should create it with id 201. Thats because as part of the generic Macintosh application that MacApp implements, when you select the About Application from the Apple menu, you get ALRT 201 displayed. If you like that, its great, and as always, if you dont like it, you can easily override it. In fact, I override it in my program because I want to calculate some system parameters. Im sure that you realize the proper method is in the Application level object (since of course the application handles requests to display the about box).
Makefile (QPlot.MAmake)
The makefile is something many Lightspeed users are unfamiliar with. MacApp 2.0s MABuild process eliminates the need for a makefile in simple cases. I have provided a simple make file, its not much, as you can see from the listing. When your program becomes more complicated the makefile usually grows proportionally in complexity. For small projects made up of two or three files, the example makefiles in the MacApp samples folder can be used as guides (and of course, you can consult the manual for more explanation).
Trouble In Paradise
One last parenthetical note before we move over to Carls description of his program. When we both did the conversions, we independently had problems printing the plots on the LaserWriter -- PostScript errors. I tore my hair out about it for a few weeks, then decided to really dig down and see what was happening. By strategically inserting debugging statements into my code I finally isolated my bug to a PicComment call. The PicComment in question sets the linewidth, and looks like this:
PicComment(SetLineWidth,2,Handle(Width));
However, the real call should look like this:
PicComment(SetLineWidth,4,Handle(Width));
The problem was that the second parameter to the PicComment is supposed to be the size (in bytes) of the data. The original specification was wrong, yet it worked (we assume) because Lightspeed Pascal must be more forgiving than MPW. Not only was this line a bug, but the Daves even featured the bug in their article (top of page 21)!! Let this be a lesson that just because someone gives you code that works you cant assume that it is completely bug-free.
That just about covers my program; Next month, well hear about Carls.
Continued in next frame
|
|
Volume Number: | | 5
|
Issue Number: | | 8
|
Column Tag: | | MacApp Workshop
|
A Tale of Two Quadratic Plotters (code)
{--------------------------------------------}
{------------ File MQPlot.p ----------------}
{--------------------------------------------}
{$P}
{MQPlot.p}
{Copyright © 1989 by Charles F. McMath All rights reserved.}
{This is the main program for the MacApp
plotter application. The original plotter was
written by Dave Smith and Dave Kelly for MacTutor
Magazine. This coversion was done by Chuck
McMath to demonstrate the same program done using
MacApp. It also hopefully demonstrates some of
the strengths of MacApp!
)
PROGRAM QPlot;
USES
{ MacApp }
UMacApp,
{ Building Blocks }
UDialog, UPrinting,
{ Implementation Use }
SANE, ToolUtils, Fonts, Resources, Script,
PickerIntf, Packages,
{ the PlotUNIT }
UQPlot;
VAR
gQPlotApplication: TQPlotApplication;
BEGIN
InitUMacApp(5);
InitUDialog;
InitPrinting;
New(gQPlotApplication);
gQPlotApplication.
IQPlotApplication(kFileType);
gQPlotApplication.Run;
END.
{--------------------------------------------}
{------------ File UQPlot.p ----------------}
{--------------------------------------------}
{Copyright © 1989 by Charles F. McMath.
All rights reserved.}
UNIT UQPlot;
INTERFACE
USES
{ MacApp - this includes all of the
things necessary from the MacApp Library }
UMacApp,
{ Building Blocks }
UDialog, UPrinting,
{ Implementation Use }
SANE, ToolUtils, Fonts, Resources, Script,
PickerIntf, Packages;
CONST
kSignature = FGT3; {Appl. signature}
kFileType = PICT;{our filetype }
kQPlotWindowID = 1001;
TYPE
TQPlotApplication = OBJECT(TApplication)
fDefGraph: INTEGER; { Graph color index }
fDefAxis:INTEGER; { Axis color index }
fDefBack:INTEGER; { Back color index }
fPrintOpt: INTEGER; { Page/Window size }
PROCEDURE TQPlotApplication.
IQPlotApplication
(itsMainFileType:OSType);
FUNCTION TQPlotApplication.DoMakeDocument
(itsCmdNumber:CmdNumber): TDocument;
OVERRIDE;
PROCEDURE TQPlotApplication.
HandleFinderRequest; OVERRIDE;
PROCEDURE TQPlotApplication.DoSetupMenus;
OVERRIDE;
FUNCTION TQPlotApplication.DoMenuCommand
(aCmdNumber: CmdNumber): TCommand;
OVERRIDE;
PROCEDURE TQPlotApplication.ShowAboutApp;
END;
TQPlotDocument = OBJECT(TDocument)
fAParam: Real;{ these parameters }
fBParam: Real;{ correspond }
{ to those }
fCParam: Real;{ in the quadratic }
fStep: Real; { equation. }
fXScale: INTEGER; { X axis scale }
fYScale: INTEGER; { Y axis scale }
fResult: INTEGER; { real results? }
fRoot1:REAL; { first root }
fRoot2:REAL; { second root }
fPlotView: TQPlotView;
PROCEDURE TQPlotDocument.IQPlotDocument
(itsFileType, itsCreator: OSType;
usesDataFork, usesRsrcFork: BOOLEAN;
keepsDataOpen, keepsRsrcOpen:
BOOLEAN);
PROCEDURE TQPlotDocument.DoMakeWindows;
OVERRIDE;
PROCEDURE TQPlotDocument.DoMakeViews
(forPrinting: BOOLEAN); OVERRIDE;
PROCEDURE TQPlotDocument.PosePlotDialog;
PROCEDURE TQPlotDocument.Quad
(a, b, c : REAL;VAR x1, x2 : REAL;
VAR result : INTEGER);
FUNCTION TQPlotDocument.SolveIt
(a, b, c: REAL; VAR x1, x2: REAL):
INTEGER;
PROCEDURE TQPlotDocument.DoNeedDiskSpace
(VAR dataForkBytes,
rsrcForkBytes: LONGINT); OVERRIDE;
PROCEDURE TQPlotDocument.DoWrite
(aRefNum: INTEGER;
makingCopy: BOOLEAN); OVERRIDE;
END;
TQPlotView = OBJECT(TView)
fPlotDoc:TQPlotDocument; { link to doc }
fOnePage:BOOLEAN; { if TRUE, page size }
fDrawing:PicHandle; { our picture! }
PROCEDURE TQPlotView.IQPlotView
(theDoc: TQPlotDocument;
theGrafColor, theAxisColor,
theBackColor: INTEGER);
PROCEDURE TQPlotView.Free; OVERRIDE;
PROCEDURE TQPlotView.CalcMinSize
(VAR minSize: VPoint); OVERRIDE;
PROCEDURE TQPlotView.DoSetupMenus;
OVERRIDE;
FUNCTION TQPlotView.DoMenuCommand
(aCmdNumber: CmdNumber): TCommand;
OVERRIDE;
PROCEDURE TQPlotView.PrQDStuff
(pRect : rect; QDdevice : integer);
PROCEDURE TQPlotView.SuperViewChangedSize
(delta: VPoint;
invalidate: BOOLEAN); OVERRIDE;
PROCEDURE TQPlotView.Resize
(width, height: VCoordinate;
invalidate: BOOLEAN); OVERRIDE;
PROCEDURE TQPlotView.Draw(area: Rect); OVERRIDE;
END;
IMPLEMENTATION
{$I UQPlot.inc1.p}
END.
{--------------------------------------------}
{---------- File UQPlot.inc1.p --------------}
{--------------------------------------------}
{Copyright © 1989 by Charles F. McMath.
All rights reserved.}
CONST
kPlotWindowID = 1001;
kPlotDLOG= 2000;
kPlotID= PPRM;
kStaggerAmt= 16;
{ these are command numbers }
cGraphColor= 1201;
cAxisColor = 1202;
cBackColor = 1203;
cGrafBlack = 10001;
cGrafWhite = 10002;
cGrafRed = 10003;
cGrafGreen = 10004;
cGrafBlue= 10005;
cGrafCyan= 10006;
cGrafMagenta = 10007;
cGrafYellow= 10008;
cAxisBlack = 11001;
cAxisWhite = 11002;
cAxisRed = 11003;
cAxisGreen = 11004;
cAxisBlue= 11005;
cAxisCyan= 11006;
cAxisMagenta = 11007;
cAxisYellow= 11008;
cBackBlack = 12001;
cBackWhite = 12002;
cBackRed = 12003;
cBackGreen = 12004;
cBackBlue= 12005;
cBackCyan= 12006;
cBackMagenta = 12007;
cBackYellow= 12008;
cPrintWS = 1301;
cPrintPS = 1302;
(*
* The following constants relate to DLOG
* and ALRT numbers.
*)
kAboutDLOG = 1024; { our about box }
VAR
gStaggerCount: INTEGER;
colors:ARRAY [1..8] OF INTEGER;
crString:Str255;
{------------------------------------------------}
FUNCTION Int2Str(theInt: INTEGER): Str255;
(*
* This utility function takes an integer and
* converts it into a string.
*)
VAR
tempLong:LongInt;
tempStr: Str255;
BEGIN
tempLong := theInt;
NumToString(tempLong, tempStr);
Int2Str := tempStr;
END; { Int2Str }
{------------------------------------------------}
FUNCTION Real2String(theReal: REAL;
numDigits: INTEGER): Str255;
(*
* This function takes a real number,
* and converts it to a string with
* the specified number of digits PAST the
* decimal point.
*)
VAR
theForm: DecForm;
xTemp: Extended;
tempStr: DecStr;
BEGIN
theForm.style := FixedDecimal;
theForm.digits := numDigits;
xTemp := theReal;
Num2Str(theForm, xTemp, tempStr);
Real2String := tempStr;
END; { Real2String }
{------------------------------------------------}
FUNCTION String2Real(theString: Str255): REAL;
(*
* This function takes a string form of a real
* number, and converts it into the real number.
*)
VAR
xTemp: Extended;
BEGIN
xTemp := Str2Num(theString);
String2Real := Num2Real(xTemp);
END; { String2Real }
{------------------------------------------------}
{------ QPlot Application Methods ------------}
{------------------------------------------------}
PROCEDURE TQPlotApplication.IQPlotApplication
(itsMainFileType: OSType);
(*
* This procedure initializes the application.
* It sets up all of our global variables and
* fills the color index array.
*)
VAR
aQPlotView:TQPlotView;
BEGIN
IApplication(itsMainFileType);
crString[0] := CHR(1);
crString[1] := CHR(13);
colors[1] := 33; { black }
colors[2] := 30; { white }
colors[3] := 205;{ red }
colors[4] := 341;{ green }
colors[5] := 409;{ blue }
colors[6] := 273;{ cyan }
colors[7] := 137;{ magenta }
colors[8] := 69; { yellow }
fDefGraph := colors[3];
fDefAxis := colors[1];
fDefBack := colors[2];
fPrintOpt := 1;
gStaggerCount := 0;
END; { TQPlotApplication.IQPlotApplication }
{------------------------------------------------}
FUNCTION TQPlotApplication.DoMakeDocument
(itsCmdNumber: CmdNumber): TDocument;
OVERRIDE;
(*
* This function is called whenever were
* creating a new document. It creates the
* document object and returns it (properly
* initialized, of course).
*)
VAR
aQPlotDocument: TQPlotDocument;
BEGIN
New(aQPlotDocument);
FailNil(aQPlotDocument);
aQPlotDocument.IQPlotDocument
(kFileType, kSignature,
kUsesDataFork, NOT kUsesRsrcFork,
NOT kDataOpen, NOT kRsrcOpen);
aQPlotDocument.fSavePrintInfo := FALSE;
DoMakeDocument := aQPlotDocument;
END; { TQPlotApplication.DoMakeDocument }
{------------------------------------------------}
PROCEDURE TQPlotApplication.HandleFinderRequest;
OVERRIDE;
BEGIN
{ just override this so we dont put up a
blank document }
END; { TQPlotApplication.HandleFinderRequest }
{------------------------------------------------}
PROCEDURE TQPlotApplication.DoSetupMenus;
OVERRIDE;
(*
* This procedure enables the appropriate menu
* items for the application.
*)
BEGIN
INHERITED DoSetupMenus;
Enable(cGraphColor, TRUE);
Enable(cAxisColor, TRUE);
Enable(cBackColor, TRUE);
(*
* Enable all of these, and put a check by
* the current one.
*)
EnableCheck(cGrafBlack, TRUE,
fDefGraph=colors[1]);
EnableCheck(cGrafWhite, TRUE,
fDefGraph=colors[2]);
EnableCheck(cGrafRed, TRUE,
fDefGraph=colors[3]);
EnableCheck(cGrafGreen, TRUE,
fDefGraph=colors[4]);
EnableCheck(cGrafBlue, TRUE,
fDefGraph=colors[5]);
EnableCheck(cGrafCyan, TRUE,
fDefGraph=colors[6]);
EnableCheck(cGrafMagenta, TRUE,
fDefGraph=colors[7]);
EnableCheck(cGrafYellow, TRUE,
fDefGraph=colors[8]);
EnableCheck(cAxisBlack, TRUE,
fDefAxis=colors[1]);
EnableCheck(cAxisWhite, TRUE,
fDefAxis=colors[2]);
EnableCheck(cAxisRed, TRUE,
fDefAxis=colors[3]);
EnableCheck(cAxisGreen, TRUE,
fDefAxis=colors[4]);
EnableCheck(cAxisBlue, TRUE,
fDefAxis=colors[5]);
EnableCheck(cAxisCyan, TRUE,
fDefAxis=colors[6]);
EnableCheck(cAxisMagenta, TRUE,
fDefAxis=colors[7]);
EnableCheck(cAxisYellow, TRUE,
fDefAxis=colors[8]);
EnableCheck(cBackBlack, TRUE,
fDefBack=colors[1]);
EnableCheck(cBackWhite, TRUE,
fDefBack=colors[2]);
EnableCheck(cBackRed, TRUE,
fDefBack=colors[3]);
EnableCheck(cBackGreen, TRUE,
fDefBack=colors[4]);
EnableCheck(cBackBlue, TRUE,
fDefBack=colors[5]);
EnableCheck(cBackCyan, TRUE,
fDefBack=colors[6]);
EnableCheck(cBackMagenta, TRUE,
fDefBack=colors[7]);
EnableCheck(cBackYellow, TRUE,
fDefBack=colors[8]);
END; { TQPlotApplication.DoSetupMenus }
{------------------------------------------------}
FUNCTION TQPlotApplication.DoMenuCommand
(aCmdNumber: CmdNumber): TCommand;
OVERRIDE;
(*
* This function handles menu requests that
* pertain to the application. It ensures that
* choices that change colors work correctly.
*)
BEGIN
DoMenuCommand := gNoChanges;
CASE aCmdNumber OF
cAboutApp: ShowAboutApp;
cGrafBlack,
cGrafWhite,
cGrafRed,
cGrafGreen,
cGrafBlue,
cGrafCyan,
cGrafMagenta,
cGrafYellow:
fDefGraph := colors[aCmdNumber -
cGrafBlack + 1];
cAxisBlack,
cAxisWhite,
cAxisRed,
cAxisGreen,
cAxisBlue,
cAxisCyan,
cAxisMagenta,
cAxisYellow:
fDefAxis := colors[aCmdNumber -
cAxisBlack + 1];
cBackBlack,
cBackWhite,
cBackRed,
cBackGreen,
cBackBlue,
cBackCyan,
cBackMagenta,
cBackYellow:
fDefBack := colors[aCmdNumber -
cBackBlack + 1];
cPrintWS,
cPrintPS:
fPrintOpt := aCmdNumber -
cPrintWS + 1;
OTHERWISE
DoMenuCommand := INHERITED
DoMenuCommand(aCmdNumber);
END; { case }
END; { TQPlotApplication.DoMenuCommand }
{------------------------------------------------}
PROCEDURE DrawBox(wPtr: WindowPtr; itemNum: INTEGER);
(*
* This procedure is the UserItem proc that
* gets called for the about box.
*)
VAR
ityp: INTEGER;
itemHdl: Handle;
tRect: Rect;
BEGIN
GetDItem(DialogPtr(wPtr), 5, ityp, itemHdl, tRect);
FrameRect(tRect);
END; { DrawBox }
{------------------------------------------------}
PROCEDURE TQPlotApplication.ShowAboutApp;
(*
* Show our about box. This version is almost
* identical to the original, except for the
* addition of some stuff about MacApp
* (incidentally, this is required by the license
* agreement for programs developed with MacApp!)
*)
VAR
idStrHandle: StringHandle;
freeSpace: Size;
myHeapSpace: LongInt;
tempStr1:Str255;
tempStr2:Str255;
tempStr3:Str255;
diaPtr:DialogPtr;
itemHit: INTEGER;
ityp: INTEGER;
itemHdl: Handle;
tRect: Rect;
BEGIN
idStrHandle :=
StringHandle(GetResource(kSignature, 0));
FailNIL(idStrHandle);
MoveHHi(Handle(idStrHandle));
HLock(Handle(idStrHandle));
freeSpace := FreeMem;
myHeapSpace := MaxMem(freeSpace);
NumToString(myHeapSpace, tempStr2);
tempStr2 := concat(Memory = , tempStr2);
tempStr3 := ;
tempStr1 := ;
ParamText(idStrHandle^^, tempStr1, tempStr2, tempStr3);
diaPtr := GetNewDialog(kAboutDLOG, NIL, Pointer(-1));
FailNIL(diaPtr);
GetDItem(diaPtr, 5, ityp, itemHdl, tRect);
SetDItem(diaPtr, 5, ityp, Handle(@DrawBox), tRect);
InitCursor;
ModalDialog(NIL, itemHit);
DisposDialog(diaPtr);
HUnlock(Handle(idStrHandle));
END; { TQPlotApplication.ShowAboutApp }
{------------------------------------------------}
{-------- QPlot Document Methods ------------}
{------------------------------------------------}
PROCEDURE TQPlotDocument.IQPlotDocument
(itsFileType, itsCreator: OSType;
usesDataFork, usesRsrcFork: BOOLEAN;
keepsDataOpen, keepsRsrcOpen:
BOOLEAN);
(*
* Initialize the QPlot document. For our
* document, this means that we will present the
* dialog box that asks for the quadratic
* parameters, and well solve the equation while
* were here.
*)
VAR
x1, x2:REAL;
BEGIN
IDocument(itsFileType, itsCreator, usesDataFork, usesRsrcFork, keepsDataOpen,
keepsRsrcOpen);
PosePlotDialog;
fResult := SolveIt(fAParam, fBParam,
fCParam, x1, x2);
IF (fResult<>-1) THEN
BEGIN
fRoot1 := x1;
fRoot2 := x2;
END
ELSE
BEGIN
fRoot1 := -999;
fRoot2 := -999
END;
fPlotView := NIL;
END; { TQPlotDocument.IQPlotDocument }
{------------------------------------------------}
PROCEDURE TQPlotDocument.DoMakeWindows; OVERRIDE;
(*
* This procedure is called when we have to
* make a new document (and its associated
* window). We just get a simple window and shove
* it on the screen.
*)
VAR
aWindow: TWindow;
BEGIN
aWindow := NewSimpleWindow(kPlotWindowID,
kWantHScrollBar, kWantVScrollBar, SELF, fPlotView);
aWindow.SimpleStagger(kStaggerAmt,
kStaggerAmt, gStaggerCount);
END; { TQPlotDocument.DoMakeWindows }
{------------------------------------------------}
PROCEDURE TQPlotDocument.DoMakeViews
(forPrinting: BOOLEAN); OVERRIDE;
(*
* This procedure is called when we are making
* a new document. It creates all views that are
* needed. In our case, its only one. We then
* initialize the view.
*)
VAR
plotApp: TQPlotApplication;
aQPlotView:TQPlotView;
aHandler:TStdPrintHandler;
tRect: Rect;
BEGIN
plotApp := TQPlotApplication(gApplication);
NEW(aQPlotView);
FailNIL(aQPlotView);
aQPlotView.IQPlotView(SELF,
plotApp.fDefGraph, plotApp.fDefAxis,
plotApp.fDefBack);
fPlotView := aQPlotView;
(*
* Now make the view printable by creating a
* print handler.
*)
IF NOT(forPrinting) THEN
BEGIN
New(aHandler);
FailNIL(aHandler);
aHandler.IStdPrintHandler(SELF,
aQPlotView, FALSE, TRUE, TRUE);
{ set up for 1/2" margins }
SetRect(tRect,35,35,-35,-35);
aHandler.InstallMargins(tRect, FALSE);
END;
END; { TQPlotDocument.DoMakeViews }
{------------------------------------------------}
PROCEDURE TQPlotDocument.PosePlotDialog;
(*
* This procedure puts up the dialog that
* requests the quadratic parameters.
*)
FUNCTION CvtEditReal(aText: TEditText):
Real;
VAR
tempStr: Str255;
BEGIN
aText.GetText(tempStr);
CvtEditReal := String2Real(tempStr);
END; { CvtEditReal }
FUNCTION CvtEditInt(aText: TEditText):
INTEGER;
VAR
tempStr: Str255;
tempLong:LongInt;
BEGIN
aText.GetText(tempStr);
StringToNum(tempStr, tempLong);
CvtEditInt := tempLong;
END; { CvtEditInt }
VAR
aWindow: TWindow;
dismisser: IDType;
aQPlotView:TQPlotView;
dView: TDialogView;
anEditText:TEditText;
aVal: TEditText;
bVal: TEditText;
cVal: TEditText;
stepVal: TEditText;
xVal: TEditText;
yVal: TEditText;
tempStr: Str255;
BEGIN
(*
* First make our calls (which never get
* executed) to tell the linker we want to create
* these types of objects.
*)
IF gCreateWithTemplates THEN
BEGIN
NEW(aQPlotView);
NEW(dView);
NEW(anEditText);
END;
(*
* Get the dialog window and find the dialog subview.
*)
aWindow := NewTemplateWindow(kPlotDLOG, NIL);
dView := TDialogView(aWindow.
FindSubView(kPlotID));
(*
* Retrieve references to each of the edit
* text items. We will need to reference them
* later to get their values.
*)
aVal := TEditText(dView.FindSubView(a ));
bVal := TEditText(dView.FindSubView(b ));
cVal := TEditText(dView.FindSubView(c ));
stepVal := TEditText(dView.FindSubView(step));
xVal := TEditText(dView.FindSubView(xscl));
yVal := TEditText(dView.FindSubView(yscl));
dView.SelectEditText(a , kRedraw);
dismisser := dView.PoseModally;
(*
* Now that we have the values, convert them
* to numbers and plug them into the quadratic
* equation solver. Note that we dont test to
* see if the OK button was pressed, because in
* this dialog, you cant exit except by saying
* OK.
*)
fAParam := CvtEditReal(aVal);
fBParam := CvtEditReal(bVal);
fCParam := CvtEditReal(cVal);
fStep := CvtEditReal(stepVal);
fXScale := CvtEditInt(xVal);
fYScale := CvtEditInt(yVal);
aWindow.Close;
END; { TQPlotDocument.PosePlotDialog }
{------------------------------------------------}
PROCEDURE TQPlotDocument.Quad(a, b, c : REAL;
VAR x1, x2 : REAL;
VAR result : INTEGER);
(*
* This procedure is identical to the one
* written in vanilla Pascal
*)
VAR check : real;
FUNCTION PositiveCalc
(a, b, check : real) : real;
BEGIN
PositiveCalc := (-b + sqrt(check)) /
(2 * a);
END; { PositiveCalc ---------- }
FUNCTION NegativeCalc
(a, b, check : real) : real;
BEGIN
NegativeCalc := (-b - sqrt(check)) /
(2 * a);
END; { NegativeCalc ---------- }
BEGIN
result := 0;
check := (b * b) - (4 * a * c);
IF result = 0 THEN
BEGIN
{ Check if double root exists }
IF check = 0 THEN
BEGIN
result := 2;
x1 := positivecalc(a, b, check);
x2 := x1;
END;
{ Check if real result}
IF check > 0 THEN
BEGIN
result := 1;
x1 := positivecalc(a, b, check);
x2 := negativecalc(a, b, check);
END;
{ Check if root is complex }
IF check < 0 THEN
BEGIN
result := 3;
check := -check;
x1 := positivecalc(a, b, check);
x2 := negativecalc(a, b, check);
END;
END;
END; { TQPlotDocument.Quad }
{------------------------------------------------}
FUNCTION TQPlotDocument.SolveIt(a, b, c: REAL;
VAR x1, x2: REAL): INTEGER;
(*
* This function is identical to the one
* written for the original program.
*)
VAR
result:INTEGER;
BEGIN
IF (a <> 0) THEN
quad(a, b, c, x1, x2, result)
ELSE
result := -1;
SolveIt := result;
END; { TQPlotDocument.SolveIt }
{------------------------------------------------}
PROCEDURE TQPlotDocument.DoNeedDiskSpace
(VAR dataForkBytes,
rsrcForkBytes: LONGINT); OVERRIDE;
(*
* This procedure calculates the amount of
* disk space we need to store our document on
* disk. In our case, its just the size of the
* drawing since no additional information is
* stored in our file.
*)
BEGIN
INHERITED DoNeedDiskSpace(dataForkBytes, rsrcForkBytes);
dataForkBytes := dataForkBytes +
512 { header } +
GetHandleSize(Handle(fPlotView.fDrawing));
END; { TQPlotDocument.DoNeedDiskSpace }
{------------------------------------------------}
PROCEDURE TQPlotDocument.DoWrite(aRefNum: INTEGER;
makingCopy: BOOLEAN); OVERRIDE;
(*
* This procedure is called to write the data
* onto the disk.
*)
VAR
count: LongInt;
buffer:ARRAY [1..256] OF INTEGER;
pHdl: PicHandle;
i:INTEGER;
err: OSErr;
BEGIN
INHERITED DoWrite(aRefNum, makingCopy);
(*
* Now write the blank 512 byte header needed
* for PICT files.
*)
FOR i:= 1 TO 256 DO
buffer[i] := 0;
count := 512;
err := FSWrite(aRefNum, count, @buffer);
IF ((err<>noErr) OR (count<>512)) THEN
SysBeep(10); { @@@ error alert }
pHdl := fPlotView.fDrawing;
HLock(Handle(pHdl));
count := GetHandleSize(Handle(pHdl));
err := FSWrite(aRefNum, count, Ptr(pHdl^));
HUnlock(Handle(pHdl));
IF ((err<>noErr) OR
(count <> GetHandleSize(Handle(pHdl)))) THEN
SysBeep(10); { @@@ error alert }
END; { TQPlotDocument.DoWrite }
{------------------------------------------------}
{---------- QPlot View Methods ----------------}
{------------------------------------------------}
PROCEDURE TQPlotView.IQPlotView(theDoc:
TQPlotDocument; theGrafColor,
theAxisColor, theBackColor: INTEGER);
(*
* This procedure is called to initialize a
* QPlot view. It sets the views variables to
* good initial values.
*)
VAR
vOrigin: VPoint;
vSize: VPoint;
tempPort:GrafPtr;
BEGIN
SetVPt(vOrigin, 0, 0);
SetVPt(vSize, 600, 400);
IView(theDoc, NIL, vOrigin, vSize, sizeVariable, sizeVariable);
fPlotDoc := theDoc;
fDrawing := NIL;
fOnePage := FALSE;
END; { TQPlotView.IQPlotView }
{------------------------------------------------}
PROCEDURE TQPlotView.Free; OVERRIDE;
(*
* This procedure is called when the view is
* finished, and we are cleaning up any space we
* have allocated. We just dispose of the picture
* handle.
*)
BEGIN
KillPicture(fDrawing);
INHERITED Free;
END; { TQPlotView.Free }
{------------------------------------------------}
PROCEDURE TQPlotView.CalcMinSize
(VAR minSize: VPoint); OVERRIDE;
(*
* This procedure is called to calculate the
* minimum size of the view. It is called
* initially when the view is being created, and
* later on when we tell the view to recalculate
* its view size.
*)
VAR
vSize: Point;
tWind: TWindow;
tRect: Rect;
BEGIN
INHERITED CalcMinSize(minSize);
IF fOnePage THEN
BEGIN
minSize := fPrintHandler.fViewPerPage;
END
ELSE
BEGIN
IF (fSuperView<>NIL) THEN
minSize := fSuperView.fSize
ELSE
BEGIN
vSize := Point(0);
IF SELF.Focus THEN ;
QDToViewPt(vSize, minSize);
END;
END;
END; { TQPlotView.CalcMinSize }
{------------------------------------------------}
PROCEDURE TQPlotView.DoSetupMenus; OVERRIDE;
(*
* This procedure is called to enable menu
* items that pertain to the view.
*)
BEGIN
INHERITED DoSetupMenus;
EnableCheck(cPrintWS, TRUE, NOT fOnePage);
EnableCheck(cPrintPS, TRUE, fOnePage);
END; { TQPlotView.DoSetupMenus }
{------------------------------------------------}
FUNCTION TQPlotView.DoMenuCommand
(aCmdNumber: CmdNumber): TCommand;
OVERRIDE;
(*
* This function is called to handle menu
* commands that pertain to the view.
*)
VAR
tempVPt: VPoint;
tRect: Rect;
BEGIN
DoMenuCommand := gNoChanges;
IF (aCmdNumber = cPrintWS) OR
(aCmdNumber = cPrintPS) THEN
BEGIN
fOnePage := (aCmdNumber=cPrintPS);
AdjustSize;
END
ELSE
DoMenuCommand :=
INHERITED DoMenuCommand(aCmdNumber);
END; { TQPlotView.DoMenuCommand }
{------------------------------------------------}
PROCEDURE TQPlotView.PrQDStuff(pRect : rect;
QDdevice : integer);
(*
* This is the main drawing procedure for the
* view. It is (almost) unchanged from the
* original version. Since in MacApp you dont
* know who youre drawing for (Display vs.
* LaserWriter) most of the display-specific code
* has been deleted. Drawing still works
* correctly, since we erase the invalid
* areas before we draw it.
*)
CONST
Display = 1;
LaserWriter = 2;
ImageWriter = 3;
NoJust = 0;
LeftJust = 1;
CenterJust = 2;
RightJust = 3;
FullJust = 4;
LinesInParagraph = 5;
{selected MacDraw comments}
picDwgBeg = 130;
picDwgEnd = 131;
picGrpBeg = 140;
picGrpEnd = 141;
TextBegin = 150;
TextEnd = 151;
StringBegin = 151;
StringEnd = 153;
TextCenter = 154;
{postscript comments}
SetLineWidth = 182;
PostScriptBegin = 190;
TextIsPostscript = 194;
PostScriptEnd = 191;
TYPE
widhdl = ^widptr;
widptr = ^widpt;
widpt = Point;
TTxtPicRec = PACKED RECORD
tJus : Byte;
tFlip : Byte;
tRot : Integer;
tLine : Byte;
tCmnt : Byte;
END;
VAR
le, tp, ri, bo : integer;
str1, str2, str3, str4, str5 : str255;
str6, str7, str8, str9 : str255;
hPos, vPos, hor, ver : integer;
x, y, z1, z2 : real;
rBox, ClipBox : rect;
Width : Widhdl;
leading : integer;
LineNo : integer;
ParagraphBegin : Point;
Indent : integer;
Paragraph : ARRAY[1..LinesInParagraph]
OF str255;
TxtPicRec : TTxtPicRec;
TxtPicPtr : QDPtr;
TxtPicHdl : QDHandle;
TextClipRgn : RgnHandle;
SaveClip : RgnHandle;
fInfo : FontInfo;
BEGIN
SaveClip := NewRgn;
GetClip(SaveClip);
ClipRect(pRect);
TextClipRgn := NewRgn;
penNormal;
TextFont(geneva);
TextSize(10);
TextFace([]);
hor := (pRect.right - pRect.left) DIV 2;
ver := (pRect.bottom - pRect.top) DIV 2;
Width := Widhdl(NewHandle(sizeof(widpt)));
Width^^.h := 10;
Width^^.v := 1;
TxtPicPtr := @TxtPicRec;
TxtPicHdl := @TxtPicPtr;
TxtPicRec.tJus := LeftJust;
TxtPicRec.tFlip := 0; {no flip}
TxtPicRec.tRot := 0; {no rotation}
TxtPicRec.tLine := 2; {1 1/2 spacing}
GetFontInfo(fInfo);
leading := fInfo.descent + fInfo.ascent +
fInfo.leading;
Indent := 2;
WITH fPlotDoc DO
BEGIN
x := -fXScale / 2;
y := fAParam * x * x + (fBParam * x)
+ fCParam;
hPos := INTEGER(ROUND(x * hor * 2 / fXScale + hor));
vPos := INTEGER(round(-y * ver * 2 / fYScale + ver));
z1 := -fBParam / (2 * fAParam);
z2 := (4 * fAParam * fCParam -
(fBParam * fBParam)) / (4 * fAParam);
END;
le := 2;
tp := ver + (ver DIV 3);
ri := 140;
IF ri >= (hor + hor DIV 3) THEN
ri := hor + hor DIV 3;
bo := ver + ver - 2;
setRect(rBox, le, tp - 14, ri, bo);
ParagraphBegin.h := 4;
ParagraphBegin.v := tp;
{Graph Text}
WITH fPlotDoc DO
BEGIN
str1 := Int2Str(-fXScale DIV 2);
str2 := Int2Str(fYScale DIV 2);
str3 := Int2Str(fXScale DIV 2);
str4 := Int2Str(-fYScale DIV 2);
Paragraph[1] := CONCAT(
y=ax^2 + bx + c, crString);
Paragraph[2] := CONCAT(a=,
Real2String(fAParam,1),
, b=,
Real2String(fBParam,1),
, c=,
Real2String(fCParam,1),
crString);
Paragraph[3] := CONCAT(x1=,
Real2String(fRoot1,2),
, x2=,
Real2String(fRoot2,2),
crString);
END;
CASE fPlotDoc.fResult OF
1 :
Paragraph[4] :=
CONCAT(
Two Real Roots, x1, x2,
crString);
2 :
Paragraph[4] :=
CONCAT(Double Root,
crString);
3 :
Paragraph[4] :=
CONCAT(Two Complex Roots ,
crString);
OTHERWISE
;
END;
Paragraph[5] := CONCAT(Slope 0 = (,
Real2String(z1, 1),
,,Real2String(z2,1), ),
crString);
PenNormal;
BackColor(TQPlotApplication(gApplication).
fDefBack);
ForeColor(TQPlotApplication(gApplication).
fDefAxis);
{Drawing Boundary}
{Begin MacDraw Document}
PicComment(picDwgBeg, 0, NIL);
PicComment(picGrpBeg, 0, NIL);
PicComment(SetLineWidth,
GetHandleSize(Handle(Width)),
Handle(Width));
IF QDdevice = Display THEN
FillRect(pRect, white);
FrameRect(pRect);
{Two Axis}
PicComment(picGrpBeg, 0, NIL);
moveto(0, ver);
line(hor + hor, 0);
moveto(hor, 0);
line(0, ver + ver);
PicComment(picGrpEnd, 0, NIL);
ForeColor(TQPlotApplication(gApplication).
fDefGraph);
{Plot Itsef}
PicComment(picGrpBeg, 0, NIL);
moveto(hPos, vPos);
WITH fPlotDoc DO
REPEAT
x := x + fStep;
y := fAParam * x * x + (fBParam * x) + fCParam;
hPos := integer(round(x * hor * 2 / fXScale + hor));
vPos := integer(round(-y * ver * 2 / fYScale + ver));
WITH pRect DO
IF (hPos < right) AND
(hPos > left) AND
(vPos < bottom) AND
(vPos > top) THEN
LineTo(hPos, vPos)
ELSE
moveto(hPos, vPos);
UNTIL x >= fXScale / 2;
PicComment(picGrpEnd, 0, NIL);
ForeColor(Colors[1]);
{Axis Text}
moveto(4, ver + 14);
DrawString(str1);
moveto(hor - 40, 14);
DrawString(str2);
moveto(hor + hor - 50, ver + 14);
DrawString(str3);
moveto(hor - 40, ver + ver - 14);
DrawString(str4);
{Box }
PicComment(picGrpBeg, 0, NIL);
PicComment(picGrpBeg, 0, NIL);
PicComment(SetLineWidth,
GetHandleSize(Handle(Width)),
Handle(Width));
IF QDdevice = Display THEN
fillRect(rBox, white);
frameRect(rBox);
PicComment(picGrpEnd, 0, NIL); {of box}
GetClip(TextClipRgn);
ClipBox := rBox;
ClipRect(ClipBox);
{Box Text}
PicComment(TextBegin, sizeof(TTxtPicRec),
Handle(TxtPicHdl));
FOR LineNo := 1 TO LinesInParagraph DO
BEGIN
moveto(ParagraphBegin.h,
ParagraphBegin.v);
move(Indent, (LineNo - 1) *
leading);
DrawString(Paragraph[LineNo]);
END;
PicComment(TextEnd, 0, NIL);
PicComment(PicGrpEnd, 0, NIL);{of Box/text}
PicComment(PicGrpEnd, 0, NIL);{of sel. all}
picComment(picDwgEnd, 0, NIL); {of drawing}
SetClip(SaveClip);
disposHandle(handle(width));
DisposeRgn(TextClipRgn);
DisposeRgn(SaveClip);
END; { TQPlotView.PrQDStuff }
{------------------------------------------------}
PROCEDURE TQPlotView.SuperViewChangedSize
(delta: VPoint; invalidate: BOOLEAN);
OVERRIDE;
(*
* This procedure changes the view size when
* the window is resized.
*)
BEGIN
(*
* We only need to go through these
* shenanigans if we are NOT displaying a page-
* sized picture. This resizes the views extent
* rectangle and invalidates the area, forcing the
* view to redraw the picture in the new size.
*)
IF NOT(fOnePage) THEN
BEGIN
AdjustSize;
ForceRedraw;
END;
END; { TQPlotView.SuperViewChangedSize }
{------------------------------------------------}
PROCEDURE TQPlotView.Resize (width,
height: VCoordinate; invalidate:
BOOLEAN); OVERRIDE;
(*
* This procedure is called from AdjustSize.
* It is here because when we toggle between Page
* Size and Window Size plots, we need to tell the
* view to redraw itself (because it changed size,
* even though the window didnt).
*)
BEGIN
ForceRedraw;
INHERITED Resize(width, height, FALSE);
ForceRedraw;
END; { TQPlotView.Resize }
{------------------------------------------------}
PROCEDURE TQPlotView.Draw(area: Rect); OVERRIDE;
(*
* This procedure is called to draw the view
* when it needs to be drawn. The picture we
* created before is ALWAYS drawn to take
* up the entire window.
*)
VAR
tRect: Rect;
BEGIN
GetQDExtent(tRect); { always draw pict to
fill entire extent of the view }
EraseRect(tRect);
IF (fDrawing=NIL) THEN
BEGIN
fDrawing := OpenPicture(tRect);
PrQDStuff(tRect, 1);
ClosePicture;
PenNormal;
END;
DrawPicture(fDrawing, tRect);
END; { TQPlotView.Draw }
{--------------------------------------------}
{------------ File UQPlot.r ----------------}
{--------------------------------------------}
/* Copyright © 1989 by Charles F. McMath.
All rights reserved. */
#ifdef Debugging
include Debug.rsrc;
#endif
include MacApp.rsrc;
include Printing.rsrc;
include Dialog.rsrc;
include QPlot CODE;
resource BNDL (128) {
FGT3,
0,
{ ICN#,
{ 0, 128, 1, 129 },
FREF,
{0, 128, 1, 129 }
}
};
resource FREF (128) {
APPL, 0,
};
resource FREF (129) {
PICT, 1,
};
type FGT3 as STR ;
resource FGT3 (0) {
© by Dave Kelly & Dave Smith \nver
4 JAN 1988;
MacApp version by Chuck McMath
};
resource WIND (1001, purgeable) {
{45, 15, 445, 615},
zoomDocProc,
invisible,
goAway,
0x0,
<<<>>>
};
resource view (1001, purgeable) {
{
root, WIND, { 50, 20 }, { 260, 430 },
sizeVariable, sizeVariable, shown,
enabled,
Window { TWindow, zoomDocProc,
goAwayBox, resizable,
modeless,
ignoreFirstClick,
freeOnClosing,
disposeOnFree,
closesDocument,
openWithDocument,
dontAdaptToScreen,
stagger, forceOnScreen,
dontCenter, QPLT, };
WIND, SCLR, { 0, 0 },
{ 260-kSBarSizeMinus1,
430-kSBarSizeMinus1 },
sizeRelSuperView,
sizeRelSuperView,
shown, enabled,
Scroller { TScroller,
vertScrollBar,
horzScrollBar,
0, 0, 16, 16,
vertConstrain,
horzConstrain,
{ 0, 0, 0, 0 } };
SCLR,IncludeViews { 1002 }
}
};
resource view (1002, purgeable) {
{
root, QPLT,
{ 0, 0 }, { 134, 414 },
sizeFixed, sizeFixed,
shown, enabled,
View { TQPlotView}
}
};
resource view (2000, purgeable) {
{ /* array viewArray: 15 elements */
root, noID,
{ 100, 105 }, { 150, 300 },
sizeVariable, sizeVariable,
notShown, enabled,
Window {
TWindow,
documentProc,
noGoAwayBox,
notResizable,
modal,
ignoreFirstClick,
freeOnClosing,
disposeOnFree,
doesntCloseDocument,
dontOpenWithDocument,
dontAdaptToScreen,
dontStagger,
dontForceOnScreen,
center,
noID,
Plot Parameters
},
/* [2] */
root, PPRM,
{0, 0},{150, 300}, sizeVariable,
sizeVariable, shown, disabled,
DialogView { TDialogView, ok ,
noID },
/* [3] */
PPRM, ok ,
{110, 230}, {26, 45}, sizeFixed,
sizeFixed, shown, enabled,
Button {
TButton,
0b0,
{1, 1},
sizeable,
notDimmed,
notHilited,
dismisses,
{0, 0, 0, 0},
plain,
0,
{0x0,0x0,0x0},
,
OK
},
/* [4] */
PPRM, a ,
{30, 15},{20, 45},
sizeFixed, sizeFixed,
shown, enabled,
EditText {
TEditText,
0b1111,
{1, 1},
sizeable,
notDimmed,
notHilited,
doesntDismiss,
{2, 2, 2, 2},
plain,
0,
{0x0,0x0,0x0},
,
justLeft,
1,
unlimited,
0b1111000000000
0000000000100000000
},
/* [5] */
PPRM, b ,
{30, 100},{20, 45}, sizeFixed,
sizeFixed, shown, enabled,
EditText {
TEditText,
0b1111,
{1, 1},
sizeable,
notDimmed,
notHilited,
doesntDismiss,
{2, 2, 2, 2},
plain,
0,
{0x0,0x0,0x0},
,
justLeft,
-1,
unlimited,
0b111100000000000
00000000100000000
},
/* [6] */
PPRM, c ,
{30, 180},{20, 45}, sizeFixed,
sizeFixed, shown, enabled,
EditText {
TEditText,
0b1111,
{1, 1},
sizeable,
notDimmed,
notHilited,
doesntDismiss,
{2, 2, 2, 2},
plain,
0,
{0x0,0x0,0x0},
,
justLeft,
-6,
unlimited,
0b1111000000000000000
0000100000000
},
/* [7] */
PPRM, step,
{80, 10},{20, 55}, sizeFixed,
sizeFixed, shown, enabled,
EditText {
TEditText,
0b1111,
{1, 1},
sizeable,
notDimmed,
notHilited,
doesntDismiss,
{2, 2, 2, 2},
plain,
0,
{0x0,0x0,0x0},
,
justLeft,
.05,
unlimited,
0b11110000000000000000
000100000000
},
/* [8] */
PPRM, xscl,
{80, 100},{20, 50}, sizeFixed,
sizeFixed, shown, enabled,
EditText {
TEditText,
0b1111,
{1, 1},
sizeable,
notDimmed,
notHilited,
doesntDismiss,
{2, 2, 2, 2},
plain,
0,
{0x0,0x0,0x0},
,
justLeft,
10,
unlimited,
0b1111000000000000000
0000100000000
},
/* [9] */
PPRM, yscl,
{80, 185},{20, 50}, sizeFixed,
sizeFixed, shown, enabled,
EditText {
TEditText,
0b1111,
{1, 1},
sizeable,
notDimmed,
notHilited,
doesntDismiss,
{2, 2, 2, 2},
plain,
0,
{0x0,0x0,0x0},
,
justLeft,
20,
unlimited,
0b111100000000000000000
00100000000
},
/* [10] */
PPRM, alab,
{10, 35},{16, 20},
sizeFixed, sizeFixed,
shown, disabled,
StaticText {
TStaticText,
0b0,
{1, 1},
sizeable,
notDimmed,
notHilited,
doesntDismiss,
{0, 0, 0, 0},
plain,
0,
{0x0,0x0,0x0},
,
justLeft,
a
},
/* [11] */
PPRM, blab,
{10, 120},{16, 20}, sizeFixed,
sizeFixed, shown, disabled,
StaticText {
TStaticText,
0b0,
{1, 1},
sizeable,
notDimmed,
notHilited,
doesntDismiss,
{0, 0, 0, 0},
plain,
0,
{0x0,0x0,0x0},
,
justLeft,
b
},
/* [12] */
PPRM, clab,
{10, 200},{16, 20}, sizeFixed,
sizeFixed, shown, disabled,
StaticText {
TStaticText,
0b0,
{1, 1},
sizeable,
notDimmed,
notHilited,
doesntDismiss,
{0, 0, 0, 0},
plain,
0,
{0x0,0x0,0x0},
,
justLeft,
c
},
/* [13] */
PPRM, slab,
{60, 10},{16, 55},
sizeFixed, sizeFixed,
shown, disabled,
StaticText {
TStaticText,
0b0,
{1, 1},
sizeable,
notDimmed,
notHilited,
doesntDismiss,
{0, 0, 0, 0},
plain,
0,
{0x0,0x0,0x0},
,
justLeft,
step size
},
/* [14] */
PPRM, xlab,
{60, 95},{16, 55},
sizeFixed, sizeFixed,
shown, disabled,
StaticText {
TStaticText,
0b0,
{1, 1},
sizeable,
notDimmed,
notHilited,
doesntDismiss,
{0, 0, 0, 0},
plain,
0,
{0x0,0x0,0x0},
,
justLeft,
x scale
},
/* [15] */
PPRM, ylab,
{60, 180},{16, 70}, sizeFixed,
sizeFixed, shown, disabled,
StaticText {
TStaticText,
0b0,
{1, 1},
sizeable,
notDimmed,
notHilited,
doesntDismiss,
{0, 0, 0, 0},
plain,
0,
{0x0,0x0,0x0},
,
justLeft,
y scale
}
}
};
resource SIZE (-1) {
dontSaveScreen,
acceptSuspendResumeEvents,
enableOptionSwitch,
canBackground,
MultiFinderAware,
backgroundAndForeground,
dontGetFrontClicks,
ignoreChildDiedEvents,
is32BitCompatible,
reserved,
reserved,
reserved,
reserved,
reserved,
reserved,
reserved,
#if qDebug
275 * 1024,
200 * 1024
#else
(250-32) * 1024,
(175-32) * 1024
#endif
};
resource DLOG (1024) {
{100, 100, 318, 416},
dBoxProc,
-1,
noGoAway,
0x0,
1024,
About Plotter
};
resource DITL (1024, purgeable) {
{ /* array DITLarray: 5 elements */
/* [1] */
{112, 235, 141, 284},
Button {
enabled,
OK
},
/* [2] */
{10, 88, 141, 289},
StaticText {
disabled,
Plot Demo\n\nGraphs
Quadratic Equations\n^0
\n^1\n^2\n^3
},
/* [3] */
{10, 10, 96, 81},
Picture {
disabled,
128
},
/* [4] */
{154, 22, 208, 283},
StaticText {
disabled,
This application brought
to you courtesy
of MacApp® 2.0.
Copyright 1985-1988
Apple Computer, Inc.
},
/* [5] */
{149, 17, 211, 290},
UserItem {
disabled
}
}
};
resource cmnu (1) {
1,
textMenuProc,
0x7FFFFFFD,
enabled,
apple,
{/* array: 2 elements */
/* [1] */
About Plotter , noIcon, noKey, noMark,
plain, cAboutApp;
/* [2] */
-, noIcon, noKey, noMark, plain, nocommand
}
};
resource cmnu (2) {
2,
textMenuProc,
allEnabled,
enabled,
File,
{/* array: 8 elements */
/* [1] */
Plot , noIcon, P, noMark, plain, cNew;
/* [2] */
-, noIcon, noKey, noMark, plain, nocommand;
/* [3] */
Save, noIcon, S, noMark, plain, cSave;
/* [4] */
Save As , noIcon, noKey, noMark, plain,
cSaveAs;
/* [5] */
Page Setup , noIcon, U, noMark, plain,
cPageSetup;
/* [6] */
Print , noIcon, O, noMark, plain, cPrint;
/* [7] */
-, noIcon, noKey, noMark, plain, nocommand;
/* [8] */
Quit, noIcon, Q, noMark, plain, cQuit
}
};
resource cmnu (3) {
3,
textMenuProc,
allEnabled,
enabled,
Edit,
{/* array: 6 elements */
/* [1] */
Undo, noIcon, Z, noMark, plain, cUndo;
/* [2] */
-, noIcon, noKey, noMark, plain, nocommand;
/* [3] */
Cut, noIcon, X, noMark, plain, cCut;
/* [4] */
Copy, noIcon, C, noMark, plain, cCopy;
/* [5] */
Paste, noIcon, V, noMark, plain, cPaste;
/* [6] */
Clear, noIcon, noKey, noMark, plain, cClear
}
};
resource cmnu (4) {
4,
textMenuProc,
allEnabled,
enabled,
Graph,
{/* array: 3 elements */
/* [1] */
Graph, noIcon, \0x1B, \0x0A, plain,
1201;
/* [2] */
Axis, noIcon, \0x1B, \0x0B, plain, 1202;
/* [3] */
Background, noIcon, \0x1B, \0x0C, plain,
1203
}
};
resource cmnu (5) {
5,
textMenuProc,
allEnabled,
enabled,
Print Options,
{/* array: 2 elements */
/* [1] */
Window Size, noIcon, [, noMark, plain,
1301;
/* [2] */
Page Size, noIcon, ], noMark, plain, 1302
}
};
resource cmnu (10) {
10,
textMenuProc,
allEnabled,
enabled,
Graph Colors,
{/* array: 8 elements */
/* [1] */
Black, noIcon, noKey, noMark, plain, 10001;
/* [2] */
White, noIcon, noKey, noMark, plain, 10002;
/* [3] */
Red, noIcon, noKey, noMark, plain, 10003;
/* [4] */
Green, noIcon, noKey, noMark, plain, 10004;
/* [5] */
Blue, noIcon, noKey, noMark, plain, 10005;
/* [6] */
Cyan, noIcon, noKey, noMark, plain, 10006;
/* [7] */
Magenta, noIcon, noKey, noMark, plain,
10007;
/* [8] */
Yellow, noIcon, noKey, noMark, plain, 10008
}
};
resource cmnu (11) {
11,
textMenuProc,
allEnabled,
enabled,
Axis Colors,
{/* array: 8 elements */
/* [1] */
Black, noIcon, noKey, noMark, plain, 11001;
/* [2] */
White, noIcon, noKey, noMark, plain, 11002;
/* [3] */
Red, noIcon, noKey, noMark, plain, 11003;
/* [4] */
Green, noIcon, noKey, noMark, plain, 11004;
/* [5] */
Blue, noIcon, noKey, noMark, plain, 11005;
/* [6] */
Cyan, noIcon, noKey, noMark, plain, 11006;
/* [7] */
Magenta, noIcon, noKey, noMark, plain,
11007;
/* [8] */
Yellow, noIcon, noKey, noMark, plain, 11008
}
};
resource cmnu (12) {
12,
textMenuProc,
allEnabled,
enabled,
Background Colors,
{/* array: 8 elements */
/* [1] */
Black, noIcon, noKey, noMark, plain, 12001;
/* [2] */
White, noIcon, noKey, noMark, plain, 12002;
/* [3] */
Red, noIcon, noKey, noMark, plain, 12003;
/* [4] */
Green, noIcon, noKey, noMark, plain, 12004;
/* [5] */
Blue, noIcon, noKey, noMark, plain, 12005;
/* [6] */
Cyan, noIcon, noKey, noMark, plain, 12006;
/* [7] */
Magenta, noIcon, noKey, noMark, plain,
12007;
/* [8] */
Yellow, noIcon, noKey, noMark, plain, 12008
}
};
resource cmnu (128) {
128,
textMenuProc,
allEnabled,
enabled,
Buzzwords,
{/* array: 1 elements */
/* [1] */
Page Setup Change, noIcon, noKey, noMark,
plain, cChangePrinterStyle
}
};
resource MBAR (128) {
{1; 2; 3; 4; 5}
};
resource MBAR (130) {
{10; 11; 12}
};
resource mctb (0) {
{0, 0,
{ /* array: 4 elements */
0, 0, 0,
65535, 65535, 65535,
65535, 0, 65535,
65535, 65535, 65535
}
}
};
resource mctb (1) {
{1, 0,
/* [1] */
{ /* array: 4 elements */
0, 65535, 65535,
65535, 65535, 65535,
65535, 0, 0,
65535, 65535, 65535
},
/* [2] */
1, 1,
{ /* array: 4 elements */
0, 0, 65535,
0, 0, 65535,
0, 0, 65535,
65535, 65535, 65535
},
/* [3] */
1, 2,
{ /* array: 4 elements */
0, 0, 65535,
0, 0, 65535,
0, 0, 65535,
65535, 65535, 65535
}
}
};
resource mctb (10) {
{10, 1,
{ /* array: 4 elements */
0, 0, 0,
0, 0, 0,
0, 0, 0,
65535, 65535, 65535
},
/* [2] */
10, 2,
{ /* array: 4 elements */
0, 0, 0,
0, 0, 0,
0, 0, 0,
65535, 65535, 65535
},
/* [3] */
10, 3,
{ /* array: 4 elements */
65535, 0, 0,
65535, 0, 0,
65535, 0, 0,
65535, 65535, 65535
},
/* [4] */
10, 4,
{ /* array: 4 elements */
0, 65535, 0,
0, 65535, 0,
0, 65535, 0,
65535, 65535, 65535
},
/* [5] */
10, 5,
{ /* array: 4 elements */
0, 0, 65535,
0, 0, 65535,
0, 0, 65535,
65535, 65535, 65535
},
/* [6] */
10, 6,
{ /* array: 4 elements */
0, 65535, 65535,
0, 65535, 65535,
0, 65535, 65535,
65535, 65535, 65535
},
/* [7] */
10, 7,
{ /* array: 4 elements */
65535, 0, 65535,
65535, 0, 65535,
65535, 0, 65535,
65535, 65535, 65535
},
/* [8] */
10, 8,
{ /* array: 4 elements */
65535, 65535, 0,
65535, 65535, 0,
65535, 65535, 0,
65535, 65535, 65535
}
}
};
resource mctb (11) {
{ /* array MCTBArray: 8 elements */
/* [1] */
11, 1,
{ /* array: 4 elements */
0, 0, 0,
0, 0, 0,
0, 0, 0,
65535, 65535, 65535
},
/* [2] */
11, 2,
{ /* array: 4 elements */
0, 0, 0,
0, 0, 0,
0, 0, 0,
65535, 65535, 65535
},
/* [3] */
11, 3,
{ /* array: 4 elements */
65535, 0, 0,
65535, 0, 0,
65535, 0, 0,
65535, 65535, 65535
},
/* [4] */
11, 4,
{ /* array: 4 elements */
0, 65535, 0,
0, 65535, 0,
0, 65535, 0,
65535, 65535, 65535
},
/* [5] */
11, 5,
{ /* array: 4 elements */
0, 0, 65535,
0, 0, 65535,
0, 0, 65535,
65535, 65535, 65535
},
/* [6] */
11, 6,
{ /* array: 4 elements */
0, 65535, 65535,
0, 65535, 65535,
0, 65535, 65535,
65535, 65535, 65535
},
/* [7] */
11, 7,
{ /* array: 4 elements */
65535, 0, 65535,
65535, 0, 65535,
65535, 0, 65535,
65535, 65535, 65535
},
/* [8] */
11, 8,
{ /* array: 4 elements */
65535, 65535, 0,
65535, 65535, 0,
65535, 65535, 0,
65535, 65535, 65535
}
}
};
resource mctb (12) {
{ /* array MCTBArray: 8 elements */
/* [1] */
12, 1,
{ /* array: 4 elements */
0, 0, 0,
0, 0, 0,
0, 0, 0,
65535, 65535, 65535
},
/* [2] */
12, 2,
{ /* array: 4 elements */
0, 0, 0,
0, 0, 0,
0, 0, 0,
65535, 65535, 65535
},
/* [3] */
12, 3,
{ /* array: 4 elements */
65535, 0, 0,
65535, 0, 0,
65535, 0, 0,
65535, 65535, 65535
},
/* [4] */
12, 4,
{ /* array: 4 elements */
0, 65535, 0,
0, 65535, 0,
0, 65535, 0,
65535, 65535, 65535
},
/* [5] */
12, 5,
{ /* array: 4 elements */
0, 0, 65535,
0, 0, 65535,
0, 0, 65535,
65535, 65535, 65535
},
/* [6] */
12, 6,
{ /* array: 4 elements */
0, 65535, 65535,
0, 65535, 65535,
0, 65535, 65535,
65535, 65535, 65535
},
/* [7] */
12, 7,
{ /* array: 4 elements */
65535, 0, 65535,
65535, 0, 65535,
65535, 0, 65535,
65535, 65535, 65535
},
/* [8] */
12, 8,
{ /* array: 4 elements */
65535, 65535, 0,
65535, 65535, 0,
65535, 65535, 0,
65535, 65535, 65535
}
}
};
resource PICT (128) {
891,
{195, 254, 281, 325},
$1101 A000 82A0 008E 0100 0A00 0000 0002"
$D002 4098 000A 00C3 00F8 00FF 0148 00C3"
$00FE 00FF 0145 00C3 00FE 00FF 0145 0000"
$02F7 0002 F700 02F7 0002 F700 02F7 0002"
$F700 02F7 0002 F700 02F7 0002 F700 02F7"
$0006 FD00 000E FC00 07FD 0001 1F80 FD00"
$07FD 0001 7FC0 FD00 07FD 0001 FFF0 FD00"
$08FE 0002 03FF FCFD 0008 FE00 0207 FFFE
$FD00 09FE 0003 1FFF FF80 FE00 09FE 0003"
$3FFF FFE0 FE00 09FE 0003 7FFF FFF8 FE00"
$0A02 0000 01FE FF00 FCFE 0008 0200 0003"
$FDFF FE00 0A02 0000 0FFD FF00 C0FF 000B
$0700 001F FFFF 3FFF E0FF 000B 0700 007F
$FFFE 1FFF F8FF 000B 0700 00FF FFFE 1FFF
$FCFF 000B 0100 01FE FF02 27FF FCFF 000B
$0100 01FE FF02 F9FF F8FF 000B 0100 00FE
$FF02 FE7F F0FF 000B 0200 003F FEFF 019F
$E0FF 000B 0200 001F FEFF 01E7 C0FF 000B
$0200 003F FEFF 01F9 80FF 000B 0200 0033"
$FEFF 01FE 80FF 000A 0200 0060 FDFF 00C0"
$FF00 0B07 0000 607F FFFF FCC0 FF00 0B07"
$0000 601F FFFF F870 FF00 0B07 0000 6007"
$FFFF F0F8 FF00 0B07 0000 6001 FFFF F0F8"
$FF00 0B07 0000 6000 FFFF F0F8 FF00 0B07"
$0000 6038 3FFF B050 FF00 0A06 0000 607C
$0FFF 30FE 000B 0700 0060 F603 FE30 A8FF
$000B 0700 0060 E301 FC30 50FF 000B 0700"
$0060 C000 7830 20FF 000B 0700 0060 0000"
$1030 88FF 000B 0200 0060 FE00 0130 50FF
$000A 0200 0060 FE00 0030 FE00 0B02 0000"
$60FE 0001 30A8 FF00 0B07 0000 6807 0700"
$B050 FF00 0A06 0000 681F 8FC0 B0FE 000B
$0700 006C 7FDF F1B0 A8FF 000A 0200 0067"
$FEFF 0030 FE00 0B09 0000 63FF FFFE 31F4"
$1000 0B09 0000 307F DFF0 6046 3000 0B09"
$0000 381F 8FC0 E045 5000 0B09 0000 1C00"
$0001 C044 9000 0B09 0000 0E00 0003 8044"
$1000 0802 0000 07FE FFFD 0009 0500 0001"
$FFFF FCFD 0008 FE00 0280 0004 FD00 9800"
$0A00 FF00 F801 1901 4800 FF00 FE01 1901"
$4500 FF00 FE01 1901 4500 0008 FE00 0280"
$0004 FD00 08FE 0002 FFFF FCFD 0008 0200"
$0001 FEAA FD00 0802 0000 03FE 55FD 000A
$0600 0006 FEAF EA80 FE00 0A06 0000 0D83"
$5835 40FE 000A 0600 001B 01B0 1AA0 FE00"
$0A06 0000 3501 5015 50FE 000A 0600 006A
$82A8 2AA8 FE00 0A06 0000 D57D 57D7 F4FE
$000A 0600 01AF AAFA AC1A FE00 0A06 0003"
$5055 0558 0DFE 000B 0700 06A0 2A02 A80A
$80FF 000B 0700 0D60 3603 5415 40FF 000B
$0700 0AB0 6B06 ABEA C0FF 000B 0700 0D5F
$D5FD 5555 40FF 0009 0100 0AFC AA00 C0FF
$0009 0100 0DFC 5500 40FF 0009 0100 0FFC
$FF00 C0FF 0002 F700 02F7 0002 F700 02F7"
$0002 F700 02F7 0002 F700 A000 8FA0 0083"
$FF
};
resource ICN# (128) {
{ /* array: 2 elements */
/* [1] */
$0001 0000 0002 8000 0004 4000 0008 2000"
$0010 1000 0020 0800 0050 0400 0088 0200"
$0100 0100 0284 0080 0440 0240 0822 0420"
$1410 0810 220A 1008 4084 3F04 802A 4082"
$4001 8041 2003 3022 1005 C814 080E 7F8F
$0412 3005 0221 0007 0140 8005 0080 6007"
$0040 1FE5 0020 021F 0010 0407 0008 0800"
$0004 1000 0002 2000 0001 4000 0000 80",
/* [2] */
$0001 0000 0003 8000 0007 C000 000F E000"
$001F F000 003F F800 007F FC00 00FF FE00"
$01FF FF00 03FF FF80 07FF FFC0 0FFF FFE0"
$1FFF FFF0 3FFF FFF8 7FFF FFFC FFFF FFFE
$7FFF FFFF 3FFF FFFE 1FFF FFFC 0FFF FFFF
$07FF FFFF 03FF FFFF 01FF FFFF 00FF FFFF
$007F FFFF 003F FE1F 001F FC07 000F F800"
$0007 F000 0003 E000 0001 C000 0000 80"
}
};
resource ICN# (129) {
{ /* array: 2 elements */
/* [1] */
$0FFF FE00 0800 0300 0800 0280 0800 0240"
$0800 0220 0800 0210 0800 03F8 0801 0008"
$0880 0008 0801 0208 0840 0008 0801 0408"
$0820 0008 0801 0808 0810 0008 0801 1008"
$0AAB AAA8 0809 2008 0804 4008 0803 8008"
$0800 0008 0801 0008 0800 0008 0801 0008"
$0800 0008 0801 0008 0800 0008 0801 0008"
$0800 0008 0800 0008 0800 0008 0FFF FFF8",
/* [2] */
$0FFF FE00 0FFF FF00 0FFF FF80 0FFF FFC0"
$0FFF FFE0 0FFF FFF0 0FFF FFF8 0FFE FFF8"
$0F7F FFF8 0FFE FDF8 0FBF FFF8 0FFF FBF8"
$0FDD 7FF8 0FFA B7F8 0FE7 DFF8 0FEF FFF8"
$0D74 5D58 0FB7 DBF8 0F7B BDF8 0EFD 7EF8"
$0FFF FFF8 0FFF FFF8 0FFF FFF8 0FFF FFF8"
$0FFF FFF8 0FFF FFF8 0FFF FFF8 0FFF FFF8"
$0FFF FFF8 0FFF FFF8 0FFF FFF8 0FFF FFF8"
}
};
{--------------------------------------------}
{---------- File UQPlot.MAmake --------------}
{--------------------------------------------}
# Copyright © 1989 Charles F. McMath.
# All rights reserved.
#
#This is the makefile. As you can see,
# theres not much to it. Because the application
# files are the standard ones, MABuild will
# figure out (correctly) what needs to be done.
AppName = QPlot
Creator = FGT3