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

iClock 5.5 - Customizable menu bar clock...
iClock replaces the old Apple's default menu bar clock with more features, customization and increases your productivity. Features: Have your Apple or Google calendar instantly available from the... Read more
Garmin Express 6.18.0.0 - Manage your Ga...
Garmin Express is your essential tool for managing your Garmin devices. Update maps, golf courses and device software. You can even register your device. Update maps Update software Register your... Read more
MarsEdit 4.3.5 - Quick and convenient bl...
MarsEdit is a blog editor for OS X that makes editing your blog like writing email, with spell-checking, drafts, multiple windows, and even AppleScript support. It works with with most blog services... Read more
Xcode 11.0 - Integrated development envi...
Xcode includes everything developers need to create great applications for Mac, iPhone, iPad, and Apple Watch. Xcode provides developers a unified workflow for user interface design, coding, testing... Read more
DaisyDisk 4.8 - $9.99
DaisyDisk allows you to visualize your disk usage and free up disk space by quickly finding and deleting big unused files. The program scans your disk and displays its content as a sector diagram... Read more
VMware Fusion 11.5.0 - Run Windows apps...
VMware Fusion and Fusion Pro - virtualization software for running Windows, Linux, and other systems on a Mac without rebooting. The latest version includes full support for Windows 10, macOS Mojave... Read more
Apple Configurator 2.10 - Configure and...
Apple Configurator makes it easy to deploy iPad, iPhone, iPod touch, and Apple TV devices in your school or business. Use Apple Configurator to quickly configure large numbers of devices connected to... Read more
Spotify 1.1.15.448. - Stream music, crea...
Spotify is a streaming music service that gives you on-demand access to millions of songs. Whether you like driving rock, silky R&B, or grandiose classical music, Spotify's massive catalogue puts... Read more
MenuMeters 1.9.8 - CPU, memory, disk, an...
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
Blocks 3.5.2 - RapidWeaver plug-in; divi...
Blocks is a plugin for RapidWeaver. It allows you to place blocks of text, graphics, and even raw HTML onto your page wherever you like. Drag blocks around, resize them, and even overlap content. All... Read more

Latest Forum Discussions

See All

