TweetFollow Us on Twitter

Reusable MacApp
Volume Number:2
Issue Number:12
Column Tag:Advanced Mac'ing

Reusable MacApp Classes

By Kurt J. Schmucker, PPI, Inc.

Kurt Schmucker is Technical Director for Educational Services at PPI in Sandy Hook, CT. He is also author of the best selling book Object-Oriented Programming for the Macintosh. In this article, Kurt introduces us to some nifty object oriented programming constructs for MacApp.

Designing Reusable Classes for MacApp Applications

Introduction

One of the most important benefits of object-oriented programming is the ability to easily reuse code. This reusability is of a completely different nature than the reusability of the Mac Toolbox, on one hand, and a Pascal unit, on the other. This is because of the dynamic binding and inheritance of object-oriented languages, of which Object Pascal is one example. In an object-oriented language, the messages sent to objects are not resolved until run-time and this resolution depends on the class of the object that receives the message. This dynamic binding is essential to the design and implementation of a framework like MacApp. For example, when MacApp wants to draw the interior of one of the windows in your application, it sends the Draw message to the view object installed in that window with some code like this:

window.fView.Draw;

The view that receives this message will be an instance of some view class that you have designed, and since you overrode its Draw method, the code you wrote will be invoked by this message. In this way even though the MacApp framework was written independently from your application, it can still call your code.

In addition, this same message (window.fView.Draw) is used for all the windows. For each window, the Draw message is sent to the appropriate view object which “knows” how to draw itself. Thus, the MacApp code for the TWindow class is reusable across all MacApp applications since it can be used without change in all of them.

There is also another kind of reusability. You might want to reuse all the functionality of the MacApp TWindow class, for example, except that you want one little change: you want a window that always asks for a confirmation before opening or closing. (Okay, so this is a weird thing to do. I'm just trying to make a point.) In an object-oriented language like Object Pascal, this is easy to do. You just design a new subclass of TWindow, call it TConfirmingWindow. This new class inherits all the functionality of the TWindow class, but in addition, you can augment the actions taken when the window opens or closes by designing your own methods for the Open and Close messages. Designing your own methods for the TConfirmingWindow is considerably easier than defining your own window or menu definition procedures -- the closest analog on the Mac. For example, this would be the way you would augment the open behavior of the window:

PROCEDURE TConfirmingWindow.Open;  OVERRIDE;
BEGIN
     IF SELF.ConfirmOpen
       THEN INHERITED Open;
END;

Fig. 1 The QuadWorld Program

This method is invoked when an instance of TConfirmingWindow is sent the Open message. This method first sends the message ConfirmOpen to this window, presumably to ask the user if this window really should appear on the Mac screen. If the answer is yes, this method then invokes its inherited Open method: the Open method for the TWindow class.

In this last example, you have reused code by inheriting it, overriding to augment the existing behavior of the window. The bottom line is that to implement the confirming window took you just a few lines of code.

This article discusses how you can use dynamic binding and inheritance to build your own reusable MacApp classes. In doing so, we will develop such a class and demonstrate its reusability by subclassing it to achieve two new kinds of functionality with only the minimum of work.

Note that the style of reusability possible in an object-oriented language is not meant to replace the kinds of software packaging found in traditional subroutine libraries like the Toolbox or a unit. Rather it is an additional tool in the hands of the Mac programmer, a tool that is appropriate for many tasks but not everything.

Rules of Thumb for Reusable Classes

Using an object-oriented language does not guarantee reusable software, just as using structured programming does not guarantee readable or maintainable code. Object-oriented programming does, however, give you the potential for a level of reusability unachievable in languages that do not have dynamic binding or inheritance.

Here are some of the rules of thumb that I use to design reusable classes. Like all rules of thumb, they are meant to be followed in moderation.

Rule 1: Whenever you are faced with a choice, design a new method.

Rule 2: Methods should be single purpose.

Rule 3: Store as instance variables the data that is needed by more than one of your methods, or data that may be needed by a subclass.

Rule 4: In deciding on the superclass for a new class, method inheritance is more important than data inheritance.

Rule 5: Write for the library, not just for yourself.

Rule 6: Code defensively. You never know who will call your methods and under what circumstances.

Rule 1 encourages all major and most minor decisions to be made in separate methods so that these decisions can be overridden in subclasses. This gives the class the maximum flexibility as a base for new subclasses. Because this principle was followed in MacApp, it was easy to design the TConfirmingWindow class. What should happen when a window is to be open is a single method in the class and thus to change this in the subclass, only one method need be overridden. There is one drawback to following this rule: proliferation of methods. You can see this in MacApp, which consists of only 39 classes but over 560 methods. The benefits of this rule, however, outweigh this inconvenience.

Rule 2 reiterates that methods should be replaceable components of building blocks: replaceable in subclasses. If methods are multi-purpose, this is difficult. In the case of the TConfirmingWindow class, we were able to override one decision -- what to do when a window is to be opened -- in one method, without having to worry about what other things this method might do. This made the TConfirmingWindow class an excellent base for new classes. A corollary of this rule is that methods tend to be short: at most a few dozen statements or so.

Rule 3 encourages you to avoid global variables by storing data needed by several methods in instance variables. In addition to the data you need, consider also the data that might be needed by a subclass: data needed to support different resolutions of the decisions you made. (Rule 1)

Rule 4 helps you decide on the superclass for a class you are designing. Examine the behavior you will inherit from the superclass rather than the data you will also inherit.

Rule 5 encourages you to go that extra step or two. Don’t do just the bare minimum that will solve the problem at hand: design the additional functionality that will make this a class worthy of being in a library.

Rule 6 warns you that since your class may be subclassed by many other programmers, its methods may be called in circumstances you cannot control. You must therefore code extremely defensively.

QuadWorld Geometric Editor and the TListView Class

The application that will be used to demonstrate this design of reusable classes is the QuadWorld application. QuadWorld is a simple MacDraw-like geometric editor. It allows the user to enter and manipulate various kinds of quadrilaterals without regard to the geometric constraints associated with special quadrilaterals like rhombi. QuadWorld presents two representations (views) of the quadrilaterals to the end user: a graphical view and a textual view. It is the textual view that will be developed as a reusable MacApp class. Figure 1 is a screen dump of QuadWorld in action. For a more detailed discussion of the implementation of the QuadWorld application, as well as a complete listing of the QuadWorld application, see Object-Oriented Programming for the Macintosh, Hayden, 1986, by myself. The complete source is also available on the MacTutor Source Code Disk for this issue, Number 15, $8.00 from MacTutor.)

