TweetFollow Us on Twitter

Plotter
Volume Number:4
Issue Number:2
Column Tag:Pascal Procedures

MultiFinder Friendly MacDraw Plotter

By Dave Kelly, MacTutor Editorial Board & David Smith, Editor & Publisher

For the past few months, MacTutor has been running a series of editorials on the importance of developers getting with the new way of doing things. In the last six months, Apple has introduced a number of product enhancements that directly affect commercial software development. These include:

• MultiFinder Quasi-Multitasking Finder

• AppleShare File Server

• New Menu Manager with nested menus

• New Color PICT format

• New Color Quickdraw Capability

• New Style-enhanced Text Edit & Clipboard type

• Multiple screen capability

• Universal Hyper-Text type data base

• Faster number crunching with 68881 chip

• New ROM interface files, MPW 2.0

• Lots of new Technical notes fixing old bugs

• New compatibility headaches

As usually happens when new goodies come on the market, everyone looks at their old software and says “how come my version of MS____ doesn’t support this?” As programmers and developers, we have a responsibility to bring our software up to speed as quickly as possible with the new way of doing things. For example, the time-consuming dialog box to change text font and style is no longer good enough when nested menus can do the same job so much faster. Black and white windows, menus and documents get old after using color for a few weeks. Big screen monitors make old style paint programs frustrating when you can’t grow the window, or have to turn off color to keep the program from crashing. And program development environments that once were speedy now send the Mac into the bit-bucket because the developer can’t figure out how to handle the switching context with a color video card installed. Even lowly debuggers have problems trying to cope with all the system enhancements. So while Apple has indeed given us lots of new toys, they have also given us lots of new headaches trying to understand and upgrade our products to work with everything. If you thought it was hard to program the Mac two years ago, pity the programmer just getting started today. And the fact that Apple has just now managed to cleaned up the documentation in Inside Macintosh Volume 5, doesn’t help much.

Fig. 1 Plotter Program generates hairline plots

Put Up or Shut Up

After hi-lighting commercial products which have made the transition to the new rules of the game, we have decided to put our money where our editorial mouth is, and attempt to publish a program that illustrates how to incorporate many of the new system features Apple has introduced in the last six months.

Problem Solving

Since one of the hottest markets for Macintosh now is in engineering environments, we will discuss a typical engineering approach to computer use. In aerospace and the defense industry, the name of the game is problem solving. You use the computer to solve a problem. It may be physics, engineering or math, but the computer is the tool for investigating the problem and finding a solution. Let’s suppose our problem is to build a space shuttle that flies rather than blows up. And let’s suppose a key part of the problem is the equation for modeling the burn rate of the solid rocket fuel in the boosters. To make things simple, we will assume the modeling equation is quadratic in form. We want to use the computer to model this quadratic equation and all others like it so we can determine the best model for the rocket’s design. The problem most engineers have when trying to use the Macintosh for problem solving, is they spend so much time figuring out how the Mac works, they never get around to the problem! So here is a shell program for modeling and simulation programs that follows the new Macintosh way of doing things. Our “Plotter” program analyzes the quadratic equation and plots it with the following Mac features:

• Dialog box for user-friendly input of parameters

• Equation plotted as a Quickdraw picture

• Plot can be saved to disk as a PICT file

• Program supports printing:

• hairline line width for graph

• Choice of window size or paper size

• Plot Window is growable, zoomable, moveable

• Plot can be opened and edited in MacDraw

• Plot can be placed in Pagemaker

• Uses Quickdraw and Postscript comments

• Supports WaitNextEvent for MultiFinder use

• Quickdraw colors for menus, graph, axis

• Nested menus allow color changes

Mac Modeling Requires Quickdraw

One of the big problems in trying to model equations on the Macintosh is how to create a Quickdraw Picture. Naturally you could just draw it on the screen as a paint-type bit-map, but then you couldn’t take advantage of the higher resolution of the LaserWriter to show the details of the plot. Once a plot is created, you want to be able to save and edit it to add notes, or even move the graph around the axis. To do this, the parts of the plot have to be created as MacDraw type objects. Yet they also have to be grouped or else you end up with 200 date points being re-positioned on the page by a careless mouse. When the plot is finished you have to be able to print or import it into a program like Pagemaker to show it to someone. So how to you create a program that does all this?

Check Your Library!

First, you make sure your supply to Technical Notes is up to date. Many of these features are documented and supported by the just released Volume Five of Inside Macintosh, and by a rash of new Technical Notes released since the last Boston Expo. Here are some of the notes that deal with the problems of pictures from the point of view of modeling equations.

• 21 Internal Picture format 11/86

• 27 MacDraw Picture Format 9/86

• 59 Pictures and Clip Regions 1/86

• 72 Optimizing for the Laser 3/86

• 91 Picture Comments 3/87

• 152 Using Laser Prep routines 7/87

• 154 Displaying large PICT files 7/87

• 155 Handles & Pointers 9/87

• 181 Picture Comments 11/87

• 183 Position Independent PS 11/87

In addition, there is the LaserWriter Technical manual, a new PICT File Format Notes, and Mac Example Applications, ver. 1.0. These three publications, available from APDA, include a wealth of information on how to create and print Quickdraw pictures and pic comments, both MacDraw and Postscript comments.

The problem of making an application MultiFinder friendly is partly addressed in the following technical notes as well as the recently released MultiFinder Development Package, ver. 1.0, from APDA:

• 158 Asked MultiFinder Questions 9/87

• 177 Sleep Parameter problem 11/87

• 180 MultiFinder Miscellanea 11/87

One of the best “overview” publications of how to implement all of the new goodies, especially color, is Dave Wilson’s new note collection, Programming the Macintosh II. This is available from MacTutor’s mail order store, in this issue. Dave has collected the slides from his Mac II seminar at last year’s Boston Expo and published his color paint program source code to provide a nice one-stop shopping center for all of the features in our program this month. Finally, you should plan on getting the Professional Programmer’s Extender from Invention Software. This contains all of the source code for volumes 1 and 2 of their extender products. This is also available from the MacTutor mail order store, in this issue. It is much faster to cut and paste source code from someone else, then re-invent the wheel yourself.

For those who like books (and have the time to read them), your in luck, if you use Borland’s Turbo Pascal for the Mac. A rash of new books have been released including Macintosh Turbo Pascal by Tom Swan [Wiley & Sons] and Turbo Pascal for the Mac by Leon Wortman [Tab Books]. Borland has also released a bunch of goodies including Turbo Pascal Tutor, and Numerical Methods Toolbox. I make it a habit to just visit my local computer bookstore regularly and buy anything with Mac on it. Call it a business expense!

How to Use the New Goodies

Our program this month features the full Macintosh User Interface, as documented in the recently released Human Interface Guidelines: The Apple Desktop Interface, published Addison-Wesley, for Apple. It would be a great challenge to take this book as a program definition and attempt to write a demo program that included full support for every feature in this book, showing how the full User Interface is implemented. The main program, and a unit called MyPlotStuff, implement the standard event loop for a single window application. Much of this was cut from the Multi-Window Text Editor MacTutor published last year. As I said, its always faster to cut and paste than re-invent. To support color, a color array is set up in the InitMac routine to define the eight Quickdraw colors. This way the program can run on all Macs without a lot of special programming. (Apple really needs to put color quickdraw into the SE so that all machines have the same version of Quickdraw, even if they can’t show color.)

MultiFinder Friendly

To be considered MultiFinder friendly, an application has to call WaitNextEvent and support suspend and resume events correctly. Our program works under MultiFinder and is especially useful with MacDraw, saving and opening our graphs between the two programs. But it wasn’t easy figuring out how to do it. The first problem we had was that neither Think’s Lightspeed Pascal nor Borland’s Turbo Pascal support the final MPW 2.0 interfaces. The latest versions, (LS Pascal 1.11 and Turbo Pascal 1.1) both use MPW 2.0 beta interfaces and so do not include either the SysEnvirons glue, nor the WaitNextEvent interface. To get around this, I added the WaitNextEvent definition to the ROM85 library for LS Pascal. Since SysEnvirons is an OS trap, I suspect you can’t call it without assembly glue to manage the register set-up so it is commented out in the listing. I just assumed the Mac was a Plus, SE or II with the latest system.

Where is WaitNextEvent

The MultiFinder documentation says you should call WaitNextEvent if it is present. Otherwise, call GetNextEvent and SystemTask. So you have to check the unimplemented trap call to find out and set a flag so you will know which event trap to use. I did this and after a while I noticed the program always said the WaitNextEvent trap was unimplemented. What? I have a Mac II with the latest system. How can it be unimplemented? Where is it? I called several people, both at Borland and Think, but no one knew the answer. Finally, after studying two sample programs from the MultiFinder Developer’s package previously mentioned, I noticed their programs didn’t seem to have any problem. It finally dawned on me. WaitNextEvent is inside MultiFinder! Thus it only exists when your application is running under MultiFinder, when I suppose it gets patched into the system. Under the Finder, it’s not there so you have to check for it. You would think this little gem would have been included in the documentation somewhere. Alas, I don’t have either the final Volume 5 of IM, nor the final MultiFinder Developer’s package!

Our plot is stored as a picture in a data structure called PlotDocHandle. We set up the document in our InitMyWindow routine in the normal manner. Note that all the rectangles we will need are set here, relative to the window’s portRect. We also do a ClipRect to set the clipping region of the window’s grafport so we don’t have any Quickdraw problems a la tech note 59.

Fig. 2 Our menus include a nested color menu

Color Menus

Our InitMyMenus routine includes an apple, file and edit menu as well as a nested menu for color and a menu for some printing options. The data structures are designed to make it easy to check the nested menu items and this code depends heavily on both Dave Wilson’s programming examples, and other code segments I’ve seen.

The Mighty Update Event

Our doMouse, Keydowns, and activate routines are pretty standard user interface stuff so no need to go over those here. The Update routine, is critical. The ideal for all Macintosh programs is to never draw anything! The application creates a Quickdraw picture or pictures and then invalidates some portion of the screen, causing an update event. Then, in the Update Event routine, you call DrawPicture to display the results of all the other application code that has been maintaining and modifying those pictures. It has always been a mystery to me how you create the perfect Update Event routine. Here is how ours works:

• Save and set the current grafport

• Begin Update to restrict the visRgn to the collection of invalid rgns that need updating.

• Erase the window’s portRect.

• Set the clip rgn to the window’s portRect.

• Draw the picture.

• Draw the grow icon.

• End the update, restoring the VisRgn.

Exactly why all of these steps are needed, in that order is a mystery to me, but it works. The window’s portRect encloses the content region of the window, which includes everything but the title bar. In particular, it includes the scroller and grow icon regions. So when you erase that, you have to re-draw the grow icon. The picture is drawn in what I call the PicRect, or the application area of the content region, not including the scroller areas. When a grow window event takes place, these rectangles have to be carefully updated so that the next update event will work properly.

MultiFinder Support