Marvel Strike Force is adding Agent Coul...
Marvel Strike Force, the popular squad-based RPG, is set to receive a bunch of new content over the next few weeks. [Read more] | Read more »
Lots of premium games are going free (so...
You may have seen over the past couple weeks a that a bunch of premium games have suddenly become free. This isn’t a mistake, nor is it some last hurrah before Apple Arcade hits, and it’s important to know that these games aren’t actually becoming... | Read more »
Yoozoo Games launches Saint Seiya Awaken...
If you’re into your anime, you’ve probably seen or heard of Saint Seiya. Based on a shonen manga by Masami Kurumada, the series was massively popular in the 1980s – especially in its native Japan. Since then, it’s grown into a franchise of all... | Read more »
Five Nights at Freddy's AR: Special...
Five Nights at Freddy's AR: Special Delivery is a terrifying new nightmare from developer Illumix. Last week, FNAF fans were sent into a frenzy by a short teaser for what we now know to be Special Delivery. Those in the comments were quick to... | Read more »
Rush Rally 3's new live events are...
Last week, Rush Rally 3 got updated with live events, and it’s one of the best things to happen to racing games on mobile. Prior to this update, the game already had multiplayer, but live events are more convenient in the sense that it’s somewhat... | Read more »
Why your free-to-play racer sucks
It’s been this way for a while now, but playing Hot Wheels Infinite Loop really highlights a big issue with free-to-play mobile racing games: They suck. It doesn’t matter if you’re trying going for realism, cart racing, or arcade nonsense, they’re... | Read more »
Steam Link Spotlight - The Banner Saga 3
Steam Link Spotlight is a new feature where we take a look at PC games that play exceptionally well using the Steam Link app. Our last entry talked about Terry Cavanaugh’s incredible Dicey Dungeons. Read about how it’s a great mobile experience... | Read more »
Combo Quest (Games)
Combo Quest 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: Combo Quest is an epic, time tap role-playing adventure. In this unique masterpiece, you are a knight on a heroic quest to retrieve... | Read more »
Hero Emblems (Games)
Hero Emblems 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: ** 25% OFF for a limited time to celebrate the release ** ** Note for iPhone 6 user: If it doesn't run fullscreen on your device... | Read more »
Puzzle Blitz (Games)
Puzzle Blitz 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Puzzle Blitz is a frantic puzzle solving race against the clock! Solve as many puzzles as you can, before time runs out! You have... | Read more »

Price Scanner via MacPrices.net

Get one of Apple’s new 2019 iPhone 11 models...
Boost Mobile is offering the new 2019 Apple iPhone 11, iPhone 11 Pro, and 11 Pro Max for $100 off MSRP. Their discount reduces the cost of an iPhone 11 to $599 for the 64GB models, $899 for the 64GB... Read more
13″ 1.4GHz Silver MacBook Pros on sale for $1...
B&H Photo has new 2019 13″ 1.4GHz 4-Core Touch Bar Silver MacBook Pros on sale for $100 off Apple’s MSRP. Overnight shipping is free to many addresses in the US. These are the same MacBook Pros... Read more
4-core and 6-core 2018 Mac minis available at...
Apple has Certified Refurbished 2018 Mac minis available on their online store for $120-$170 off the cost of new models. Each mini comes with a new outer case plus a standard Apple one-year warranty... Read more
$250 prepaid Visa card with any Apple iPhone,...
Xfinity Mobile will include a free $250 prepaid Visa card with the purchase of any new iPhone, new line activation, and transfer of phone number to Xfinity Mobile. Offer is valid through October 27,... Read more
Sprint is offering the 64GB Apple iPhone 11 P...
Sprint has the new 64GB iPhone 11 Pro available for $12.50 per month for new customers with an eligible trade-in in of iPhone 7 or newer. That’s down from their standard monthly lease of $41.67. The... Read more
Final week: Apple’s 2019 Back to School Promo...
Purchase a new Mac using Apple’s Education discount, and take up to $400 off MSRP. All teachers, students, and staff of any educational institution with a .edu email address qualify for the discount... Read more
Save $30 on Apple’s AirPods at these reseller...
Amazon is offering discounts on new 2019 Apple AirPods ranging up to $30 off MSRP as part of their Labor Day sale. Shipping is free: – AirPods with Charging Case: $144.95 $15 off MSRP – AirPods with... Read more
Preorder your Apple Watch Series 5 today at A...
Amazon has Apple Watch Series 5 GPS models available for preorder and on sale today for $15 off Apple’s MSRP. Shipping is free and starts on September 20th: – 40mm Apple Watch Series 5 GPS: $384.99 $... Read more
21″ iMacs on sale for $100 off Apple’s MSRP,...
B&H Photo has new 21″ Apple iMacs on sale for $100 off MSRP with models available starting at $999. These are the same iMacs offered by Apple in their retail and online stores. Overnight shipping... Read more
2018 4 and 6-Core Mac minis on sale today for...
Apple resellers are offering new 2018 4-Core and 6-Core Mac minis for $100-$150 off MSRP for a limited time. B&H Photo has the new 2018 4-Core and 6-Core Mac minis on sale for up to $150 off... Read more

Jobs Board

Geek Squad *Apple* Master Consultation Agen...
**734380BR** **Job Title:** Geek Squad Apple Master Consultation Agent **Job Category:** Services/Installation/Repair **Location Number:** 000134-Dublin-Store **Job Read more
*Apple* Mobile Master - Best Buy (United Sta...
**727032BR** **Job Title:** Apple Mobile Master **Job Category:** Store Associates **Location Number:** 000356-Manassas-Store **Job Description:** **What does a Best Read more
*Apple* Mobility Pro - Best Buy (United Stat...
**733802BR** **Job Title:** Apple Mobility Pro **Job Category:** Store Associates **Location Number:** 000122-Las Vegas II-Store **Job Description:** At Best Buy, Read more
*Apple* Mobility Pro - Best Buy (United Stat...
**733541BR** **Job Title:** Apple Mobility Pro **Job Category:** Store Associates **Location Number:** 001180-Northridge-Store **Job Description:** At Best Buy, our Read more
*Apple* Mobile Master - Best Buy (United Sta...
**732324BR** **Job Title:** Apple Mobile Master **Job Category:** Store Associates **Location Number:** 000013-Fargo-Store **Job Description:** **What does a Best Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.