The list view class is a good candidate for some special design attention since its functionality -- displaying a list of text items so that the user can select one with the mouse -- is needed by many applications. (QuadWorld was designed and implemented before the new 128K ROMs with the List Manager was released.) Other parts of QuadWorld are not such good candidates. After all, how often do you need the code to rotate a rhombus?

This brings up an interesting point. Any class designed in Object Pascal can be reused and so, for that matter, can any piece of code. What we are talking about here is designing the code of a class to be used with the MacApp class library, so that it can be reused easily by other programmers who may not have access to the source code or don’t want to bother with all the fine points of the algorithms involved. These programmers will still want to be able to make small modifications to the functionality of the class, in the way that TConfirmingWindow is a slight modification of the vanilla window modelled by TWindow. This can be almost trivial to do, if the original class was designed well, by following the rules of thumb discussed earlier.

The TListView Class

The essential structure of the TListView class is shown in Figure 2. This collection of methods follows most (if not all) of the rules outlined above. For example, since I had to make a choice when setting up the QuickDraw port (font, text size, text style, etc.), I coded my preference in a separate method (SetPen). If you needed the functionality of this class, but wanted one of these characteristics to be different, you would only have to subclass TListView and override this one method.

Less obviously, whenever I am about to do something with an entire entry in the list (test for coverage by a mouse click, highlight, or move the pen into position for drawing), I use a method to compute the rectangle surrounding an item. Who knows, perhaps someone would want to accomplish this aspect of a list view differently. (In fact, the last section of this article does exactly this.)

In the area of data, the fCurrentItem instance variable always stores the number of the item currently being processed (drawn, enumerated, etc.) in case a subclass needs to use it. The fItemHeight variable stores the height of an individual item since this data is needed by several methods.

Of course, like any good idea, you can carry these rules-of-thumb of class design too far. Abusing rule 1, for example, will result in so many methods that no one can understand what is going on and, in addition, performance will degrade. I hope that I have successfully straddled the fence between a class that can’t easily be used as a basis for inheritance and one with so many methods as to be incomprehensible.

When a class is properly designed, note how short and simple the methods turn out:

PROCEDURE TListView.ChangeSelection(
 index: INTEGER);
BEGIN
 SELF.fFrame.Focus;
 SELF.DoHighlightSelection(SELF.fHLDesired, hlOff);
 SELF.fCurrentSelection := index;
 SELF.DoHighlightSelection(hlOff, SELF.fHLDesired);
END;

FUNCTION  TListView.DoMouseCommand(
 VAR downLocalPoint: Point; 
 VAR info: EventInfo;
 VAR hysteresis: Point): TCommand; OVERRIDE;
VAR index: INTEGER;
BEGIN
{If this mouse press results in a change of the current selection, let 
the document deal with it in any way it chooses.  This is done because 
the document might control several views, or might deal with a changed 
selection in some other application-specific manner. }

        DoMouseCommand := gNoChanges;
        index := SELF.PtToIndex(downLocalPoint);
        IF (index > 0) THEN
          BEGIN
             IF fCurrentSelection = index 
 { A click on the current selection means to
 deselect it }
 THEN
 TListDocument(SELF.fDocument).
 SetSelection(0)
 ELSE
 TListDocument(SELF.fDocument).
 SetSelection(index);
          END
END;

These methods are short and clear enough that it is easy to see that they are correct just by reading them.

Some of the interrelationships between the methods of the TListView class are also shown in Figure 2. Basically, there are a few basic methods that encode my decisions to some issues as to how a list view should behave (DrawOneItem, SetUpBox, SetItemHeightAndLineAscent, and SetPen). The rest of the methods are written in terms of each other and these basic ones, as you can see in both the ChangeSelection and DoMouseCommand methods above.

To get some new functionality in a subclass of a class designed in this way, you only need (usually) to override one or more of the basic methods -- everything else just works. On a considerably larger scale, MacApp works exactly the same way. Instead of modelling just a list of selectable items, MacApp models an entire Macintosh application and you just override some classes to add the functionality that distinguishes your application from all other ones.

The full source code for the TListView class is at the end of this article. (Since the MacApp classes can currently only be accessed from MPW Pascal, all the sample code and all the work for this article was done in MPW. While the latest version of TML Pascal supports the Object Pascal extensions, it does not yet support other Pascal extensions used by MacApp.)

Some TListView Subclass

In order to demonstrate the flexibility of a properly designed class like TListView, let’s use it as the superclass for two new classes. These two new classes will both portray lists of items in the QuadWorld application, but each will do so in a different way.

Figure 3

TTwoLineListView