Our doMulti routine implements the suspend and resume events from MultiFinder. Basically, it is a carbon copy of our activate / deactivate event routine. Since it is not really an event, we have to do a lot of processing to check for suspend or resume, and for a mouse moved event, if we are supporting that feature of MultiFinder. Our program is set up for it, but we don’t support it, since, for multiple monitors, you have to do a lot of region arithmetic I don’t fully understand. The beta documentation also says we have to call SystemEvent and post an activate event if a DA is the front window when we are suspended or resumed. I have included this code in our doMulti routine, but it may not be necessary. I’m looking forward to checking the final documentation to see about this.

Grow Window

Our MyPlotStuff unit implements the remainder of the usual user interface. We have an about box dialog that reads the ID string like the new Finder does and displays it. We also find out how much memory we have left from whatever MultiFinder has allocated to us by calling MaxMem. Our doMenuBar routine does the usual menu bar processing, including checking the nested color menu items when the user selects a new color. The menus also are displayed in the color of the selection thanks to the color menu resources Steve Sheets taught us about in past issues of MacTutor.

Our grow routine is the most important one in this unit. Grow works with the update event to change the window properly when it is resized. It is another Macintosh mystery I’ve never fully understood. As I understand it, you have a pre-grow period and a post-grow period, determined by when you call SizeWindow. I use the routine for both a grow window event and a zoom window event. In the case of a zoom, you get the new window size from the portRect, which is already set for you by the zoom. In the case of a grow, you call GrowWindow to return the new window size. I re-calculate the important window rectangles both before a SizeWindow and after. SizeWindow changes the portRect of the window to the new size. So, first you invalidate the scroller, grow and pic rectangles with the old portRect, then you SizeWindow to change the portRect, and invalidate the scroller, grow and pic rectangles for the new window size. This seems to correctly get all the window areas properly drawn during the update event. I suspect we may be invalidating to many areas and that this can be re-fined so that you only invalidate certain rectangles depending on whether the window is getting smaller or larger. Also, if you have controls, you can move and re-size controls separately, so they can be removed from the invalidation by calling the validate trap. If anyone has a more efficient grow / update routine for the general case, I’d like to see it.

Fig. 3 MultiFinder support: Plotter works with MacDraw

Saving Our Graph

Once we have played around with various graphs, it would be nice to save them. Our myFileStuff unit does the SaveAs and Save functions. To save a picture, you call the standard file dialog and if the reply is good, you create the file, open the file, set the file position to the start of the file and write something into the file. Then you close the file and flush the toilet, er, volume. This is the order of calls in our doSaveAs routine, the majority of which is error checking if anything goes wrong, goes wrong, goes wrong

To write our graph out to disk, we simply have to write out our quickdraw picture. But first we write a 512 byte blank header. This will cause MacDraw to open the PICT file and use it’s default settings. Our plotter program has no open function so we save our document as a MacDraw document so it can be viewed later and edited. The picture handle is stored in DrawingPic so we do a GetHandleSize and write out that number of bytes to the file, passing a pointer to the dereferenced picture handle. After saving the picture, we set the window title to the file name and store the file name, and volume reference number in our document data structure and enable the Save menu item for future use. For the Save function, we do the same thing as the SaveAs (they could be combined at the expense of clarity) except that we get the file name and volume reference number from our document datastructure rather than from a standard file dialog.

Fig. 4 Dialog Box gets our parameters

But Where Do We Actually Model It?

I told you it was tempting to get lost in the Macintosh interface and forget all about the modeling task our Boss assigned us to! We are supposed to be modeling a quadratic equation and coming up with some numbers for that next moon shot, not sitting around reading Inside Macintosh for the 100th time. Our Solve Unit implements the routines to analyze our equation and plot it by actually creating the quickdraw picture we’ve been spending all this time talking about. We also put up a dialog box to get all of the equation parameters and plot characteristics we need. We can set the step size, and the scale of both the x axis and the y axis. And of course, we can input the three parameters that define the general quadratic equation. In our doDialog routine, we get these parameters from the user, and remember them so they appear the next time the plot function is selected. SetIText is used to set the dialog items with the values of the global variables that hold the equation parameters. A flag is used to by-pass SetIText if this is the first time we have been called, in which case the default values from the dialog item resources will be used. This allows the user to modify the default values in the resource file with ResEdit if he wishes.

The hard part about the dialog box is converting the user’s response into real numbers. LS Pascal has a routine called ReadString that takes the string variables from the GetDItem trap and converts them into integer and real numbers for our equation parameters. However, this routine crashes mightily if the user types a non-numeric entry in the dialog! So the first improvement would be to protect yourself by checking the dialog input somehow.

The Quadratic Solution

The example problem is to find the roots of a quadratic equation. The problem in itself is not difficult (this time), but the solution technique will be similar for other problems.

First we should consider what the input specifications will be. Among the questions we should ask ourselves about the problem are:

1) What values will be provided as input to the program?

2) What range will these values be in?

3) What format will the values be in?

4) In what ways can these input values be used? Can the inputs be modified?

5) How will the input values be provided to the program?

6) What values will be returned by the program?

7) What format will the return values be in?

8) What kind of output will be produced?

Any quadratic equation may be reduced to the form, --

ax2 + bx + c = 0

then

If a, b, and c are real then:

If b2-4ac is positive, the roots are real and unequal;

If b2-4ac is zero, the roots are real and equal;

If b2-4ac is negative, the roots are imaginary and unequal.

The inputs required in this problem are a, b, and c. These values will be in the set of real numbers. Our dialog box allows us to input these values. On our plot, we print the results in a box indicating if the analysis yields real, or complex results. We can also solve the first derivative problem and determine where the graph has a local minimum or a maximum. Our program doesn’t do it, but we could also indicate the second derivative to show if the curve is concave upward or downward. Keep in mind that in a “real life” problem the algorithm is not always given as in this example. The user normally doesn’t care how the program arrives at the result as long as the result is correct. If too many details are specified early in the defining the problem, the programmer may get “locked in” to a particular solution too early. The programmer then lacks the flexibility needed to choose the best technique to solve the problem.

Our SolveIt and Quad routines implement the analysis portion of our modeling program by solving the quadratic equation and checking for the type of result. When the user selects Plot from the file menu, our doPlot routine will call SolveIt, which in turn calls Quad to get the “answers”. With answers in hand, we call PlotMe to create a quickdraw picture and display it in our window. After creating the picture with OpenPicture and ClosePicture, we get the picture displayed by simply invalidating the window’s portRect. This forces an update event, when will draw the graph by calling DrawPicture in our update event routine. Thus the whole plotter program really boils down to the magical routine which creates this quickdraw picture. This routine is called PrQDPicStuff in our Solve unit and is the most important of the whole program.

Creating the Quickdraw Picture

When we call OpenPicture, the characteristics of the current grafport will be used to determine how the picture will be stored. In our PrQDPicStuff routine, we want to allow for drawing either the picture on the screen or on the printer. We do this by passing to the routine, the display rectangle for the picture and a parameter indicating if we have been called from the Plot function or from the Print function. This was done to allow the added printer option of printing the graph using the full printer page, rather than the window’s portRect. While printing to full page size is not WYSIWYG, it is useful to get the biggest plot possible when you are trying to analyze a function’s behavior. In addition, if we are printing on the laserwriter, we set the font to times instead of geneva.

Constructing the picture definition routine is important. In our program we have grouped all the tasks together. First we calculate all the program variables, from the picture rectangle’s width and height. We get the font information from GetFontInfo so our paragraph can be constructed properly. The initial x and y values are calculated. These real values are converted into integer coordinates for plotting in such a way that the plot axis will extend the full length and height of the picture rectangle.

Next all the text is constructed with LS Pascal’s StringOf command and stored in Macintosh strings. For our paragraph, we use an array of strings which will be used to display the analysis results: the roots of the equation, the local min/max point, and the equation’s defining parameters. Once all the initial calculations and strings are set up, we are ready to ‘draw’ into our picture.

We set the background, axis and graph colors as set by the color menu items checked. We draw a box around the graph boundary (the display rectangle). We draw the axis, the plot itself, and a box for our paragraph. Finally we draw the paragraph and dispose of our temporary handles and clip regions.

Pic Comments

In order to get MacDraw to display our graph, the objects we draw must be grouped together. In particular, the graph itself must be grouped so all those little line segments stick together. A Pic Comment is used to do this. Pic Comments allow commands to be imbedded within the picture definition. There are two published lists of Pic Comments in the technical notes. One is the Pic Comments that MacDraw recognizes. The other are the Pic Comments that Postscript recognizes. Using these two sets of Pic Comments will enhance an application’s ability to create pictures that can be imported into other applications or that can take advantage of new capabilities. Apple is supposed to be keeping a list of registered Pic Comments, but little has come from it. The power of QuickDraw’s Pic Comments has really not been exploited or pushed by Apple in comparison to postscript.

PostScript Pic Comments

Since Quickdraw cannot do hairlines, we need to use some postscript Pic Comments the LaserWriter understands to set the line width thinner than that possible by Quickdraw. To get a hairline, define:

Type
 widhdl = ^widptr;
 widptr=^widpt;
 widpt=Point;
Var
 Width:Widhdl;

Then set up Width as follows:

Width^^.h:=10;
Width^^.v:=1;

Then use a Pic Comment:

PicComment(SetLineWidth,2,Handle(Width));

We can also draw our text so that it will be grouped as a paragraph by using the TextBegin and TextEnd comments. With the PicGrpEnd and PicGrpBegin comments we can group objects so that they stay together when moved around in MacDraw.

Printing Our Graph

Once our picture is defined, we can print it when the user selects print from the file menu. To print, we call our PrQDStuff routine above with the printing rectangle obtained from the Print Setup dialog and the display device set to LaserWriter. PrQDStuff will re-create the picture to the page rectangle, if the print option is selected, and a simple DrawPicture command during an open printing grafport will send the picture to the printer. The print traps are now in the system file, so we no longer link to the printing glue. In our doPrint routine, we get our print handle, saved in our document definition, save the current grafport and call PrOpen. Then we validate our print record and call PrJobDialog to put up the printing dialog box. If the user selects print, then we open the printing grafport by calling PrOpenDoc followed by PrOpenPage. From the printing info record (prInfo.rPage) we get the PageRect to pass to our picture creation routine. The PrintMe routine opens the picture and re-creates it using the PageRect, just as our PlotMe routine set up the picture for drawing to the screen. After drawing the picture into the printing grafPort, we close the page, close the document and close the printing manager by calling PrClosePage, PrCloseDoc and PrClose respectfully. When the printing grafPort is opened, the low level Quickdraw primitives are replaced with pointers into the printing manager, where the printer equivalent of the Quickdraw primitives translate the quickdraw picture definition into LaserPrep commands, which in turn are defined by the LaserPrep file into Postscript, and then interpreted by the Postscript interpreter in the LaserWriter into an image on the page. How does Postscript image the page? I don’t know. If I did know, I’d build a Postscript photo-typesetter, buy an Adobe Atlas board and have myself a 2700 dot per inch typesetting machine without paying Allied Linotype $50,000.

New Goodies Not Addressed

