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

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 »
Aether Gazer unveils Chapter 16 of its m...
After a bit of maintenance, Aether Gazer has released Chapter 16 of its main storyline, titled Night Parade of the Beasts. This big update brings a new character, a special outfit, some special limited-time events, and, of course, an engaging... | Read more »
Challenge those pesky wyverns to a dance...
After recently having you do battle against your foes by wildly flailing Hello Kitty and friends at them, GungHo Online has whipped out another surprising collaboration for Puzzle & Dragons. It is now time to beat your opponents by cha-cha... | Read more »
Pack a magnifying glass and practice you...
Somehow it has already been a year since Torchlight: Infinite launched, and XD Games is celebrating by blending in what sounds like a truly fantastic new update. Fans of Cthulhu rejoice, as Whispering Mist brings some horror elements, and tests... | Read more »
Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, don’t remind me I could currently buy him this month I’m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »

Price Scanner via MacPrices.net

Apple Magic Keyboards for iPads are on sale f...
Amazon has Apple Magic Keyboards for iPads on sale today for up to $70 off MSRP, shipping included: – Magic Keyboard for 10th-generation Apple iPad: $199, save $50 – Magic Keyboard for 11″ iPad Pro/... Read more
Apple’s 13-inch M2 MacBook Airs return to rec...
Apple retailers have 13″ MacBook Airs with M2 CPUs in stock and on sale this weekend starting at only $849 in Space Gray, Silver, Starlight, and Midnight colors. These are the lowest prices currently... Read more
Best Buy is clearing out iPad Airs for up to...
In advance of next week’s probably release of new and updated iPad Airs, Best Buy has 10.9″ M1 WiFi iPad Airs on record-low sale prices for up to $200 off Apple’s MSRP, starting at $399. Sale prices... Read more
Every version of Apple Pencil is on sale toda...
Best Buy has all Apple Pencils on sale today for $79, ranging up to 39% off MSRP for some models. Sale prices for online orders only, in-store prices may vary. Order online and choose free shipping... Read more
Sunday Sale: Apple Studio Display with Standa...
Amazon has the standard-glass Apple Studio Display on sale for $300 off MSRP for a limited time. Shipping is free: – Studio Display (Standard glass): $1299.97 $300 off MSRP For the latest prices and... Read more
Apple is offering significant discounts on 16...
Apple has a full line of 16″ M3 Pro and M3 Max MacBook Pros available, Certified Refurbished, starting at $2119 and ranging up to $600 off MSRP. Each model features a new outer case, shipping is free... Read more
Apple HomePods on sale for $30-$50 off MSRP t...
Best Buy is offering a $30-$50 discount on Apple HomePods this weekend on their online store. The HomePod mini is on sale for $69.99, $30 off MSRP, while Best Buy has the full-size HomePod on sale... Read more
Limited-time sale: 13-inch M3 MacBook Airs fo...
Amazon has the base 13″ M3 MacBook Air (8GB/256GB) in stock and on sale for a limited time for $989 shipped. That’s $110 off MSRP, and it’s the lowest price we’ve seen so far for an M3-powered... Read more
13-inch M2 MacBook Airs in stock today at App...
Apple has 13″ M2 MacBook Airs available for only $849 today in their Certified Refurbished store. These are the cheapest M2-powered MacBooks for sale at Apple. Apple’s one-year warranty is included,... Read more
New today at Apple: Series 9 Watches availabl...
Apple is now offering Certified Refurbished Apple Watch Series 9 models on their online store for up to $80 off MSRP, starting at $339. Each Watch includes Apple’s standard one-year warranty, a new... Read more

Jobs Board

Licensed Practical Nurse - Womens Imaging *A...
Licensed Practical Nurse - Womens Imaging Apple Hill - PRN Location: York Hospital, York, PA Schedule: PRN/Per Diem Sign-On Bonus Eligible Remote/Hybrid Regular Read more
DMR Technician - *Apple* /iOS Systems - Haml...
…relevant point-of-need technology self-help aids are available as appropriate. ** Apple Systems Administration** **:** Develops solutions for supporting, deploying, Read more
Operating Room Assistant - *Apple* Hill Sur...
Operating Room Assistant - Apple Hill Surgical Center - Day Location: WellSpan Health, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Read more
Solutions Engineer - *Apple* - SHI (United...
**Job Summary** An Apple Solution Engineer's primary role is tosupport SHI customers in their efforts to select, deploy, and manage Apple operating systems and Read more
DMR Technician - *Apple* /iOS Systems - Haml...
…relevant point-of-need technology self-help aids are available as appropriate. ** Apple Systems Administration** **:** Develops solutions for supporting, deploying, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.