The TTwoLineListView class presents a two-line entry for each quadrilateral. The first line is the type of quadrilateral, just like in TListView. However this does not allow you to distinguish between each of the squares or each of the rhombi, for example, without interacting with the view by highlighting individual quadrilaterals. TTwoLineListView corrects this problem. The second line of an entry in a TTwoLineListView display the coordinates of the vertices of the quadrilateral. Figure 3 shows a screen dump of QuadWorld using the TTwoLineListView.

To accomplish the functionality of the TTWoLineListView we had do nothing more than subclass TListView and override two of its basic methods, DrawOneItem and SetItemHeightAndLineAscent. Even this overriding did not require us to re-implement the functionality of the basic methods in TListView, but merely augment their computations. As was mentioned in Ken Doyle’s article on Object Pascal, the keyword INHERITED can be used to access a method of an ancestor or superclass. In TTwoLineListView, I did so in both of its methods.

Since the major theme of this article is reusability in MacApp, let me document the steps to add TTwoLineListView to QuadWorld, in particular, and the general steps of adding any new class to an application written with MacApp. Adding a new view like the two-line list view to an application like QuadWorld consisted of the following amount of work:

Design the new class.

= For the TTwoLineListView this was about 30 lines of MPW Pascal with a stub routine for the construction and formatting the line of text representing the vertices. (See step 4.)

Incorporate this new class into the application.

= Modify the make file to include dependencies on the new class file. In the case of the QuadWorld make file, this was three modifications, each of which were adding another dependency to an existing rule.

= Modify the USES statement in the application’s major unit (UQuadWorld.p, in the case of QuadWorld) and main program (MQuadWorld.p) by adding the name of the new unit to the existing USES statement.

= Refer to the new class in the major unit of the application. In the case of the TTwoLineListView class in QuadWorld, this consisted of modifying the data type of one temporary variable in method TQuadDocument.DoMakeViews from the type TListView to the type TTwoLineListView. This causes an object of the TTwoLineListView class to be allocated at run-time and to be connected to the other objects that comprise the QuadWorld application.

Add whatever features necessary to the application to generate any new information needed by the new class.

= For QuadWorld, this meant providing methods in the TQuadDocument class and in the various quadrilateral classes to generate a string representing the vertex coordinates of any quadrilateral.

TMiniIconListView

The TMiniIconListView solves the problem of the individual who cannot remember which is a rhombus and which is a parallelogram since it presents a little icon for each type of quadrilateral. This icon is the same for all quadrilaterals, for all parallelograms, etc. Figure 4 shows a screen dump of QuadWorld using the TTwoLineListView.

To implement TMiniIconListView, I inherited from TListView and overrode only the DrawOneItem basic method.

Adding the necessary features to QuadWorld to generate the information needed by the mini-icon view (step 3 above) provides an interesting example of how programming with an object-oriented language differs from programming with a non-object-oriented language. My first version of TMiniIconListView.DrawOneItem ignored the peripheral issue of generating this information, concentrating rather on the structural issues of the new class that was being designed. This was done by using a stub routine for actually obtaining and drawing the icon:

PROCEDURE TMiniIconListView.DrawOneItem
 (item: Str255);  OVERRIDE;
    { Draw the mini-icon }
 PROCEDURE DrawIcon;
 VAR  tempRect: Rect;
 
 BEGIN
     { The rectangle into which the icon will be drawn }
 tempRect := SELF.SetUpBox(SELF.fCurrentItem);
 tempRect.right := tempRect.left +  SELF.fItemHeight;
    { Fake the icon for now }
 InsetRect(tempRect, 4, 4);
 FrameRect(tempRect);
 END;

BEGIN
 DrawIcon;
 Move(SELF.fItemHeight, 0);
 INHERITED DrawOneItem(item);
END;

To complete the TMiniIconListView class required fleshing out this stub. There were two extremes for this task: a non-flexible, non-object-oriented, closed solution something like this:

 
{ Get the icon by testing the itemString }
 CASE item OF
 'a Quadrilateral':icon := quadIcon;
 'a Parallelogram':icon := parallelogramIcon;
 'a Rhombus':    icon := rhombusIcon;
 'a Rectangle':  icon := rectangleIcon;
 'a Square':icon := squareIcon;
 OTHERWISEicon := quadIcon

or a flexible, object-oriented, open solution something like this:

{ Get the icon by “asking” the quad what its icon is }
icon := (SELF.fCurrentItem).AsQuad.MiniIconNumber

The object-oriented solution turns out to be slightly longer, but was so much clearer and more malleable with respect to future changes in QuadWorld that there really wasn't any choice as to which was the truly “best” solution.

To be more precise, the modifications to QuadWorld to generate the information needed by the mini-icon view were:

“Flesh” out the stub routine as follows:

PROCEDURE TMiniIconListView.DrawOneItem
 (item: Str255);  OVERRIDE;
   { Draw the mini-icon }
 PROCEDURE DrawIcon;
 VAR  tempRect: Rect;
 tempHandle: Handle;
 myDocument: TMiniIconListDocument;
 BEGIN
     { The rectangle into which the icon will be drawn }
 tempRect := SELF.SetUpBox(SELF.fCurrentItem);
 tempRect.right := tempRect.left +  SELF.fItemHeight;

    { Get the icon }
 myDocument := 
 TMiniIconListDocument(SELF.fDocument);
 tempHandle := 
 GetIcon(myDocument.ReportIconNumber
 (SELF.fCurrentItem));
 FailNIL(tempHandle);
 
     { Draw the Icon }
 PlotIcon(tempRect, tempHandle);
 END;
 