The key to this program is the fact that it produces a document that can be read, edited and placed by two other popular programs. In this way, the Plotter Program illustrates an important rule in the MultiFinder world: applications should work together to produce a library of compatible tools. The best way this can happen is by formatting the output of similar tools to read and write compatible documents. The MacDraw PICT file, the MacPaint bit-map file and the MacWrite formatted text file are important document standards that many similar tools support, besides the nearly universal unformatted text file. Even though Apple has defined a standard style definition for Text Edit that allows for font changes, we have yet to see a widely used formatted text item type that is a standard across many applications.

Apple Share is another new goodie that affects developers. How do we make Apple Share compatible programs? Consider this: To be MultiFinder friendly, Apple says you should keep track of where your windows were moved last. That means you have to write something to disk to save this information, but where? If you write it in the resource fork, the most logical place, then your program can’t be Apple Share compatible since the resource fork is not sharable. Two users cannot write to the same resource fork without destroying the integrity of the file. So Apple has created a catch-22 situation; supporting one new goodie (MultiFinder) makes it impossible to support another (Apple Share). Apple’s recommended solution is to write a “configuration file” into the system folder. Great. Now our hard disks will have hundreds of little configuration files to keep track of! Since the resource manager is Apple Share brain damaged, it hardly seems realistic for Apple to expect developer’s to rush madly to embrace this goodie. We encourage Apple to continue to define and support compatible data formats so tools can be used together. [Complete listing starts on the next page.]

Fig. 6 Pic Comments group objects for editing in MacDraw. Note that MacDraw ignores hairline Pic Comment!


{ Quadratic Equation Example program }
{ By Dave Kelly & Dave Smith     }
{ ©MacTutor, 1988}

PROGRAM QuadraticPlotter;

 USES
 ROM85, PrintTraps, PlotGlobals, Misc, myPlotStuff;

 PROCEDURE crash;
 BEGIN
 ExitToShell;
 END;

 PROCEDURE InitMac;
 VAR
 err : OSErr;
 BEGIN
 MaxApplZone;
 MoreMasters;
 MoreMasters;
 MoreMasters;
 MoreMasters;
 MoreMasters;
 InitGraf(@thePort);
 InitFonts;
 InitWindows;
 InitMenus;
 TEInit;
 InitDialogs(@crash);
 InitCursor;
 FlushEvents(everyEvent, 0);
 Finished := false;
 dialogflg := false;

 color[1] := 33; {black}
 color[2] := 30; {white}
 color[3] := 205;{red}
 color[4] := 341;{green}
 color[5] := 409;{blue}
 color[6] := 273;{cyan}
 color[7] := 137;{magenta}
 color[8] := 69; {yellow}

 theWorld.machineType := 4; {Mac II}
 {err := SysEnvirons(1, theWorld);  not in LSP 1.11}
 typeOfMac := theWorld.machineType;

 IF (typeOfMac >= 0) AND (NGetTrapAddress(WNETrapNum, ToolTrap) = NGetTrapAddress(UnImplTrapNum, 
ToolTrap)) THEN
 BEGIN
 WNE := FALSE;
 doMessage(‘WaitNextEvent not implemented’, ‘’, ‘’, ‘’); {Only true under 
Multifinder}
 END
 ELSE
 WNE := TRUE;
 mouseRgn := NewRgn;
 END;

 PROCEDURE initRects;
 VAR
 MemoryPtr : ^Integer;
 BEGIN
 MemoryPtr := pointer(mBarHeightGlobal); 
 mBarHeight := MemoryPtr^;
 screen := ScreenBits.Bounds; {current screen device}
 SetRect(DragArea, Screen.left + 4, Screen.top + mBarHeight + 4, Screen.right 
- 4, Screen.bottom - 4);
 SetRect(GrowArea, Screen.left + MinWidth, Screen.top + MinHeight, Screen.right 
- 8, Screen.bottom - 8);
 SetRect(PlotWindowRect, Screen.left + 15, Screen.top + 45, Screen.right 
- 75, Screen.bottom - 50);
 SetRect(ZoomRect, Screen.left + 4, Screen.top + mBarHeight + 4, Screen.right 
- 4, Screen.bottom - 4);
 END;

 PROCEDURE InitMyWindow;
 VAR
 windtype : integer;
 Visible : boolean;
 GoAway : boolean;
 RefVal : LongInt;
 title : str255;
 myPrint : THPrint;
 BEGIN
 Visible := false;
 windtype := documentProc + ZoomBox;
 GoAway := true;
 RefVal := 0;
 title := ‘Quadratic Plotter’;
 PlotWindow := NewWindow(NIL, PlotWindowRect, title, Visible, windtype, 
pointer(-1), GoAway, RefVal);
 PlotWindowPeek := WindowPeek(PlotWindow);
 SetPort(PlotWindow);
 TextFont(Geneva);
 TextSize(10);
 TextFace([]); {plain}
 TextMode(1);  {Or}
 PenNormal;
 ForeColor(blackColor);
 BackColor(whiteColor);
 PlotDocHandle := DocHandle(NewHandle(sizeof(Document)));
 PlotWindowPeek^.refCon := LongInt(PlotDocHandle);
 PlotWindowPeek^.windowKind := userKind;
 PlotDocHandle^^.drawing := NIL;
 WITH PlotWindow^.portRect DO
 BEGIN
 SetRect(VCRect, right - (SBarWidth - 1), top - 1, right + 1, bottom 
- (SBarWidth - 2));
 SetRect(HCRect, left - 1, bottom - (SBarWidth - 1), right - (SBarWidth 
- 2), bottom + 1);
 SetRect(GrowRect, HCRect.right, HCRect.top, VCRect.right, HCRect.bottom);
 SetRect(PicRect, left, top, right - (SBarWidth - 1), bottom - (SBarWidth 
- 1));
 SetRect(PageRect, left, top, right - (SBarWidth - 1), bottom - (SBarWidth 
- 1));
 END;  {of with }
 ClipRect(PlotWindow^.portRect);
 myPrint := THPrint(NewHandle(SIZEOF(TPrint)));
 PlotDocHandle^^.Print := myPrint;
 END;

 PROCEDURE InitMyMenus;
 VAR
 i : integer;
 BEGIN
 myMenus[AppleM] := GetMenu(AppleMenu);
 AddResMenu(myMenus[AppleM], ‘DRVR’);
 myMenus[FileM] := GetMenu(FileMenu);
 myMenus[EditM] := GetMenu(EditMenu);
 myMenus[ColorM] := GetMenu(ColorMenu);
 myMenus[OptionM] := GetMenu(OptionMenu);
 myMenus[GraphM] := GetMenu(GraphMenu);
 myMenus[AxisM] := GetMenu(AxisMenu);
 myMenus[BackgroundM] := GetMenu(BackgroundMenu);

 FOR i := 1 TO MenuCount DO
 InsertMenu(myMenus[i], 0);
 FOR i := SubMenuStart TO TotalMenuCount DO
 InsertMenu(myMenus[i], -1);

 GraphColor := 3;
 CheckItem(myMenus[GraphM], GraphColor, true);
 AxisColor := 1;
 CheckItem(myMenus[AxisM], AxisColor, true);
 BackgroundColor := 2;
 CheckItem(myMenus[BackgroundM], BackgroundColor, true);

 CheckItem(myMenus[OptionM], 1, true);
 Option := 1;  {window rect printing}

 DrawMenuBar;
 END; {of proc}

 PROCEDURE doMouse (myEvent : EventRecord);
 VAR
 whereIsIt : integer;
 whichWindow : WindowPtr;
 localPt, globalPt : Point;
 oldPort : GrafPtr;
 BEGIN
 globalPt := myEvent.where;
 localPt := globalPt;{global coord of mouse}
 GlobalToLocal(localPt);  {local coord of mouse}
 whereIsIt := FindWindow(globalPt, whichWindow);
 CASE whereIsIt OF
 inDesk : {0}
 doMessage(‘Mouse Click on Desktop.’, ‘(Not handled in this program.)’, 
‘’, ‘’);
 inMenuBar :    {1}
 doMenuBar(MenuSelect(globalPt));
 inSysWindow :   {2}
 SystemClick(myEvent, whichWindow);
 inContent :     {3}
 doContent(myEvent, whichWindow);
 inDrag : {4}
 doDrag(whichWindow, globalPt);
 inGrow : {5}
 doGrow(whichWindow, globalPt, False);
 inGoAway : {6}
 IF TrackGoAway(whichWindow, globalPt) THEN
 HideWindow(whichWindow);
 inZoomIn, InZoomOut :    {7, 8}
 BEGIN
 IF TrackBox(whichWindow, globalPt, whereIsIt) THEN
 BEGIN
 GetPort(OldPort);
 SetPort(whichWindow); {safety device}
 EraseRect(whichWindow^.portRect);
 ZoomWindow(whichWindow, whereIsIt, True);
 doGrow(whichWindow, globalPt, True);
 SetPort(OldPort);
 END;
 END;
 OTHERWISE
 BEGIN
 END;
 END; {of whereIsIt}
 END;

 PROCEDURE doKeyDowns (myEvent : EventRecord);
 VAR
 ch : char;
 charCode : longInt;
 keyCode : longInt;
 BEGIN
 charCode := BitAnd(myEvent.Message, charCodeMask);  