BEGIN
 DrawIcon;
 Move(SELF.fItemHeight, 0);
 INHERITED DrawOneItem(item);
END;

Add the new method ReportIconNumber to the TDocument class displayed by the TMiniIconListView.

This task is one of the few made more difficult by the characteristics of MPW Pascal. Even if you know that the instance of the subclass of TDocument that you will be using in your application will, at run-time, understand a message like ReportIconNumber, you must find some way to inform the compiler that everything is OK at compile-time. (More robust object-oriented languages like Smalltalk-80® and Objective-C® have no such requirement.) The solution to this difficulty, in the case of Object Pascal, is to define an abstract superclass with a null method for ReportIconNumber. This is the class TMiniIconListDocument.

Change the inheritance of TQuadDocument so that it is a specialization of TMiniIconListDocument and then implement the ReportIconNumber method as follows:

FUNCTION  TQuadDocument.ReportIconNumber
 (itemNumber: INTEGER): INTEGER;  OVERRIDE;
VAR theQuad: TQuad;
BEGIN
        theQuad := TQuad(SELF.ListIndexToQuad(itemNumber));
        IF theQuad = NIL
     THEN ReportIconNumber := cQuadIcon
     ELSE ReportIconNumber := theQuad.MiniIconNumber;
END;

Add the MiniIconNumber method to all the quadrilateral classes so that each kind of quadrilateral “knows” what its particular mini-icon number is.

Add the resource ids for the five new icons to the UQuadWorld unit.

Add the icon resources to the compiled QuadWorld application with ResEdit.

The source code for these two subclasses of TListView is also appended to the end of this article. For the QuadWorld program itself, see Source Code Disk #15.

{  List View unit used in the  QuadWorld Application  }
{ Copyright 1986, Productivity Products International, Inc. }
{ Reproduction & distribution rights granted to MacTutor }

 { This unit implements a list view like that of the Smalltalk
Listview class, i.e., a vertical list of text items any one of
which can be selected with the mouse.  Like the Smalltalk
class, this TListView makes some assumptions about the
protocol of the document it displays.  In particular,
TListView assumes that its fDocument field refers to an
instance of a subclass of TListDocument (defined here) and
thus has the following additional document methods (at a
minimum):
        T(Your)Document.SetSelection(newSelectionIndex);

This method is used to communicate to your document that the selection 
has changed, presumably because the user has selected a text string in 
the TListView
              T(Your)Document.ReportListItem: Str255;

A function which returns the textual version of a given item to be displayed 
by the TListView. Note that your document can contain primarily non-textual 
data - the ListView will display a textual representation of that data, 
a representation that you construct in this method.

     To see one example of how this ListView class can be used, see the 
QuadWorld application on source disk 15. }

UNIT UListView;

INTERFACE

USES
  { This set of units are portions of the Macintosh ROM }
    MemTypes, QuickDraw, OSIntf, ToolIntf, PackIntf,

  { This set of units are portions of MacApp }
    UObject, UList, UMacApp;

TYPE
    TListDocument = OBJECT(TDocument)
 { an abstract superclass that contains null methods
 needed for the proper use of a list view }
      { No additional instance variables }
        PROCEDURE TListDocument.SetSelection(
 newSelectionIndex: INTEGER);
        FUNCTION  TListDocument.ReportListItem(                itemNumber: 
INTEGER): Str255;
    END;   { of TListDocument }

    TListView = OBJECT(TView)
    { Instance variables }
        fCurrentItem:   INTEGER; { index of the current        
 selection, if any, otherwise 0 }
        fCurrentSelection:   INTEGER;{ index of the current
 selection, if any, 
 otherwise 0 } 
        fNumberOfItems:  INTEGER;  { number of items
 currently in the list }
        fItemHeight:  INTEGER;{ height of each line            
 including leading }
        fLineAscent:  INTEGER;{ position of baseline           
 relative to top of line }

   { Initialization }
        PROCEDURE TListView.IListView(
 itsParent: TView;   itsDocument: TListDocument;
 itsExtent: Rect;  itsVDeterminer: SizeDeterminer;
 itCanSelect: BOOLEAN);
         {$IFC qDebug}
        PROCEDURE TListView.Inspect; OVERRIDE;
 { Debugging print of the listView }
         {$ENDC}
   { Commands and Selections }
        PROCEDURE TListView.ChangeSelection(
 index: INTEGER);
        FUNCTION  TListView.DoMouseCommand(
 VAR downLocalPoint: Point; VAR info: EventInfo;
 VAR hysteresis: Point): TCommand;  OVERRIDE;
        FUNCTION  TListView.PtToIndex(
 testPoint: Point): INTEGER;
 { Convert from a point in local view
 coordinates to the index of the list of itemsat               
 this point.  This is a method so that it can
 easily be overridden by clients. }

   { Displaying }
        PROCEDURE TListView.AboutToDraw;  OVERRIDE;
 { Set the font and text style }
        PROCEDURE TListView.CalcMinExtent(
 VAR minExtent: Rect);  OVERRIDE;
 { This method must be overridden since a list view
 has a variable vertical extent. }
        PROCEDURE TListView.DoHighlightSelection(
 fromHL, toHL: HLState);  OVERRIDE;
        PROCEDURE TListView.Draw(area: Rect);  OVERRIDE;
        PROCEDURE TListView.DrawOneItem(item: Str255); 
 { Draw one item.  This is here so that it can                 
 easilly be overridden by a client. }
        PROCEDURE TListView.InvalidateItem(
 itemNumber: INTEGER);
        PROCEDURE TListView.SetItemHeightAndLineAscent;
 { Setup the item height and ascent for drawing the
 text; this is a method so that it can be overridden
 easily by clients}
        FUNCTION  TListView.SetUpBox(index: INTEGER): Rect;    
 { Calculate the surrounding rectangle for an item }
        PROCEDURE TListView.SetPen;
 { Setup the pen for drawing the text; this is a               method 
so that it can be overridden easily by
 clients}
    END;  { TListView }