keyCode := BitShift(BitAnd(myEvent.Message, keyCodeMask), -8); 
 ch := Chr(charCode);  {get keyboard char}
 IF BitAnd(myEvent.Modifiers, CmdKey) = CmdKey THEN
 doMenuBar(MenuKey(ch))  { do menu command key}
 ELSE
 BEGIN  { do keystroke }
 ParamText(‘No typing supported ’, ‘’, ‘’, ‘’);
 itemhit := CautionAlert(AlertDialog, NIL);
 END;  { of do key stroke }
 END;  { of proc}

 PROCEDURE doUpdates (myEvent : EventRecord);
 VAR
 UpdateWindow : WindowPtr;
 TempPort : GrafPtr;
 BEGIN
 UpdateWindow := WindowPtr(myEvent.message);
 IF UpdateWindow = PlotWindow THEN
 BEGIN
 GetPort(TempPort); {save port}
 SetPort(UpdateWindow);
 BeginUpDate(UpdateWindow);
 BackColor(whiteColor);
 EraseRect(UpdateWindow^.portRect);
 ClipRect(UpdateWindow^.portRect);
 DrawPicture(DrawingPic, PicRect);
 DrawGrowIcon(UpdateWindow);
 EndUpDate(UpdateWindow);
 SetPort(TempPort);{restore port}
 END;
 END; {of proc}

 PROCEDURE doActivates (myEvent : EventRecord);
 VAR
 TargetWindow : WindowPtr;
 TargetPeek : WindowPeek;
 BEGIN
 TargetWindow := WindowPtr(myEvent.message);
 TargetPeek := windowPeek(TargetWindow);
 SetPort(TargetWindow);
 IF Odd(myEvent.modifiers) THEN
 BEGIN {activate}
 IF TargetWindow = PlotWindow THEN
 BEGIN
 DisableItem(myMenus[EditM], eUndo);
 DisableItem(myMenus[EditM], eCut);
 DisableItem(myMenus[EditM], eCopy);
 DisableItem(myMenus[EditM], ePaste);
 DisableItem(myMenus[EditM], eClear);
 DrawGrowIcon(TargetWindow);
 END
 END  { of activate loop}
 ELSE
 BEGIN {deactivate}
 IF TargetWindow = PlotWindow THEN
 BEGIN
 EnableItem(myMenus[EditM], eUndo);
 EnableItem(myMenus[EditM], eCut);
 EnableItem(myMenus[EditM], eCopy);
 EnableItem(myMenus[EditM], ePaste);
 EnableItem(myMenus[EditM], eClear);
 DrawGrowIcon(TargetWindow);
 END; { of my window activation}
 END; {of deactivate loop}
 END; {of proc}

 PROCEDURE doMulti (myEvent : EventRecord);

 CONST
 MouseMovedEvt = $FA;
 VAR
 HiByte : byte;
 bit0 : LongInt;
 sysresult : boolean;
 ResumeWindow : WindowPtr;
 ResumePeek : WindowPeek;
 SuspendWindow : WindowPtr;
 SuspendPeek : WindowPeek;
 MouseMove : LongAndByte;
 BEGIN
 bit0 := 31; {convert 68000 to toolbox}
 MouseMove.longView := myEvent.message;
 HiByte := Byte(MouseMove.byteView.byte0);
 IF HiByte = MouseMovedEvt THEN
 BEGIN {Handle mouse moved event}
 END;
 {check for resume event }
 IF Odd(myEvent.message) THEN
 BEGIN  {resume}
 {activate Event}
 ResumeWindow := FrontWindow;
 IF ResumeWindow = PlotWindow THEN
 BEGIN
 SetPort(ResumeWindow);
 InvalRect(ResumeWindow^.portRect); 
 DisableItem(myMenus[EditM], eUndo);
 DisableItem(myMenus[EditM], eCut);
 DisableItem(myMenus[EditM], eCopy);
 DisableItem(myMenus[EditM], ePaste);
 DisableItem(myMenus[EditM], eClear);
 DrawGrowIcon(ResumeWindow);
 END;
 IF FrontWindow <> NIL THEN
 BEGIN {DA check}
 ResumePeek := WindowPeek(FrontWindow);
 IF ResumePeek^.windowKind < 0 THEN {DA}
 BEGIN
 myEvent.what := activateEvt;
 BitSet(@myEvent.modifiers, bit0);
 sysresult := SystemEvent(myEvent);
 END; {da}
 END; {DA check}
 { end of activate Event}
 END {of resume}
 ELSE
 BEGIN  {suspend}
 {de-activate Event}
 SuspendWindow := FrontWindow;
 IF SuspendWindow = PlotWindow THEN
 BEGIN {plotwindow}
 SetPort(SuspendWindow);
 InvalRect(SuspendWindow^.portRect); 
 EnableItem(myMenus[EditM], eUndo);
 EnableItem(myMenus[EditM], eCut);
 EnableItem(myMenus[EditM], eCopy);
 EnableItem(myMenus[EditM], ePaste);
 EnableItem(myMenus[EditM], eClear);
 DrawGrowIcon(SuspendWindow);
 END; {plotwindow}
 IF FrontWindow <> NIL THEN
 BEGIN {DA check}
 SuspendPeek := WindowPeek(FrontWindow);
 IF SuspendPeek^.windowKind < 0 THEN
 BEGIN {da}
 myEvent.what := activateEvt;
 BitClr(@myEvent.modifiers, bit0);
 sysresult := SystemEvent(myEvent);
 END; {da}
 END; {DA check}
 { end of de-activate Event}
 END; {suspend}
 END; {of proc}

 PROCEDURE MainEventLoop;
 CONST
 MultiFinderEvt = 15;
 VAR
 sleep : LongInt;
 Event : EventRecord;
 DoIt : Boolean;
 BEGIN
 sleep := 10;
 REPEAT
 IF WNE THEN
 DoIt := WaitNextEvent(EveryEvent, Event, sleep, NIL) 
 ELSE
 BEGIN
 SystemTask;
 DoIt := GetNextEvent(EveryEvent, Event);
 END;
 IF DoIt THEN
 CASE Event.what OF
 mouseDown : 
 doMouse(Event);
 KeyDown, Autokey : 
 doKeyDowns(Event);
 updateEvt : 
 doUpdates(Event);
 activateEvt : 
 doActivates(Event);
 MultiFinderEvt : 
 doMulti(Event);
 OTHERWISE
 BEGIN
 END;
 END; {of event case}
 UNTIL Finished; {end program}
 END;

{ Main Program}
BEGIN
 InitMac;
 InitRects;
 InitMyWindow;
 InitMyMenus;
 MainEventLoop;
END.


UNIT MyPlotStuff;

INTERFACE

 USES
 ROM85, PrintTraps, PlotGlobals, Misc, solve, MyFileStuff, MyPrintStuff;

 PROCEDURE doAbout;
 PROCEDURE doQuit;
 PROCEDURE doMenubar (menuResult : LongInt);
 PROCEDURE doContent (ConEvent : EventRecord;
 contentWindow : windowPtr);
 PROCEDURE doDrag (GrabWindow : WindowPtr;
 GlobalMouse : point);
 PROCEDURE doGrow (ResizeWindow : WindowPtr;
 Globalmouse : point;
 Zoomflg : Boolean);
IMPLEMENTATION

 PROCEDURE doAbout;
 VAR
 IDStrHandle : StringHandle;
 dialogP : DialogPtr;
 item : integer;
 Str1, Str2, Str3 : str255;
 myHeapSpace : LongInt;
 FreeSpace : Size;
 BEGIN
 IDStrHandle := StringHandle(GetResource(rsrc, 0));
 IF IDStrHandle = NIL THEN
 BEGIN
 doMessage(‘Get About box crash!’, ‘’, ‘’, ‘’);
 ExitToShell;
 END;
 MoveHHi(Handle(IDStrHandle));
 HLock(Handle(IDStrHandle));
 FreeSpace := FreeMem;
 myHeapSpace := MaxMem(FreeSpace);
 NumToString(myHeapSpace, Str2);
 Str2 := concat(‘Memory = ‘, Str2);
 Str3 := ‘’;
 Str1 := ‘’;
 ParamText(IDStrHandle^^, Str1, Str2, Str3);
 dialogP := GetNewDialog(AboutDialog, NIL, pointer(-1));
 IF dialogP = NIL THEN
 BEGIN
 doMessage(‘Dialog crash!’, ‘We are dead...’, ‘’, ‘’);
 ExitToShell;
 END;
 initCursor;
 ModalDialog(NIL, item);
 DisposDialog(dialogP);
 HUnlock(Handle(IDStrHandle));
 END;

 PROCEDURE doQuit;
 BEGIN
 BackColor(whiteColor);
 ForeColor(blackColor);
 PenNormal;
 DisposeWindow(PlotWindow);
 DisposeRgn(mouseRgn);
 DisposHandle(Handle(PlotDocHandle));
 Finished := true;
 END; {of proc}

 PROCEDURE doMenubar; {(menuResult : LongInt)}
 VAR
 theMenu : integer;
 theItem : integer;
 daName : STR255;
 accItem : integer;
 temp : GrafPtr;
BEGIN
 theMenu := HiWord(menuResult); {menu}
 theItem := LoWord(menuResult); {item}
 CASE theMenu OF
 AppleMenu : 
 BEGIN
 IF theItem = aAbout THEN
 doAbout
 ELSE
 BEGIN  {must be DA}
 GetItem(myMenus[AppleM], theItem, daName);
 GetPort(temp);  {protect against flacky DA}
 accItem := OpenDeskAcc(daName);
 SetPort(temp);
 END; {else}
 END; {of AppleMenu}
 FileMenu : 
 BEGIN
 CASE theItem OF
 fPlot : 
 BEGIN
 doPlot;
 END;
 fSave : 
 BEGIN
 doSave;
 END;
 fSaveAs : 
 BEGIN
 doSaveAs;
 END;
 fPageSet : 
 BEGIN
 doPageSet;
 END;
 fPrint : 
 BEGIN
 doPrint;
 END;
 fQuit : 
 BEGIN
 doQuit;
 END;
 OTHERWISE
 BEGIN
 END;
 END; {of theitem}
 END; {of FileMenu}
 EditMenu : 
 BEGIN
 IF NOT SystemEdit(theitem - 1) THEN
 BEGIN
 CASE theItem OF
 eUndo : 
 BEGIN
 END;
 eCut : 
 BEGIN
 END;
 eCopy : 
 BEGIN
 END;
 ePaste : 
 BEGIN
 END;
 eClear : 
 BEGIN
 END;
 OTHERWISE
 BEGIN
 END;
 END; {of case}
 END; {of system edit}
 END; {of EditMenu}
 ColorMenu : 
 BEGIN {just a dummy for submenus}
 END; {of color menu}
 GraphMenu : 
 BEGIN
 CheckItem(myMenus[GraphM], GraphColor, false);
 GraphColor := theitem;
 CheckItem(myMenus[GraphM], GraphColor, true);
 END; {of graph menu}
 AxisMenu : 
 BEGIN
 CheckItem(myMenus[AxisM], AxisColor, false);
 AxisColor := theitem;
 CheckItem(myMenus[AxisM], AxisColor, true);
 END; {of axis menu}
 BackgroundMenu : 
 BEGIN
 CheckItem(myMenus[BackgroundM], BackgroundColor, false);
 BackgroundColor := theitem;
 CheckItem(myMenus[BackgroundM], BackgroundColor, true);
 END; {of background menu}
 OptionMenu : 
 BEGIN
 CheckItem(myMenus[OptionM], Option, false);
 Option := theitem;
 CheckItem(myMenus[OptionM], Option, true);
 END;
 OTHERWISE
 BEGIN
 END;
 END; {of theMenu}
 HiliteMenu(0);  {un-hilite selected menu}
 END;

 PROCEDURE doContent; {(ConEvent : EventRecord}
 {contentWindow : windowPtr);}
 VAR
 localPt, globalPt : Point;
 part : integer;
 myRect : Rect;
 control : ControlHandle;
 BEGIN
 IF contentWindow <> FrontWindow THEN
 SelectWindow(contentWindow);
 globalPt := ConEvent.where;
 localPt := globalPt;{global coord of mouse}
 GlobalToLocal(localPt);  {local coord of mouse}
 part := FindControl(localPt, contentWindow, control);

 IF contentWindow = PlotWindow THEN
 BEGIN
 SetPort(PlotWindow);
 IF part <> 0 THEN
 BEGIN {in control}
 END;
 IF part = 0 THEN
 BEGIN  {content region}
 myRect := PlotWindow^.portRect;
 IF PtInRect(localPt, myRect) THEN
 BEGIN
 END;  {of ptInRect}
 END; { of part=0 }
 END; {of contentwindow}
 END;  {of proc}

 PROCEDURE doDrag; {(GrabWindow : WindowPtr}
 {GlobalMouse : point);}
 BEGIN
 DragWindow(GrabWindow, GlobalMouse, DragArea);
 END;

 PROCEDURE doGrow; {(ResizeWindow : WindowPtr;}
 {Globalmouse : point;}
 {ZoomFlg:Boolean);}
 VAR
 newSize : LongInt;
 hsize : integer;
 vsize : integer;
 oldPort : GrafPtr;
 myRect : rect;
 tempLong : LongInt;
 BEGIN
 IF (ResizeWindow <> FrontWindow) THEN
 SelectWindow(ResizeWindow)
 ELSE
 BEGIN
 IF (ZoomFlg) THEN
 BEGIN
 WITH ResizeWindow^.portRect DO
 BEGIN
 tempLong := bottom - top;
 newSize := BitShift(tempLong, 16);
 newSize := newSize + (right - left);
 END;
 END
 ELSE
 newSize := GrowWindow(ResizeWindow, Globalmouse, GrowArea);
 IF newSize <> 0 THEN
 BEGIN  {grow the window}
 hsize := LoWord(newSize);
 vsize := HiWord(newSize);
 IF ResizeWindow = PlotWindow THEN
 BEGIN
 WITH ResizeWindow^.portRect DO {Pre-Grow}
 BEGIN
SetRect(VCRect, right - (SBarWidth - 1), top - 1, right + 1, bottom - 
(SBarWidth - 2));
SetRect(HCRect, left - 1, bottom - (SBarWidth - 1), right - (SBarWidth 
- 2), bottom + 1);
SetRect(GrowRect, HCRect.right, HCRect.top, VCRect.right, HCRect.bottom);
SetRect(PicRect, left, top, right - (SBarWidth - 1), bottom - (SBarWidth 
- 1));
 END;  {of with }
 InvalRect(VCRect);
 InvalRect(HCRect);
 InvalRect(GrowRect);
SizeWindow(ResizeWindow, hsize, vsize, TRUE); {new portRect}
 WITH ResizeWindow^.portRect DO {Post Grow}
 BEGIN
SetRect(VCRect, right - (SBarWidth - 1), top - 1, right + 1, bottom - 
(SBarWidth - 2));
SetRect(HCRect, left - 1, bottom - (SBarWidth - 1), right - (SBarWidth 
- 2), bottom + 1);
SetRect(GrowRect, HCRect.right, HCRect.top, VCRect.right, HCRect.bottom);
SetRect(PicRect, left, top, right - (SBarWidth - 1), bottom - (SBarWidth 
- 1));
SetRect(PageRect, left, top, right - (SBarWidth - 1), bottom - (SBarWidth 
- 1));
 END;  {of with }
 InvalRect(VCRect);
 InvalRect(HCRect);
 InvalRect(GrowRect);
 InvalRect(PicRect);
 END; {of if ResizeWindow}
 END; {of grow window stuff}
 END; {of if then newsize}
 END;  { of proc }

END. {of unit}


UNIT MyFileStuff;

INTERFACE

 USES
 ROM85, PrintTraps, PlotGlobals, Misc;

 PROCEDURE doSaveAs;
 PROCEDURE doSave;

IMPLEMENTATION

 PROCEDURE doSaveAs;
 LABEL
 1, 2;
 CONST
 SFPutLeft = 82;
 SFPutTop = 50;
 headerBytes = 512;
 TYPE
 DrawHeader = RECORD
 fill : ARRAY[1..256] OF integer;
 END;
 VAR
 SFPutPt : Point;
 theReply : SFReply;
 err : OSErr;
 refNum : Integer;
 bytes : LongInt;
 myWindow : WindowPtr;
 title : str255;
 myType : OSType;
 myCreator : OSType;
 str1, str2 : str255;
 header : DrawHeader;
 i : integer;
 myPicture : PicHandle;
 PicLength : LongInt;
 BEGIN
 myPicture := PlotDocHandle^^.Drawing;
 IF myPicture = NIL THEN
 doMessage(‘No picture to save yet!’, ‘’, ‘’, ‘’)
 ELSE
 BEGIN
 SetPt(SFPutPt, SFPutLeft, SFPutTop);
 WITH header DO
 BEGIN
 FOR i := 1 TO headerBytes DIV 2 DO
 fill[i] := 0;
 END;
 bytes := headerBytes;
 myWindow := PlotWindow;
 GetWTitle(myWindow, title);
 SFPutFile(SFPutPt, ‘Save Plot as ’, title, NIL, theReply);
 IF theReply.good THEN
 BEGIN {theReply.good}
 myType := ‘PICT’;
 myCreator := ‘MDRW’; {MacDraw doc}
 err := Create(theReply.fname, theReply.vRefNum, myCreator, myType);
 IF err <> noErr THEN
 BEGIN {err<>noErr}
 IF err = dupFNErr THEN
 BEGIN {err=dupFNerr}
 err := FSDelete(theReply.fname, theReply.vRefNum);
 IF err <> noErr THEN
 BEGIN {err<>noErr}
 doMessage(‘Cannot delete duplicate file.’, ‘’, ‘’, ‘’);
 GOTO 1;
 END; {err<>noErr}
 err := Create(theReply.fname, theReply.vRefNum, myCreator, myType);
 IF err <> noErr THEN
 BEGIN {err<>noErr}
 doMessage(‘Cannot create file...’, ‘’, ‘’, ‘’);
 GOTO 1;
 END; {err<>noErr}
 END {err=dupFNerr}
 ELSE {create file error}
 BEGIN {create file error}
 doMessage(‘Cannot create new file...’, ‘’, ‘’, ‘’);
 GOTO 1;
 END; {create file error}
 END; {err<>noErr}
 err := FSOpen(theReply.fname, theReply.vRefNum, refNum);
 IF err <> noErr THEN
 BEGIN
 doMessage(‘Cannot open file...’, ‘’, ‘’, ‘’);
 GOTO 1;
 END;
 err := SetFPos(refNum, FSFromStart, 0);
 IF err <> noErr THEN
 BEGIN
 doMessage(‘Cannot set start of file...’, ‘’, ‘’, ‘’);
 GOTO 2;
 END;

 err := FSWrite(refNum, bytes, @header);
 IF err <> noErr THEN
 BEGIN
 doMessage(‘Cannot write header to file...’, ‘’, ‘’, ‘’);
 GOTO 2;
 END;
 IF bytes <> 512 THEN
 BEGIN
 NumToString(bytes, str1);
 str1 := concat(str1, ‘ bytes’);
 str2 := concat(‘out of ‘, ‘512’);
 doMessage(‘Only able to write ‘, str1, str2, ‘to file.’);
 GOTO 2;
 END;
 PicLength := GetHandleSize(Handle(DrawingPic));
 bytes := PicLength;
 err := FSWrite(refNum, bytes, pointer(DrawingPic^));
 IF err <> noErr THEN
 BEGIN
 doMessage(‘Cannot write picture to file...’, ‘’, ‘’, ‘’);
 GOTO 2;
 END;
 IF bytes <> PicLength THEN
 BEGIN
 NumToString(bytes, str1);
 str1 := concat(str1, ‘ bytes’);
 NumToString(PicLength, str2);
 str2 := concat(‘out of ‘, str2);
 doMessage(‘Only able to write ‘, str1, str2, ‘to file.’);
 GOTO 2;
 END;
 SetWTitle(myWindow, theReply.fname);
 PlotDocHandle^^.FileName := theReply.fname;
 PlotDocHandle^^.VolRefNum := theReply.vRefNum;
 EnableItem(myMenus[FileM], fSave);
2 :
 err := FSClose(refNum);
 IF err <> noErr THEN
 BEGIN
 doMessage(‘Cannot close file...’, ‘’, ‘’, ‘’);
 ExitToShell;
 END;
 err := FlushVol(NIL, theReply.vRefNum);
 IF err <> NoErr THEN
 BEGIN
 doMessage(‘Cannot flush volume...’, ‘’, ‘’, ‘’);
 ExitToShell;
 END;
1 :
 SetCursor(arrow);
 END; {if good}
 END; {else pic exits}
 END;{ of proc}

 PROCEDURE doSave;
 LABEL
 1, 2;
 CONST
 headerBytes = 512;
 TYPE
 DrawHeader = RECORD
 fill : ARRAY[1..256] OF integer;
 END;
 VAR
 err : OSErr;
 refNum : Integer;
 bytes : LongInt;
 myWindow : WindowPtr;
 title : str255;
 str1, str2 : str255;
 header : DrawHeader;
 i : integer;
 myPicture : PicHandle;
 PicLength : LongInt;
 myRefNum : integer;
 myFname : str255;
 BEGIN
 myPicture := PlotDocHandle^^.Drawing;
 IF myPicture = NIL THEN
 doMessage(‘No picture to save yet!’, ‘’, ‘’, ‘’)
 ELSE
 BEGIN
 myRefNum := PlotDocHandle^^.VolRefNum;
 myFname := PlotDocHandle^^.FileName;
 IF myRefNum = 0 THEN
 BEGIN
 doMessage(‘Cannot save file’, ‘Use SaveAs...’, ‘’, ‘’);
 GOTO 1;
 END;

 WITH header DO
 BEGIN
 FOR i := 1 TO headerBytes DIV 2 DO
 fill[i] := 0;
 END;
 bytes := headerBytes;

 err := FSOpen(myFname, myRefNum, refNum);
 IF err <> noErr THEN
 BEGIN
 doMessage(‘Cannot open file...’, ‘’, ‘’, ‘’);
 GOTO 1;
 END;
 err := SetFPos(refNum, FSFromStart, 0);
 IF err <> noErr THEN
 BEGIN
 doMessage(‘Cannot set start of file...’, ‘’, ‘’, ‘’);
 GOTO 2;
 END;

 err := FSWrite(refNum, bytes, @header);
 IF err <> noErr THEN
 BEGIN
 doMessage(‘Cannot write header to file...’, ‘’, ‘’, ‘’);
 GOTO 2;
 END;
 IF bytes <> 512 THEN
 BEGIN
 NumToString(bytes, str1);
 str1 := concat(str1, ‘ bytes’);
 str2 := concat(‘out of ‘, ‘512’);
 doMessage(‘Only able to write ‘, str1, str2, ‘to file.’);
 GOTO 2;
 END;
 PicLength := GetHandleSize(Handle(DrawingPic));
 bytes := PicLength;
 err := FSWrite(refNum, bytes, pointer(DrawingPic^));
 IF err <> noErr THEN
 BEGIN
 doMessage(‘Cannot write picture to file...’, ‘’, ‘’, ‘’);
 GOTO 2;
 END;
 IF bytes <> PicLength THEN
 BEGIN
 NumToString(bytes, str1);
 str1 := concat(str1, ‘ bytes’);
 NumToString(PicLength, str2);
 str2 := concat(‘out of ‘, str2);
 doMessage(‘Only able to write ‘, str1, str2, ‘to file.’);
 GOTO 2;
 END;
2 :
 err := FSClose(refNum);
 IF err <> noErr THEN
 BEGIN
 doMessage(‘Cannot close file...’, ‘’, ‘’, ‘’);
 ExitToShell;
 END;
 err := FlushVol(NIL, myRefNum);
 IF err <> NoErr THEN
 BEGIN
 doMessage(‘Cannot flush volume...’, ‘’, ‘’, ‘’);
 ExitToShell;
 END;
1 :
 SetCursor(arrow);
 END; {if good}
 END;{ of proc}

END.


UNIT solve;