IMPLEMENTATION

{ Private data }
CONST
 txMargin = 4;   { horizontal space between view edge and
 the left edge of the text }

{ *****  TListDocument  (an abstract superclass )  ****** }

    PROCEDURE TListDocument.SetSelection(
 newSelectionIndex: INTEGER);
    BEGIN
        {$IFC qDebug}
 ProgramBreak('Call to TListDocument.SetSelection
  - an abstract superclass');
 {$ENDC}
    END;

    FUNCTION  TListDocument.ReportListItem(
 itemNumber: INTEGER): Str255;
    BEGIN
        {$IFC qDebug}
 ProgramBreak('Call to TListDocument.ReportList
  - an abstract superclass'); 
 {$ENDC}
    END;


{ ****************  TListView  ************************ }

    PROCEDURE TListView.IListView(itsParent: TView;
 itsDocument: TListDocument;   itsExtent: Rect;
 itsVDeterminer: SizeDeterminer;
 itCanSelect: BOOLEAN);
    BEGIN
 SELF.IView(itsParent, itsDocument, itsExtent, sizeFixed,
 itsVDeterminer, itCanSelect, hlDim);
 SELF.fCurrentItem := 0;
 SELF.fCurrSelection := 0;
 SELF.fNumberOfItems := 0;
 SELF.fItemHeight := 17;       { Reasonable default }
 SELF.fLineAscent := 12;       { Reasonable default }
 SELF.fInformBeforeDraw := TRUE;
 { Before drawing the first time, call the
 AboutToDraw method }
    END;
 
{$IFC qDebug}
    PROCEDURE TListView.Inspect; OVERRIDE; 
 { Debugging print of the listView }
    BEGIN
 INHERITED Inspect;
 WriteLn(' The current item is: ', SELF.fCurrentItem);
 WriteLn(' The selected item is: ',SELF.fCurrentSelection);
 WriteLn(' The number of items is: ',SELF.fNumberOfItems);
 WriteLn(' The item height is: ', SELF.fItemHeight);
 WriteLn(' The line ascent is: ', SELF.fLineAscent);
    END;
{$ENDC}

{ Set the font and text style just before drawing the view for the first 
time }
    PROCEDURE TListView.AboutToDraw;  OVERRIDE;
 BEGIN
 SELF.SetPen;
 SELF.SetItemHeightAndAscent;
 SELF.AdjustExtent;
 SELF.fInformBeforeDraw := FALSE;  { No longer call
 this method }
 END;
 
{ This method must be overridden so that MacApp can determine the view's 
true extent }
    PROCEDURE TListView.CalcMinExtent(
 VAR minExtent: Rect);  OVERRIDE;
     BEGIN
     { How many items are there? }
        SELF.fCurrentItem := 1;
        WHILE (TListDocument(SELF.fDocument).
 ReportListItem( SELF.fCurrentItem ) <> ' ')
               DO SELF.fCurrentItem := SELF.fCurrentItem + 1;
        SELF.fNumberOfItems := SELF.fCurrentItem - 1;
 SELF.fCurrentItem := 0;
 { No item currently being processed }

     { Set the amount of room needed for that many items }
   minExtent := SELF.fExtentRect;
        IF SELF.fSizeDeterminer[v] <> sizeFixed
 { Only need to adjust vertical extent }
          THEN minExtent.botRight.v :=
 SELF.fNumberOfItems*SELF.fItemHeight;
    END;


    PROCEDURE TListView.ChangeSelection(
 index: INTEGER);
    BEGIN
        SELF.fFrame.Focus;
        SELF.DoHighlightSelection(SELF.fHLDesired, hlOff);
        SELF.fCurrentSelection := index;
        SELF.DoHighlightSelection(hlOff, SELF.fHLDesired);
    END;

{ Dim highlighting of text by gray XORing is not very readable, so dim 
highlight a text string by framing it with a gray rectangle.  (Standard 
highlighting when the window displaying
the view is active is still to invert - black XORing.)  The state transition 
diagram is:                                                          
     hlTo

               |  OFF   |    DIM   |   ON
          -----|--------|----------|--------
          OFF  |  (NA)  |   Frame  | Invert
          -----|--------|----------|--------
          DIM  | Frame  |   (NA)   | Frame +
hlFrom         |        |          | Invert
          -----|--------|----------|---------
          ON   | Invert | Invert + |  (NA)
               |        |  Frame   |
          -----|--------|----------|---------

  Since this matrix is (almost) symmetric, we can add together
the hlFrom and hlTo parameters and take one action for each of the three 
possible sums.  }

    PROCEDURE TListView.DoHighlightSelection(
 fromHL, toHL: HLState); OVERRIDE;
    VAR r:  Rect;
    BEGIN
        IF (SELF.fCurrentSelection > 0) THEN
            BEGIN
                { Make r be the rectangle to invert }
                r := SELF.SetUpBox(SELF.fCurrentSelection);
                InsetRect(r, 1, 1);

                { Set the pen pattern and mode properly }
                PenPat(ltGray);
                PenMode(patXor);

                IF RectIsVisible(r) THEN 
 { only do highlighting if part of the rectangle is            
 visible }
                  BEGIN
                      CASE (fromHl + toHL) OF
 hlOffDim:  FrameRect(r);
 hlOffOn:   InvertRect(r);
 hlDimOn:   IF fromHL = hlDim
 THEN BEGIN
 FrameRect(r);   InvertRect(r);
 END
 ELSE BEGIN
 InvertRect(r);  FrameRect(r);
 END;
                      END  { of CASE }
                  END
            END
    END;

    FUNCTION  TListView.DoMouseCommand(
 VAR downLocalPoint: Point; VAR info: EventInfo;
 VAR hysteresis: Point): TCommand; OVERRIDE;
    VAR index: INTEGER;
    BEGIN
        { If this mouse press results in a change of the current selection, 
let the document deal with it in any way it chooses.  This is done because 
the document might control several views, or might deal with a changed 
selection in some other application-specific manner. }

        DoMouseCommand := gNoChanges;
        index := SELF.PtToIndex(downLocalPoint);
        IF (index > 0) THEN
          BEGIN
              IF fCurrentSelection = index 
 { A click on current selection means to deselect it }
                THEN
 TListDocument(SELF.fDocument).SetSelection(0)
                ELSE
 TListDocument(SELF.fDocument).
 SetSelection(index);
          END
    END;

{ Write the textual representation of all the items, properly positioned 
}
    PROCEDURE TListView.Draw(area: Rect); OVERRIDE;
    VAR bBox:  Rect;    { the bounding box for a single item }
        stringToDraw:  Str255;
    BEGIN
         SELF.fCurrentItem := 1;
 { Get each item in turn from the document and draw it }
         stringToDraw := TListDocument(SELF.fDocument).
 ReportListItem( SELF.fCurrentItem );
         WHILE (stringToDraw <> ' ') DO
            BEGIN
                { Calculate the rectangle to fill }
                bBox := SELF.SetUpBox( SELF.fCurrentItem );

                IF RectIsVisible(bBox) THEN
 { only write text that will be seen }
                    BEGIN
                      MoveTo(bBox.left + txMargin, 
 bBox.top + SELF.fLineAscent);
                      SELF.DrawOneItem(stringToDraw);
                    END;

                SELF.fCurrentItem:= SELF.fCurrentItem + 1;
                stringToDraw := TListDocument(SELF.fDocument).
 ReportListItem( SELF.fCurrentItem);
            END;
          SELF.fNumberOfItems := SELF.fCurrentItem - 1;
          SELF.fCurrentItem := 0;
 { No item currently being processed }
    END;


    PROCEDURE TListView.DrawOneItem(item: Str255);
    BEGIN
        DrawString(item);
    END;

{ In theory, this method invalidates the area occupied by the item, that 
is, the listDocument will send this message to the view when any single 
item needs to be redrawn and will pass the number of this item as the 
message argument.   In reality, this method invalidates the entire panel 
so as not to leave "holes" in the textual representation of the document. 
 The argument is never used.   }

    PROCEDURE TListView.InvalidateItem(
 itemNumber: INTEGER);
    BEGIN
        SELF.AdjustExtent;
 { In case the size of the view has changed }
        SELF.InvalidRect(SELF.fExtentRect); 
 { AdjustExtent will cause a focus }
    END;

{ Decide what item is indicated by this point; return 0 iff this point 
indicates no item }
    FUNCTION  TListView.PtToIndex(
 testPoint: Point): INTEGER;
    VAR  i: INTEGER;   { FOR Loop Index }
    BEGIN
        PtToIndex := 0;     { Assume the item is NOT found }
        FOR i := 1 TO SELF.fNumberOfItems DO
           IF PtInRect(testPoint, SELF.SetUpBox(i))
             THEN BEGIN
 PtToIndex := i;
 LEAVE       { Don't check the rest }
                  END
    END;

{ Set the line spacing characteristics.  This is a separate method so 
that clients can easily override it without also having to worry about 
setting the font. It assumes that SetPet
has been called and that the view has been focused. }

    PROCEDURE TListView.SetItemHeightAndLineAscent;  
    VAR fInfo: FontInfo;
    BEGiN
      GetFontInfo(fInfo);
      WITH fInfo DO
        BEGIN
          SELF.fItemHeight := ascent + descent + leading + 1;
          SELF.fLineAscent := ascent + (leading DIV 2) - 1;
        END;
    END;

{ Set up the pen for drawing the characters; assumes that we are focused 
on the correct window (or a frame in the correct window); this is a method 
so it can be overridden easily }
    PROCEDURE TListView.SetPen;
    BEGIN
      PenNormal;
      TextFont(SystemFont);
      TextSize(12);
      TextFace([]);
    END;

{ Create a rectangle, for this item. }
    FUNCTION  TListView.SetUpBox(index: INTEGER): Rect;
    VAR  bBox: Rect;
    BEGIN
    { use same left and right as view, calculate the top and
 bottom for this item number}
        WITH bBox DO
            BEGIN
                left := SELF.fExtentRect.left;
                top := (index - 1)* SELF.fItemHeight;
 { Subtract 1 to get the TOP line }
                right := SELF.fExtentRect.right;
                bottom := top + SELF.fItemHeight;
            END;
        SetUpBox := bBox;
    END;

END.


{ TwoLineList View unit which is used in a variant of the MacApp QuadWorld 
Application.
Copyright 1986 by Productivity Products International, Inc. }

UNIT UTwoLineListView;

INTERFACE

USES
  { This set of units are portions of the Macintosh ROM }
    MemTypes, QuickDraw, OSIntf, ToolIntf, PackIntf,

  { This set of units are portions of MacApp }
    UObject, UList, UMacApp, UListView;

TYPE
    TTwoLineListDocument = OBJECT(TListDocument)
 { an abstract superclass that contains methods
 needed for the proper use of a two-line list view }
     { No additional instance variables }
        FUNCTION
 TTwoLineListDocument.ReportCoordinateString
 (itemNumber: INTEGER): Str255;      
    END;   { of TTwoLineListDocument }

    TTwoLineListView = OBJECT(TListView)
      { No additional instance variables }
        PROCEDURE TTwoLineListView.DrawOneItem
 (item: Str255);  OVERRIDE;    
        PROCEDURE TTwoLineListView.
 SetItemHeightAndLineAscent;   OVERRIDE;
    END;  { TTwoLineListView }

IMPLEMENTATION

{ ***  TTwoLineListDocument  (an abstract superclass )  ** }

    FUNCTION  TTwoLineListDocument.ReportCoordinateString
 (itemNumber: INTEGER): Str255;
    BEGIN
{$IFC qDebug}
 ProgramBreak('Call to TTwoLineListDocument.
 ReportCoordinateString- an abstract superclass'); 
{$ENDC}
    END;
 

{ *****************  TTwoLineListView  ***************}

{ Draw one item which consists of two lines.  Use the
overridden procedure to draw the first line and then augment
with the code to draw the second line. }
  PROCEDURE TTwoLIneListView.DrawOneItem
 (item: Str255);  OVERRIDE;
  VAR myDocument:TTwoLineListDocument;
  BEGIN
 INHERITED DrawOneItem(item);
 Move(-StringWidth(item), SELF.fItemHeight DIV 2);
 
        myDocument := TTwoLineListDocument
 (SELF.fDocument);
      DrawString(myDocument.ReportCoordinateString
 (SELF.fCurrentItem));
    END;

{ Double the item height for a two-line item. }
  PROCEDURE TTwoLineListView.
 SetItemHeightAndLineAscent;  OVERRIDE;
  BEGIN
 INHERITED SetItemHeightAndLineAscent;
 SELF.fItemHeight := 2*SELF.fItemHeight;
  END;

END.

 { MiniIcon ListView unit which is used in a variant of the the MacApp 
QuadWorld Application. Copyright 1986 by Productivity Products International, 
Inc., for MacTutor. }

UNIT UMiniIconListView;

INTERFACE

USES
  { This set of units are portions of the Macintosh ROM }
    MemTypes, QuickDraw, OSIntf, ToolIntf, PackIntf,

  { This set of units are portions of MacApp }
    UObject, UList, UMacApp, UListView;

TYPE
    TMiniIconListDocument = OBJECT(TListDocument)
 { an abstract superclass that contains methods          needed for the 
proper use of a mini-iconlist view }
      { No additional instance variables }
        FUNCTION  TMiniIconListDocument.ReportIconNumber
 (itemNumber: INTEGER): INTEGER; 
    END;   { of TMiniIconListDocument }
    TMiniIconListView = OBJECT(TListView)
      { No additional instance variables }
        PROCEDURE TMiniIconListView.DrawOneItem
 (item: Str255);  OVERRIDE;    
    END;  { TMiniIconListView }
IMPLEMENTATION
{ ***  TMiniIconListDocument  (an abstract superclass )  *** }
    FUNCTION  TMiniIconListDocument.
 ReportIconNumber(itemNumber: INTEGER): INTEGER;
    BEGIN
{$IFC qDebug}
 ProgramBreak('Call to TMiniIconListDocument.
 ReportIconNumber- an abstract superclass');
{$ENDC}
    END;
{ **************** TMiniIconListView  ***************** }
{ Draw an icon first, then draw the text string }
    PROCEDURE TMiniIconListView.DrawOneItem
 (item: Str255);  OVERRIDE;
   { Draw the mini-icon }
 PROCEDURE DrawIcon;
 VAR  tempRect: Rect;
 tempHandle: Handle;
 myDocument: TMiniIconListDocument;
 BEGIN
    { The rectangle into which the icon will be drawn }
 tempRect := SELF.SetUpBox(SELF.fCurrentItem);
 tempRect.right := tempRect.left + 
 SELF.fItemHeight;
    { Get the icon }
 myDocument := 
 TMiniIconListDocument(SELF.fDocument);
 tempHandle := 
 GetIcon(myDocument.ReportIconNumber
 (SELF.fCurrentItem));
 FailNIL(tempHandle);
    { Draw the Icon }
 PlotIcon(tempRect, tempHandle);
 END;
    BEGIN
 DrawIcon;
 Move(SELF.fItemHeight, 0);
 INHERITED DrawOneItem(item);
    END;
END.
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Netflix Games expands its catalogue with...
It is a good time to be a Netflix subscriber this month. I presume there's a good show or two, but we are, of course, talking about their gaming service that seems to be picking up steam lately. May is adding five new titles, and there are some... | Read more »
Seven Knights Idle Adventure drafts in a...
Seven Knights Idle Adventure is opening up more stages, passing the 15k mark, and players may find themselves in need of more help to clear these higher stages. Well, the cavalry has arrived with the introduction of the Legendary Hero Iris, as... | Read more »
AFK Arena celebrates five years of 100 m...
Lilith Games is quite the behemoth when it comes to mobile games, with Rise of Kingdom and Dislyte firmly planting them as a bit name. Also up there is AFK Arena, which is celebrating a double whammy of its 5th anniversary, as well as blazing past... | Read more »
Fallout Shelter pulls in ten times its u...
When the Fallout TV series was announced I, like I assume many others, assumed it was going to be an utter pile of garbage. Well, as we now know that couldn't be further from the truth. It was a smash hit, and this success has of course given the... | Read more »
Recruit two powerful-sounding students t...
I am a fan of anime, and I hear about a lot that comes through, but one that escaped my attention until now is A Certain Scientific Railgun T, and that name is very enticing. If it's new to you too, then players of Blue Archive can get a hands-on... | Read more »
Top Hat Studios unveils a new gameplay t...
There are a lot of big games coming that you might be excited about, but one of those I am most interested in is Athenian Rhapsody because it looks delightfully silly. The developers behind this project, the rather fancy-sounding Top Hat Studios,... | Read more »
Bound through time on the hunt for sneak...
Have you ever sat down and wondered what would happen if Dr Who and Sherlock Holmes went on an adventure? Well, besides probably being the best mash-up of English fiction, you'd get the Hidden Through Time series, and now Rogueside has announced... | Read more »
The secrets of Penacony might soon come...
Version 2.2 of Honkai: Star Rail is on the horizon and brings the culmination of the Penacony adventure after quite the escalation in the latest story quests. To help you through this new expansion is the introduction of two powerful new... | Read more »
The Legend of Heroes: Trails of Cold Ste...
I adore game series that have connecting lore and stories, which of course means the Legend of Heroes is very dear to me, Trails lore has been building for two decades. Excitedly, the next stage is upon us as Userjoy has announced the upcoming... | Read more »
Go from lowly lizard to wicked Wyvern in...
Do you like questing, and do you like dragons? If not then boy is this not the announcement for you, as Loongcheer Game has unveiled Quest Dragon: Idle Mobile Game. Yes, it is amazing Square Enix hasn’t sued them for copyright infringement, but... | Read more »

Price Scanner via MacPrices.net

Apple introduces the new M4-powered 11-inch a...
Today, Apple revealed the new 2024 M4 iPad Pro series, boasting a surprisingly thin and light design that pushes the boundaries of portability and performance. Offered in silver and space black... Read more
Apple introduces the new 2024 11-inch and 13-...
Apple has unveiled the revamped 11-inch and brand-new 13-inch iPad Air models, upgraded with the M2 chip. Marking the first time it’s offered in two sizes, the 11-inch iPad Air retains its super-... Read more
Apple discontinues 9th-gen iPad, drops prices...
With today’s introduction of the new 2024 iPad Airs and iPad Pros, Apple has (finally) discontinued the older 9th-generation iPad with a home button. In response, they also dropped prices on 10th-... Read more
Apple AirPods on sale for record-low prices t...
Best Buy has Apple AirPods on sale for record-low prices today starting at only $79. Buy online and choose free shipping or free local store pickup (if available). Sale price for online orders only,... Read more
13-inch M3 MacBook Airs on sale for $100 off...
Best Buy has Apple 13″ MacBook Airs with M3 CPUs in stock and on sale today for $100 off MSRP. Prices start at $999. Their prices, along with Amazon’s, are the lowest currently available for new 13″... Read more
Amazon is offering a $100 discount on every 1...
Amazon has every configuration and color of Apple’s 13″ M3 MacBook Air on sale for $100 off MSRP, now starting at $999 shipped. Shipping is free: – 13″ MacBook Air (8GB RAM/256GB SSD): $999 $100 off... Read more
Sunday Sale: Take $150 off every 15-inch M3 M...
Amazon is now offering a $150 discount on every configuration and color of Apple’s M3-powered 15″ MacBook Airs. Prices start at $1149 for models with 8GB of RAM and 256GB of storage: – 15″ M3 MacBook... Read more
Apple’s 24-inch M3 iMacs are on sale for $150...
Amazon is offering a $150 discount on Apple’s new M3-powered 24″ iMacs. Prices start at $1149 for models with 8GB of RAM and 256GB of storage: – 24″ M3 iMac/8-core GPU/8GB/256GB: $1149.99, $150 off... Read more
Verizon has Apple AirPods on sale this weeken...
Verizon has Apple AirPods on sale for up to 31% off MSRP on their online store this weekend. Their prices are the lowest price available for AirPods from any Apple retailer. Verizon service is not... Read more
Apple has 15-inch M2 MacBook Airs available s...
Apple has clearance, Certified Refurbished, 15″ M2 MacBook Airs available starting at $1019 and ranging up to $300 off original MSRP. These are the cheapest 15″ MacBook Airs for sale today at Apple.... Read more

Jobs Board

IN6728 Optometrist- *Apple* Valley, CA- Tar...
Date: May 8, 2024 Brand: Target Optical Location: Apple Valley, CA, US, 92308 **Requisition ID:** 824398 At Target Optical, we help people see and look great - and Read more
Nurse Anesthetist - *Apple* Hill Surgery Ce...
Nurse Anesthetist - Apple Hill Surgery Center Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Now Read more
LPN-Physician Office Nurse - Orthopedics- *Ap...
LPN-Physician Office Nurse - Orthopedics- Apple Hill Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Read more
Supervisor/Therapist Rehabilitation Medicine...
Supervisor/Therapist Rehabilitation Medicine - Apple Hill (Outpatient Clinic) - Day Location: York Hospital, York, PA Schedule: Full Time Sign-On Bonus Eligible Read more
BBW Sales Support- *Apple* Blossom Mall - Ba...
BBW Sales Support- APPLE BLOSSOM MALL Brand: Bath & Body Works Location: Winchester, VA, US Location Type: On-site Job ID: 04388 Job Area: Store: Sales and Support Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.