INTERFACE

 USES
 ROM85, PrintTraps, PlotGlobals, Misc;

 PROCEDURE quad (a, b, c : real;
 VAR x1, x2 : real;
 VAR result : integer);

 FUNCTION solveit : integer;
 PROCEDURE doPlot;
 PROCEDURE PrQDStuff (pRect : rect;
 QDdevice : integer);

IMPLEMENTATION

 FUNCTION positivecalc (a, b, check : real) : real;
 BEGIN
 positivecalc := (-b + sqrt(check)) / (2 * a);
 END;

 FUNCTION negativecalc (a, b, check : real) : real;
 BEGIN
 negativecalc := (-b - sqrt(check)) / (2 * a);
 END;

 PROCEDURE doDialog;
 VAR
 dialogP : DialogPtr;
 item : integer;
 dtype : integer;
 ditem : handle;
 drect : rect;
 dtext : Str255;
 BEGIN
 dialogP := GetNewDialog(ParamDialog, NIL, pointer(-1));
 IF dialogP = NIL THEN
 BEGIN
 doMessage(‘Dialog crash!’, ‘We are dead...’, ‘’, ‘’);
 ExitToShell;
 END;
 initCursor;
 IF dialogflg THEN
 BEGIN
 dtext := StringOf(a : 4 : 1);
 GetDItem(dialogP, dA, dtype, ditem, drect);
 SetIText(ditem, dtext);
 dtext := StringOf(b : 4 : 1);
 GetDItem(dialogP, dB, dtype, ditem, drect);
 SetIText(ditem, dtext);
 dtext := StringOf(c : 4 : 1);
 GetDItem(dialogP, dC, dtype, ditem, drect);
 SetIText(ditem, dtext);
 dtext := StringOf(step : 5 : 3);
 GetDItem(dialogP, dSTEP, dtype, ditem, drect);
 SetIText(ditem, dtext);
 dtext := StringOf(xscale);
 GetDItem(dialogP, dXSCALE, dtype, ditem, drect);
 SetIText(ditem, dtext);
 dtext := StringOf(yscale);
 GetDItem(dialogP, dYSCALE, dtype, ditem, drect);
 SetIText(ditem, dtext);
 END;
 REPEAT
 ModalDialog(NIL, item);
 UNTIL item = dOK;
 GetDItem(dialogP, dA, dtype, ditem, drect);
 GetIText(ditem, dtext);
 ReadString(dtext, a);
 GetDItem(dialogP, dB, dtype, ditem, drect);
 GetIText(ditem, dtext);
 ReadString(dtext, b);
 GetDItem(dialogP, dC, dtype, ditem, drect);
 GetIText(ditem, dtext);
 ReadString(dtext, c);
 GetDItem(dialogP, dSTEP, dtype, ditem, drect);
 GetIText(ditem, dtext);
 ReadString(dtext, step);
 GetDItem(dialogP, dXSCALE, dtype, ditem, drect);
 GetIText(ditem, dtext);
 ReadString(dtext, xscale);
 GetDItem(dialogP, dYSCALE, dtype, ditem, drect);
 GetIText(ditem, dtext);
 ReadString(dtext, yscale);
 PlotDocHandle^^.aParam := a;
 PlotDocHandle^^.bParam := b;
 PlotDocHandle^^.cParam := c;
 PlotDocHandle^^.stepParam := step;
 PlotDocHandle^^.xParam := xscale;
 PlotDocHandle^^.yParam := yscale;
 dialogflg := true;
 DisposDialog(dialogP);
 END;

 PROCEDURE quad; {(a, b, c : real;var x1, x2 : real;var result : integer);}
 VAR
 check : real;
 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;

 PROCEDURE PrQDStuff; {(pRect : rect; QDdevice : integer);}
 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;
 IF QDdevice = LaserWriter THEN
 BEGIN
 TextFont(times);
 TextSize(10);
 TextFace([]);
 TextMode(srcOr);
 END;
 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;
 x := -xscale / 2;
 y := a * x * x + (b * x) + c;
 hPos := integer(round(x * hor * 2 / xscale + hor));
 vPos := integer(round(-y * ver * 2 / yscale + ver));
 z1 := -b / (2 * a);
 z2 := (4 * a * c - (b * b)) / (4 * a);
 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}
 str1 := stringOf(-xscale DIV 2);
 str2 := stringOf(yscale DIV 2);
 str3 := stringOf(xscale DIV 2);
 str4 := stringOf(-yscale DIV 2);

 Paragraph[1] := StringOf(‘y=ax^2 + bx + c’, chr(13));
 Paragraph[2] := StringOf(‘a=’, a : 3 : 1, ‘, b=’, b : 3 : 1, ‘, c=’, 
c : 3 : 1, chr(13));
 Paragraph[3] := StringOf(‘x1=’, x1 : 5 : 3, ‘, x2=’, x2 : 5 : 3, chr(13));
 CASE result OF
 1 : 
Paragraph[4] := StringOf(‘Two Real Roots, x1, x2’, chr(13));
 2 : 
Paragraph[4] := StringOf(‘Double Root’, chr(13));
 3 : 
Paragraph[4] := StringOf(‘Two Complex Roots ‘, chr(13));
 OTHERWISE
 ;
 END;
Paragraph[5] := StringOf(‘Slope 0 = (‘, z1 : 2 : 1, ‘,’, z2 : 2 : 1, 
‘)’, chr(13));

 PenNormal;
 BackColor(Color[BackgroundColor]);
 ForeColor(Color[AxisColor]);

 {Drawing Boundry}
 PicComment(picDwgBeg, 0, NIL); {Begin MacDraw Document}
 PicComment(picGrpBeg, 0, NIL);
 PicComment(SetLineWidth, 2, 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(Color[GraphColor]);

 {Plot Itsef}
 PicComment(picGrpBeg, 0, NIL);
 moveto(hPos, vPos);
 REPEAT
 x := x + step;
 y := a * x * x + (b * x) + c;
 hPos := integer(round(x * hor * 2 / xscale + hor));
 vPos := integer(round(-y * ver * 2 / yscale + 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 >= xscale / 2;
 PicComment(picGrpEnd, 0, NIL);
 ForeColor(Color[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, 2, 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 select all objects}
 picComment(picDwgEnd, 0, NIL); {of drawing}
 SetClip(SaveClip);
 disposHandle(handle(width));
 DisposeRgn(TextClipRgn);
 DisposeRgn(SaveClip);
 END;

 PROCEDURE PlotMe;
 CONST
 Display = 1;
 VAR
 Displayrect : rect;
 pstate : PenState;
 BEGIN
 Displayrect := PicRect;
 IF PlotDocHandle^^.drawing <> NIL THEN
 BEGIN
 KillPicture(DrawingPic);
 PlotDocHandle^^.drawing := NIL;
 END;
 GetPenState(pstate);
 DrawingPic := OpenPicture(Displayrect);
 PrQDStuff(Displayrect, Display);
 ClosePicture;
 SetPenState(pstate);
 InvalRect(Displayrect); {draw picture}
 PlotDocHandle^^.drawing := DrawingPic; {save it}
 END;

 FUNCTION solveit; { : integer;}
 BEGIN
 doDialog;
 IF a <> 0 THEN
 quad(a, b, c, x1, x2, result)
 ELSE
 result := -1;
 solveit := result;
 END;

 PROCEDURE doPlot;
 BEGIN
 result := solveit;
 showWindow(PlotWindow);
 IF PlotWindow <> FrontWindow THEN
 SelectWindow(PlotWindow);
 IF result <> -1 THEN
 BEGIN
 PlotMe;
 EnableItem(myMenus[FileM], fPrint);
 END;
 END;
END.

UNIT MyPrintStuff;

INTERFACE

 USES
 ROM85, PrintTraps, PlotGlobals, Misc, myFileStuff, Solve;
 PROCEDURE doPageSet;
 PROCEDURE doPrint;
IMPLEMENTATION

 PROCEDURE PrintMe;
 CONST
 LaserWriter = 2;
 VAR
 theWorld : rect;
 pstate : penstate;
 BEGIN
 theWorld := PicRect;
 IF Option = 1 THEN
 theWorld := PicRect
 ELSE IF Option = 2 THEN
 theWorld := PageRect
 ELSE
 doMessage(‘Printing Rectangle Problem’, ‘’, ‘’, ‘’);
 GetPenState(pstate);
 PrintingPic := OpenPicture(theWorld);
 PrQDStuff(theWorld, LaserWriter);
 ClosePicture;
 SetPenState(pstate);
 DrawPicture(PrintingPic, theWorld);
 KillPicture(PrintingPic);
 END;

 PROCEDURE doPrint;
 VAR
 DoIt : boolean;
 myPrint : THPrint;
 myPrStatus : TPrStatus;
 myPrPort : TPPrPort;
 PrRect : rect;
 str1 : str255;
 temp : GrafPtr;
 numCopies : integer;
 count : integer;
 prStatus : TPrStatus;
 BEGIN {1}
 IF DrawingPic <> NIL THEN
 BEGIN {2}
 myPrint := PlotDocHandle^^.print;
 getport(temp);
 PrOpen;
 IF PrError = noErr THEN
 BEGIN {3}
 DoIt := PrValidate(myPrint);
 DoIt := PrJobDialog(myPrint);
 IF PrError <> noErr THEN
 doMessage(‘Printer error in job dialog’, ‘’, ‘’, ‘’);
 IF DoIt THEN
 BEGIN {4}
 myPrPort := PrOpenDoc(myPrint, NIL, NIL);
 IF PrError = noErr THEN
 BEGIN {5}
 numCopies := myPrint^^.prJob.iCopies;
 FOR count := 1 TO numCopies DO
 BEGIN {6}
 PrOpenPage(myPrPort, NIL);
 IF PrError = noErr THEN
 BEGIN {7}
 { print something dummy!}
 PageRect := myPrint^^.prInfo.rPage;
 PrintMe;
 END { 7}
 ELSE
doMessage(‘OpenPage error’, ‘cannot print this page’, ‘’, ‘’);
 PrClosePage(myPrPort);
 IF PrError <> noErr THEN
doMessage(‘ClosePage error’, ‘cannot close this page’, ‘’, ‘’);
 END; {6}
 END { 5}
 ELSE
 doMessage(‘OpenDoc error’, ‘cannot print’, ‘’, ‘’);
 PrCloseDoc(myPrPort);
 IF PrError <> noErr THEN
 doMessage(‘CloseDoc error’, ‘’, ‘’, ‘’);
 IF (myPrint^^.prJob.bJDocLoop = bSpoolLoop) AND (PrError = noerr) THEN
 PrPicFile(myPrint, NIL, NIL, NIL, prStatus);
 END; {4}
 END; {3}
 PrClose;
 setport(temp);
 END;{2}
 END; {of proc 1}


 PROCEDURE doPageSet;
 VAR
 DoIt : boolean;
 myPrint : THPrint;
 BEGIN
 myPrint := PlotDocHandle^^.print;
 PrOpen;
 IF PrError = noErr THEN
 BEGIN
 DoIt := PrValidate(myPrint);
 DoIt := PrStlDialog(myPrint);
 IF PrError <> noErr THEN
 doMessage(‘Printer error in style dialog’, ‘’, ‘’, ‘’)
 ELSE
 PageRect := myPrint^^.prInfo.rpage;
 END
 ELSE
 doMessage(‘Cannot perform PrOpen!’, ‘’, ‘’, ‘’);
 PrClose;
 END;
END. {of unit}

UNIT PlotGlobals;
INTERFACE
 USES
 ROM85, PrintTraps;
{ Global Constants }
 CONST

 {multifinder stuff}
 SysEnvTrap = $90;
 WNETrapNum = $60;    {trap number of WaitNextEvent}
 UnImplTrapNum = $9F;{trap number”unimplemented trap”}
 {window constants}
 ZoomBox = 8;  {window type}
 MinWidth = 80;
 MinHeight = 80;
 mBarHeightGlobal = $BAA;
 GrayRgnLowMemGlobal = $9EE;
 sBarWidth = 16;
 rsrc = ‘PLTR’;  {creator bytes restype}
 {dialog stuff}
 AboutDialog = 256;
 ParamDialog = 257;
 MessageDialog = 258;
 AlertDialog = 260;
 { menu res id’s}
 AppleMenu = 256;
 FileMenu = 257;
 EditMenu = 258;
 ColorMenu = 259;
 OptionMenu = 260;
 {submenus id’s}
 GraphMenu = 44;
 AxisMenu = 45;
 BackgroundMenu = 46;
 MenuCount = 5;
 SubMenuStart = 6;
 TotalMenuCount = 8;
 AppleM = 1;
 FileM = 2;
 EditM = 3;
 ColorM = 4;
 OptionM = 5;
 GraphM = 6;
 AxisM = 7;
 BackGroundM = 8;
 {menu items}
 aAbout = 1;
 fPlot = 1;
 fSave = 3;
 fSaveAs = 4;
 fPageSet = 5;
 fPrint = 6;
 fQuit = 8;
 eUndo = 1;
 eCut = 3;
 eCopy = 4;
 ePaste = 5;
 eClear = 6;
 oWindowRect = 1;
 oPageRect = 2;
 {Dialog Items}
 dOK = 1;
 dA = 2;
 dB = 3;
 dC = 4;
 dSTEP = 5;
 dXSCALE = 6;
 dYSCALE = 7;
 TYPE
 Document = RECORD
 aParam : real;
 bParam : real;
 cParam : real;
 stepParam : real;
 xParam : integer;
 yParam : integer;
 drawing : PicHandle;
 print : THPrint;
 FileName : str255;
 volRefNum : integer;
 END;
 DocPtr = ^Document;
 DocHandle = ^DocPtr;
 LongAndByte = RECORD
 CASE integer OF
 1 : (
 longView : LongInt
 );
 2 : (
 byteView : RECORD
 byte0 : SignedByte;
 byte1 : Signedbyte;
 byte2 : Signedbyte;
 byte3 : Signedbyte;
 END;
 );
 END;
{ Global Variables }
 VAR
 {my misc stuff}
 Finished : boolean;
 mBarHeight : Integer;
 {Multifinder stuff}
 WNE : boolean; {Multifinder friendly}
 SysEnv : boolean; {Multifinder friendly}
 theWorld : SysEnvRec;  {not in LSP 1.11 }
 typeOfMac : integer;
 mouseRgn : RgnHandle; {cursor region to pass to WNE}
 {menu stuff}
 myMenus : ARRAY[1..TotalMenuCount] OF MenuHandle;
 GraphColor : integer;
 AxisColor : integer;
 BackgroundColor : integer;
 color : ARRAY[1..8] OF LongInt;
 Option : integer; {1 = windowrect, 2=pagerect}
 {rectangles}
 DragArea : Rect;  {window drag area}
 GrowArea : Rect;{window grow area}
 Screen : Rect;  {physical screen area}
 PlotWindowRect : Rect; {beginning window size}
 ZoomRect : Rect;{zoomed window size}
 HCRect, VCRect, GrowRect : Rect; {scroller rects}
 PicRect : Rect; {content region of less scrollers}
 PageRect : rect;
 {dialogs stuff}
 ItemHit : integer;
 dialogflg : boolean;
 {plot stuff}
 a, b, c, x1, x2, check, step : real;
 result, xscale, yscale : integer;
 PlotWindow : WindowPtr;
 PlotWindowPeek : WindowPeek;
 PlotDocHandle : DocHandle;
 DrawingPic : PicHandle;
 PrintingPic : PicHandle;

IMPLEMENTATION

END.

*Plotter.R
*

Plotter.RSRC
????????

Type PLTR = STR 
 ,0
© by Dave Kelly & Dave Smith \0Dver 4 JAN 1988

Type FREF
,128
APPL 0
,129
PICT 1

Type BNDL
,128
PLTR 0
ICN#
0 128 1 129
FREF 
0 128 1 129

* ------ Multifinder events --------

* bit 15 = switcher save screen
* bit 14 = accept suspend resume events
* bit 13 = switcher enable option switch
* bit 12 = can do background on null events
* bit 11 = multifinder aware 
*         (activates & deactivates topmost 
*           window at resume, suspend events)

Type SIZE = GNRL
 ,-1
.H
4800    ;; $4800 = bits 14,11 set
.L
128000  ;; (for 150K recomended)
.L
80000 ;; (for 80K minimum)
.I
* ------------ menus ------------

Type MENU
* the desk acc menu
 ,256
\14;;apple menu
 About Plotter 
 (-

* the file menu
 ,257
File
 Plot /P
 (-
 (Save /S
 Save as 
 Page Setup  /U
 (Print  /O
 (-
   Quit /Q

* the edit menu
 ,258
Edit
 (Undo /Z
 (-
 Cut /X
 Copy /C
 Paste /V
 Clear

* the color menu
 ,259
Color
 Graph /\1B!\2C
 Axis /\1B!\2D
 Background /\1B!\2E

* the Option menu
 ,260
Print Options
 Window Size /[
 Page Size /]

* submenus
 ,44
Graph
 Black
 White
 Red
 Green
 Blue
 Cyan
 Magenta
 Yellow

 ,45
Axis
 Black
 White
 Red
 Green
 Blue
 Cyan
 Magenta
 Yellow

 ,46
Background
 Black
 White
 Red
 Green
 Blue
 Cyan
 Magenta
 Yellow


* ------ Dialogs --------
* About Box dialog...
type DLOG
 ,256
About Plotter 
100 100 250 400 
visible NoGoAway 
1
0
256

type DITL
 ,256
3
BtnItem Enabled
112 235 141 284 
OK

StatText Disabled
10 88 141 289 
Plot Demo\0D\0D++
Graphs Quadratic Equations\0D^0\0D^1\0D^2\0D^3

PicItem Disabled
10 10 96 81 
128

* Plot box dialog...
type DLOG
 ,257
Plot Parameters
100 105 250 405 
Visible NoGoAway 
4
0
257

type DITL
 ,257
13
* ok button (default)
BtnItem Enabled
110 230 136 275 
OK

* a parameter
EditText Enabled
30 15 46 60 
1

* b parameter
EditText Enabled
30 100 46 145 
-1

*c parameter
EditText Enabled
30 180 46 225 
-6

* step parameter
EditText Enabled
80 10 96 65 
.05

* xscale parameter
EditText Enabled
80 100 96 150
10

* yscale parameter
EditText Enabled
80 185 96 235 
20

StatText Disabled
10 35 26 55 
a

StatText Disabled
10 120 26 140 
b

StatText Disabled
10 200 26 220 
c

StatText Disabled
60 10 76 65 
step size

StatText Disabled
60 95 76 150 
x scale

StatText Disabled
60 180 76 250 
y scale

* Program Messages Dialog box...
type DLOG
 ,258
Program Messages
100 100 200 400
Visible NoGoAway
1
0
258

type DITL
 ,258
3
BtnItem Enabled
65 230 95 285
OK

StatText Disabled
15 60 85 222 
^0\0D^1\0D^2\0D^3

IconItem Disabled
10 10 42 42
1

* ------ Alerts ------------

* Program error alerts...
type ALRT
 ,260
100 100 200 400
260
5555

type DITL
 ,260
2

BtnItem
65 230 95 285
OK

StatText Disabled
15 60 60 275 
Program Problem Alert:\0D^0^1^2^3

* misc resources

Type ICN# = GNRL
  ,128 (0)
.H
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 8000
*
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 8000 

Type ICN# = GNRL
  ,129 (0)
.H
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
*
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 

 
TYPE PICT = GNRL
 ,128
.I
891
195 254 281 325
.H
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

* Menu color Definitions
*
* TYPE mctb followed by ID#,
* followed by number of entries.
* (ID 0 is menu bar entry.)
* Other ID# are menu ID#.
*
* For each entry:
* 1. Menu ID number
* 2. Menu item number
* 3. RGB color 1 (3 INTEGERS)
* 4. RGB color 2 (3 INTEGERS)
* 5. RGB color 3 (3 INTEGERS)
* 6. RGB color 4 (3 INTEGERS)
* 7. filler integer


* menu bar
Type mctb = GNRL
 ,0
* number of entries
.I
1
.I
* Menu ID number & the Menu item number
* 0 & 0 for menu bar entry.
* Default title & title background =
* black on white
* Default item & item background = 
* magenta on white
0 0
.H
0000 0000 0000
FFFF FFFF FFFF 
FFFF 0000 FFFF 
FFFF FFFF FFFF 
0000

* apple menu
 ,256
.I
3
.I
* title & title background =
* Cyan on white
* default item & background=
* red on white
256 0
.H
0000 FFFF FFFF 
FFFF FFFF FFFF 
FFFF 0000 0000 
FFFF FFFF FFFF 
0000
.I
* ITEM ONE
* Mark, command, name
* and background =
* blue on white
256 1
.H
0000 0000 FFFF
0000 0000 FFFF
0000 0000 FFFF
FFFF FFFF FFFF 
0000
.I
* ITEM TWO
* Mark, command, name
* and background =
* blue on white
256 2
.H
0000 0000 FFFF
0000 0000 FFFF
0000 0000 FFFF
FFFF FFFF FFFF 
0000

* Graph menu
 ,44
.I
8
.I
* black on white
44 1
.H
0000 0000 0000
0000 0000 0000
0000 0000 0000
FFFF FFFF FFFF
0000
.I
* black on white
44 2
.H
0000 0000 0000
0000 0000 0000
0000 0000 0000
FFFF FFFF FFFF
0000
.I
* red on white
44 3
.H
FFFF 0000 0000
FFFF 0000 0000
FFFF 0000 0000
FFFF FFFF FFFF
0000
.I
* green on white
44 4
.H
0000 FFFF 0000
0000 FFFF 0000
0000 FFFF 0000
FFFF FFFF FFFF
0000
.I
* blue on white
44 5
.H
0000 0000 FFFF
0000 0000 FFFF
0000 0000 FFFF
FFFF FFFF FFFF
0000
.I
* cyan on white
44 6
.H
0000 FFFF FFFF
0000 FFFF FFFF
0000 FFFF FFFF
FFFF FFFF FFFF
0000
.I
* magenta on white
44 7
.H
FFFF 0000 FFFF
FFFF 0000 FFFF
FFFF 0000 FFFF
FFFF FFFF FFFF
0000
.I
* yellow on white
44 8
.H
FFFF FFFF 0000
FFFF FFFF 0000
FFFF FFFF 0000
FFFF FFFF FFFF
0000

* Axis menu
 ,45
.I
8
.I
45 1
.H
0000 0000 0000
0000 0000 0000
0000 0000 0000
FFFF FFFF FFFF
0000
.I
45 2
.H
0000 0000 0000
0000 0000 0000
0000 0000 0000
FFFF FFFF FFFF
0000
.I
45 3
.H
FFFF 0000 0000
FFFF 0000 0000
FFFF 0000 0000
FFFF FFFF FFFF
0000
.I
45 4
.H
0000 FFFF 0000
0000 FFFF 0000
0000 FFFF 0000
FFFF FFFF FFFF
0000
.I
45 5
.H
0000 0000 FFFF
0000 0000 FFFF
0000 0000 FFFF
FFFF FFFF FFFF
0000
.I
45 6
.H
0000 FFFF FFFF
0000 FFFF FFFF
0000 FFFF FFFF
FFFF FFFF FFFF
0000
.I
45 7
.H
FFFF 0000 FFFF
FFFF 0000 FFFF
FFFF 0000 FFFF
FFFF FFFF FFFF
0000
.I
45  8
.H
FFFF FFFF 0000
FFFF FFFF 0000
FFFF FFFF 0000
FFFF FFFF FFFF
0000

* Background menu
 ,46
.I
8
.I
46 1
.H
0000 0000 0000
0000 0000 0000
0000 0000 0000
FFFF FFFF FFFF
0000
.I
46 2
.H
0000 0000 0000
0000 0000 0000
0000 0000 0000
FFFF FFFF FFFF
0000
.I
46 3
.H
FFFF 0000 0000
FFFF 0000 0000
FFFF 0000 0000
FFFF FFFF FFFF
0000
.I
46 4
.H
0000 FFFF 0000
0000 FFFF 0000
0000 FFFF 0000
FFFF FFFF FFFF
0000
.I
46 5
.H
0000 0000 FFFF
0000 0000 FFFF
0000 0000 FFFF
FFFF FFFF FFFF
0000
.I
46 6
.H
0000 FFFF FFFF
0000 FFFF FFFF
0000 FFFF FFFF
FFFF FFFF FFFF
0000
.I
46 7
.H
FFFF 0000 FFFF
FFFF 0000 FFFF
FFFF 0000 FFFF
FFFF FFFF FFFF
0000
.I
46 8
.H
FFFF FFFF 0000
FFFF FFFF 0000
FFFF FFFF 0000
FFFF FFFF FFFF
0000
.I

 
AAPL
$99.76
Apple Inc.
+2.09
MSFT
$44.08
Microsoft Corpora
+0.45
GOOG
$520.84
Google Inc.
+9.67

MacTech Search:
Community Search:

Software Updates via MacUpdate

Apple iOS 8.1 - The latest version of Ap...
The latest version of iOS can be downloaded through iTunes. Apple iOS 8 comes with big updates to apps you use every day, like Messages and Photos. A whole new way to share content with your family.... Read more
TechTool Pro 7.0.5 - Hard drive and syst...
TechTool Pro is now 7, and this is the most advanced version of the acclaimed Macintosh troubleshooting utility created in its 20-year history. Micromat has redeveloped TechTool Pro 7 to be fully 64... Read more
PDFKey Pro 4.0.2 - Edit and print passwo...
PDFKey Pro can unlock PDF documents protected for printing and copying when you've forgotten your password. It can now also protect your PDF files with a password to prevent unauthorized access and/... Read more
Yasu 2.9.1 - System maintenance app; per...
Yasu was originally created with System Administrators who service large groups of workstations in mind, Yasu (Yet Another System Utility) was made to do a specific group of maintenance tasks... Read more
Hazel 3.3 - Create rules for organizing...
Hazel is your personal housekeeper, organizing and cleaning folders based on rules you define. Hazel can also manage your trash and uninstall your applications. Organize your files using a... Read more
Autopano Giga 3.7 - Stitch multiple imag...
Autopano Giga allows you to stitch 2, 20, or 2,000 images. Version 3.0 integrates impressive new features that will definitely make you adopt Autopano Pro or Autopano Giga: Choose between 9... Read more
MenuMeters 1.8 - CPU, memory, disk, and...
MenuMeters is a set of CPU, memory, disk, and network monitoring tools for Mac OS X. Although there are numerous other programs which do the same thing, none had quite the feature set I was looking... Read more
Coda 2.5 - One-window Web development su...
Coda is a powerful Web editor that puts everything in one place. An editor. Terminal. CSS. Files. With Coda 2, we went beyond expectations. With loads of new, much-requested features, a few... Read more
Arq 4.6.1 - Online backup to Google Driv...
Arq is super-easy online backup for the Mac. Back up to your own Google Drive storage (15GB free storage), your own Amazon Glacier ($.01/GB per month storage) or S3, or any SFTP server. Arq backs up... Read more
Airfoil 4.8.10 - Send audio from any app...
Airfoil allows you to send any audio to AirPort Express units, Apple TVs, and even other Macs and PCs, all in sync! It's your audio - everywhere. With Airfoil you can take audio from any... Read more

Latest Forum Discussions

See All

This Week at 148Apps: October 13-17, 201...
Expert App Reviewers   So little time and so very many apps. What’s a poor iPhone/iPad lover to do? Fortunately, 148Apps is here to give you the rundown on the latest and greatest releases. And we even have a tremendous back catalog of reviews; just... | Read more »
Angry Birds Transformers Review
Angry Birds Transformers Review By Jennifer Allen on October 20th, 2014 Our Rating: :: TRANSFORMED BIRDSUniversal App - Designed for iPhone and iPad Transformed in a way you wouldn’t expect, Angry Birds Transformers is a quite... | Read more »
GAMEVIL Announces the Upcoming Launch of...
GAMEVIL Announces the Upcoming Launch of Mark of the Dragon Posted by Jessica Fisher on October 20th, 2014 [ permalink ] Mark of the Dragon, by GAMEVIL, put | Read more »
Interview With the Angry Birds Transform...
Angry Birds Transformers recently transformed and rolled out worldwide. This run-and-gun title is a hit with young Transformers fans, but the ample references to classic Transformers fandom has also earned it a place in the hearts of long-time... | Read more »
Find Free Food on Campus with Ypay
Find Free Food on Campus with Ypay Posted by Jessica Fisher on October 20th, 2014 [ permalink ] iPhone App - Designed for the iPhone, compatible with the iPad | Read more »
Strung Along Review
Strung Along Review By Jordan Minor on October 20th, 2014 Our Rating: :: GOT NO STRINGSUniversal App - Designed for iPhone and iPad A cool gimmick and a great art style keep Strung Along from completely falling apart.   | Read more »
P2P file transferring app Send Anywhere...
File sharing services like Dropbox have security issues. Email attachments can be problematic when it comes to sharing large files. USB dongles don’t fit into your phone. Send Anywhere, a peer-to-peer file transferring application, solves all of... | Read more »
Zero Age Review
Zero Age Review By Jordan Minor on October 20th, 2014 Our Rating: :: MORE THAN ZEROiPad Only App - Designed for the iPad With its mind-bending puzzles and spellbinding visuals, Zero Age has it all.   | Read more »
Hay Ewe Review
Hay Ewe Review By Campbell Bird on October 20th, 2014 Our Rating: :: SAVE YOUR SHEEPLEUniversal App - Designed for iPhone and iPad Pave the way for your flock in this line drawing puzzle game from the creators of Worms.   | Read more »
My Very Hungry Caterpillar (Education)
My Very Hungry Caterpillar 1.0.0 Device: iOS Universal Category: Education Price: $3.99, Version: 1.0.0 (iTunes) Description: Care for your very own Very Hungry Caterpillar! My Very Hungry Caterpillar will captivate you as he crawls... | Read more »

Price Scanner via MacPrices.net

2013 15-inch 2.0GHz Retina MacBook Pro availa...
B&H Photo has leftover previous-generation 15″ 2.0GHz Retina MacBook Pros now available for $1599 including free shipping plus NY sales tax only. Their price is $400 off original MSRP. B&H... Read more
Updated iPad Prices
We’ve updated our iPad Air Price Tracker and our iPad mini Price Tracker with the latest information on prices and availability from Apple and other resellers, including the new iPad Air 2 and the... Read more
Apple Pay Available to Millions of Visa Cardh...
Visa Inc. brings secure, convenient payments to iPad Air 2 and iPad mini 3as well as iPhone 6 and 6 Plus. Starting October 20th, eligible Visa cardholders in the U.S. will be able to use Apple Pay,... Read more
Textkraft Pocket – the missing TextEdit for i...
infovole GmbH has announced the release and immediate availability of Textkraft Pocket 1.0, a professional text editor and note taking app for Apple’s iPhone. In March 2014 rumors were all about... Read more
C Spire to offer iPad Air 2 and iPad mini 3,...
C Spire on Friday announced that it will offer iPad Air 2 and iPad mini 3, both with Wi-Fi + Cellular, on its 4G+ LTE network in the coming weeks. C Spire will offer the new iPads with a range of... Read more
Belkin Announces Full Line of Keyboards and C...
Belkin International has unveiled a new lineup of keyboard cases and accessories for Apple’s newest iPads, featuring three QODE keyboards and a collection of thin, lightweight folios for both the... Read more
Verizon offers new iPad Air 2 preorders for $...
Verizon Wireless is accepting preorders for the new iPad Air 2, cellular models, for $100 off MSRP with a 2-year service agreement: - 16GB iPad Air 2 WiFi + Cellular: $529.99 - 64GB iPad Air 2 WiFi... Read more
Price drops on refurbished Mac minis, now ava...
The Apple Store has dropped prices on Apple Certified Refurbished previous-generation Mac minis, with models now available starting at $419. Apple’s one-year warranty is included with each mini, and... Read more
Apple refurbished 2014 MacBook Airs available...
The Apple Store has Apple Certified Refurbished 2014 MacBook Airs available for up to $180 off the cost of new models. An Apple one-year warranty is included with each MacBook, and shipping is free.... Read more
Refurbished 2013 MacBook Pros available for u...
The Apple Store has Apple Certified Refurbished 13″ and 15″ MacBook Pros available starting at $929. Apple’s one-year warranty is standard, and shipping is free: - 13″ 2.5GHz MacBook Pros (4GB RAM/... Read more

Jobs Board

*Apple* Retail - Multiple Positions (US) - A...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
Position Opening at *Apple* - Apple (United...
…customers purchase our products, you're the one who helps them get more out of their new Apple technology. Your day in the Apple Store is filled with a range of Read more
Position Opening at *Apple* - Apple (United...
**Job Summary** At the Apple Store, you connect business professionals and entrepreneurs with the tools they need in order to put Apple solutions to work in their Read more
Position Opening at *Apple* - Apple (United...
**Job Summary** The Apple Store is a retail environment like no other - uniquely focused on delivering amazing customer experiences. As an Expert, you introduce people Read more
Position Opening at *Apple* - Apple (United...
**Job Summary** As businesses discover the power of Apple computers and mobile devices, it's your job - as a Solutions Engineer - to show them how to introduce these Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.