TweetFollow Us on Twitter

AutoSave
Volume Number:6
Issue Number:5
Column Tag:MacOOPs!

USavedMe Auto-Save Class

By Fred Condo Jr., Covina, CA

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

An Auto-Saving Document Class

[Fred Condo Jr. is a doctoral student of information science at the Claremont Graduate School.]

This article presents a new abstract document class for MacApp, TAutoSaveDocument, which allows the user to specify an interval after which the document automatically saves itself. Such a feature is useful in applications, such as word processing, in which a user is likely to make extensive changes to a document and lose track of time; this feature is definitely not good in applications where the user is likely to want to back out of changes with a Revert command.

TAutoSaveDocument is a subclass of TDocument, which MacApp defines. To use TAutoSaveDocument, you simply make your document class a subclass of it; even a completed MacApp application can be converted to use auto-saving documents in a matter of minutes. This speedy conversion is one of the benefits of object-oriented programming and MacApp.

Design considerations

I designed USavedMe, the Pascal unit that embodies TAutoSaveDocument, with the following results (some interrelated) in mind:

• Auto-saving should impose a minimal encumbrance on the end user.

• The interface to auto-saving should be through a modeless dialog.

• The dialog should be visually distinct from the document.

• The unit should be easy for programmers to use.

• It should take very little time to convert existing code.

• Complex search-and-replace operations on the source code of existing programs should not be needed.

• Program performance should not be degraded.

I used a modeless dialog in order to allow the user to summon the dialog and then decide to forget about it without dismissing it. This style of interface lets the user bring back the document or another window with a single click of the mouse; modal dialogs require the user first to find the Cancel button, then to click it, and finally to shift his or her attention to whatever it was that the dialog intruded upon. MacApp makes it so simple to invoke, use, and program modeless dialogs that there is no excuse for using a modal dialog unnecessarily. In the standard Macintosh Toolbox environment, modal dialogs are seductive, since they are significantly easier to code than are modeless dialogs. This factor is quite abolished under MacApp.

I chose rDocProc as the dialog window. Many designers of commercial applications have taken to using documentProc, the standard document window, for dialogs. Some people have been using documentProc for modal dialogs, which are supposed to be in dBoxProc windows. This is bad interface design. Users should be able to tell at a glance, without having to read a title or scan the contents of a window, what kind of window it is. Using the document window style indiscriminately puts an unnecessary cognitive burden on the user.

The round-cornered, inverse-titled window is fairly uncommon. Among Apple’s standard desk accessories, only the Calculator and the Puzzle use it. Its appearance is strikingly different from a document window’s. Inasmuch as DAs are essentially modeless dialogs, I would like to propose that such windows be used for modeless dialogs, instead of the document-style window. I believe that Apple should incorporate this recommendation into their human interface guidelines. Figure 1 shows a screen dump of Apple’s DrawShapes program as modified to use USavedMe.

Figure 1: Modeless dialogs and document windows

New document behavior

The unit USavedMe defines two classes: TAutoSaveDocument and TAutoSaveDialogView. The latter is the view class that implements the modeless dialog.

Besides its initialization routine, TAutoSaveDocument overrides four methods and implements one new method. The four overridden methods are Free, DoIdle, DoSetUpMenus, and DoMenuCommand. The new method is PoseAutoSaveDialog. In addition, the Fields method is overridden for debugging and inspector use.

The Free method simply checks to see if an associated dialog is open, which it tracks through the new fAutoSaveWindow field. If one exists, the document sends it a Close message, and calls INHERITED Free. Sending Close makes sure no dialogs are left floating around, which can happen with modeless, but not modal, dialogs.

MacApp calls DoIdle for any event handler in the command chain. For objects not installed as cohandlers, this means that the front-most, active document gets called at idle time to get some idle processing done. TAutoSaveDocument.DoIdle contains the code that determines whether the user-specified time has elapsed, and, if so, issues a Save message to itself. If anything were to degrade program performance, it would be this method. However, since it is called at intervals no smaller than 30 seconds, and since it is really a tiny bit of code, no perceptible degradation occurs. If your application’s documents are complex and large, of course, saving takes a while. In such cases, users should be advised not to set auto-saving to unnecessarily short intervals. Usually, 10 to 15 minutes make an adequate interval, balancing the desire to avoid re-doing work against the long time it takes to save.

When you make your application’s document class a descendent of TAutoSaveDocument, you will call INHERITED DoSetUpMenus, calling TAutoSaveDocument’s code (you do this in the normal course of MacApp programming). This allows the auto-save document to set up the two menu commands that it defines--“Auto-saving,” which is a check-mark item determining whether auto-saving is on, and “Set auto-save time,” which invokes the dialog. If you have been looking at the code listings (Listings 6 and 7), you may be wondering what the field fHasEverBeenSet is for. This field causes the “Auto-saving” menu item to be disabled until after the user has invoked and dismissed the dialog at least once. This prevents the user from turning on auto-saving without at least having seen what the default interval is. You may have a different design philosophy, and may want to alter this to allow auto-saving to be turned on regardless of whether the user has looked at the dialog.

You may also be wondering why “Set auto-save time” is disabled for untitled (actually, never-saved) documents. The reason is that, if you allow auto-saving to be invoked for a document that has no disk file, the user is presented with the SFPutFile dialog. If the user presses the Cancel button, the dialog is simply put back up, forcing the user to save the file, which is a rather ugly situation. The solution to this is to simply prevent auto-saving from being turned on before the file has first been saved “manually.” You may want to add a check box to SFPutFile that would allow the user to save and set auto-saving at the same time, or, alternatively, trap SFPutFile’s Cancel button to turn auto-saving off.

TAutoSaveDocument.DoMenuCommand accomplishes two things: first, it handles the two commands that USavedMe defines; second, it intercepts “Save” and “Save as ” commands. The reason for this is that the modeless dialog displays the name of the document to which it applies. Intercepting these commands allows the document to send any associated dialog a message telling it to update its document name.

PoseAutoSaveDialog reads the dialog from a view resource, sets the editable text item to the correct number, and adjusts a static text item so that, if the interval specified is 1 minute, the word “minute” is displayed instead of “minutes.” It then opens the modeless dialog window, which is then on its own.

Modeless dialog action

The other class defined in USavedMe is TAutoSaveDialogView, which has one field (fAutoSaveDocument) to hold a reference to its associated document. It overrides the following methods of TDialogView: Free, Close, DismissDialog, and, of course, Fields. It defines one new method, UpdateDocumentName, which was alluded to above.

Free sets the fAutoSaveWindow reference of TAutoSaveDocument to NIL, so that, when the dialog is freed, the document knows that it is gone. It then calls INHERITED Free.

There are two cases where the modeless dialog may be closed without being dismissed. One is when the user clicks the go-away box; the other is when the dialog’s document is closed. The purpose of overriding Close is to make these situations have the same result as dismissing the dialog by clicking the Cancel button. If this is not done, MacApp crashes when it tries to access the dialog view after it has been freed. The critical thing here is to set the dialog view’s fDismissed field to TRUE.

By the way, you may not agree that clicking the go-away box should be tantamount to pressing Cancel. One alternative to this might be to treat this form of dismissal like pressing the OK button only if the user has typed characters into the editable text item, and treating it like Cancel otherwise. Another alternative is simply to do away with the go-away box, making Cancel and OK the only ways to get rid of the dialog window besides closing the associated document.

DismissDialog is where the dialog gets to take its intended action. If Cancel is pressed, the dialog simply goes away. If OK is pressed, it validates the editable text, sets the appropriate fields of TAutoSaveDocument, and turns auto-saving on. Since the dialog view is enabled (in ViewEdit or in the Rez code), MacApp handles the Return, Enter, -., and Escape keys.

Using USavedMe in your code

As an example of converting an existing MacApp application to use auto-save documents, I chose Apple’s DrawShapes sample program. For the conversion, I had to make minor changes in MDrawShapes.p, UDrawShapes.p, UDrawShapes.inc1.p, and DrawShapes.r. Listings 1-4 show the changes made. Additions are shown in underline type; deletions are shown in strikeout type. DrawShapes doesn’t have a .MAMake (dependency) file, so I had to provide one to show the dependency of DrawShapes on USavedMe and of USavedMe on MacApp. The DrawShapes.MAMake file is shown in Listing 5.

In Listing 1 (changes to MDrawShapes.p), I have added USES specifications for USavedMe and the MacApp building blocks that USavedMe itself uses (UTEView and UDialog). Also, the initialization code now calls the initialization routines for these building blocks. (InitUSavedMe simply prevents the Linker from stripping out the code of TAutoSaveDialogView, which is to be instantiated from templates.)

Listing 3 shows that the only changes to UDrawShapes.p are a repetition of the above-mentioned USES declarations, and the substitution of TAutoSaveDocument for TDocument in the definition of the application’s document object class.

The changes to the implementation section, UDrawShapes.inc1.p, are even smaller, as shown in Listing 4. There, the only change is in the name of the initialization routine called from the IShapeDocument method. In the new case, rather than calling MacApp’s standard document initialization code (IDocument), you call IAutoSaveDocument instead. IAutoSaveDocument calls IDocument for you.

Listing 2 shows the Rez source changes for DrawShapes. Your program probably already uses template views, so you probably don’t have to add the lines to use them. The critical item is the line that includes the resource code for USavedMe. Also, note that the new definition for the File menu is simply MacApp’s default File menu, with USavedMe’s two commands inserted into it. In your code, you will want to simply add the two commands to your existing File menu. The Rez code for UsavedMe, which defines its view resource, is shown in Listing 8.

Idle Conflicts

What if your application wants its documents to get idle time, and your document type already implements DoIdle? In such a case, you would add one statement at the beginning of your DoIdle routine: DoIdle := INHERITED DoIdle(idlePhase);. In addition, IYourDocument, immediately after calling IAutoSaveDocument, should set the field fRespectIdleTime to TRUE. When this field is set, TAutoSaveDocument will not increase the value of fIdleFreq. This is particularly important if your documents are installed as MacApp cohandlers that require frequent idle processing. In the default case, fRespectIdleTime is FALSE, and toggling auto-saving off sets fIdleFreq to MAXLONGINT.

Conclusion

MacApp and object-oriented programming make small, incremental enhancements to existing functions exceedingly simple. This code only took a while to create because I was constantly running into problems with MacApp 2.0b5; under 2.0b9, I was able to finish this project in about a day. And, of course, using the finished code is a snap. What’s more, if you wanted to modify the behavior of auto-saving documents, you could do that extremely fast, too.

As Macintosh applications become more complex to perform more sophisticated tasks, a strict adherence to the philosophy behind the Macintosh user interface becomes ever more critical. The goal is to make using the computer as light a cognitive load on the user as possible. Using one kind of window for a wide variety of interactions can only cause the user to do more cognitive work, and is therefore inexcusable. By sticking to a clear visual expression of the different functions of document windows and dialogs, application designers will ease the load on the users, and automatically make their products more attractive. Visually distinctive, modeless dialogs are one way of accomplishing a good user interface. MacApp makes the production of such an interface very simple.

Listing 1: Changes to MDrawShapes.p
 
USES 
{ • MacApp } 
UMacApp, 
 
{ • Building Blocks } 
UPrinting, UTEView, UDialog, USavedMe, 
 
{ • Implementation Use } 
UDrawShapes; 
 

IF ValidateConfiguration(gConfiguration) THEN       { Make sure we can 
run } 
BEGIN 
{ Continue with remainder of initialization } 
InitUMacApp(10); { Init MacApp; 10 calls to MoreMasters } 
InitUTEView; 
InitUDialog; 
InitUSavedMe; 
InitUPrinting;  { Initialize the printing unit} 
 
Listing 2: Changes to DrawShapes.r
 
#if qDebug 
include “Debug.rsrc”; 
#endif 
include “MacApp.rsrc”; 
include “Printing.rsrc”; 
 
#if qTemplateViews 
#ifndef __ViewTypes__ 
#include “ViewTypes.r” 
#endif 
#endif 
 
#include “USavedMe.r” 
 
include $$Shell(“ObjApp”)”DrawShapes” ‘CODE’; 
 
 
include “Defaults.rsrc” ‘cmnu’ (mApple);      // Grab the default Apple/File 
menus Apple menu 
 
include “Defaults.rsrc” ‘cmnu’ (mFile); 
 
resource ‘cmnu’ (mFile, 
#if qNames 
“mFile”, 
#endif 
nonpurgeable) { 
mFile, 
textMenuProc, 
0x7FFFFFFD, 
enabled, 
“File”, 
{ 
“New”, noIcon, “N”,    noMark, plain, cNew; 
“Open ”, noIcon, “O”,    noMark, plain, cOpen; 
“-”, noIcon, noKey,  noMark, plain, nocommand; 
“Close”, noIcon, “W”,    noMark, plain, cClose; 
“Save”, noIcon, “S”,    noMark, plain, cSave; 
“Save As ”, noIcon, noKey, noMark, plain, cSaveAs; 
“Save a Copy In ”, noIcon, noKey, noMark, plain, cSaveCopy; 
“Revert”, noIcon, noKey,  noMark, plain, cRevert; 
“-”, noIcon, noKey,  noMark, plain, nocommand; 
“Auto-saving”, noIcon, noKey, noMark, plain, cToggleAutoSave; 
“Set auto-save time”, noIcon, noKey, noMark, plain, cSetAutoSave; 
“-”, noIcon, noKey,  noMark, plain, nocommand; 
“Page Setup ”, noIcon, noKey,  noMark, plain, cPageSetup; 
“Print One”, noIcon, noKey,  noMark, plain, cPrintOne; 
“Print ”, noIcon, “P”,    noMark, plain, cPrint; 
“-”, noIcon, noKey,  noMark, plain, nocommand; 
“Quit”, noIcon, “Q”,    noMark, plain, cQuit 
} 
}; 
 
Listing 3: Changes to UDrawShapes.p
 
UNIT UDrawShapes; 
 
INTERFACE 
 
USES 
{ • MacApp } 
UMacApp, 
 
{ • Building Blocks } 
UPrinting, 
 
UTEView, UDialog, USavedMe, 
{ • Implementation Use } 
Picker, ToolUtils, Resources, Fonts; 
 
TYPE 
 
TShapeDocument      = OBJECT (TDocument) (TAutoSaveDocument) 
 
fShapeView:         TShapeView; 
 
Listing 4: Changes to UDrawShapes.inc1.p
PROCEDURE TShapeDocument.IShapeDocument(fileType: OSType); 
 
IDocument IAutoSaveDocument(fileType, kDocType, kUsesDataFork, kUsesRsrcFork, 
NOT kDataOpen, NOT kRsrcOpen); 
 
Listing 5: DrawShapes.MAMake
 

#------------------------------------------------
#    List here the Application’s Name
AppName = DrawShapes

#------------------------------------------------
#    List any additional interfaces that your application is dependent 
on
OtherInterfaces =  
    “{SrcApp}USavedMe.p”

#------------------------------------------------
#    Name any other object files to link in
OtherLinkFiles = 
    “{ObjApp}USavedMe.p.o”

#------------------------------------------------
#    Express any additional dependencies for separate compilations.
#    Include dependencies for the MacApp and Building block interfaces
#    if you are dependent on them
“{ObjApp}USavedMe.p.o”    ƒ 
               “{SrcApp}USavedMe.inc1.p” 
               “{SrcApp}USavedMe.p” 
               {MacAppIntf} 
               {BuildingBlocksIntf}

#------------------------------------------------
Listing 6: USavedMe.p
{ Copyright © 1989 Fred J. Condo Jr. All rights reserved. }

UNIT USavedMe;
 {[f-]}
{ This unit defines the object type
TAutoSaveDocument, a MacApp® abstract type from
which you should descend your application’s
actual document object class(es).

 When a TAutoSaveDocument is in the command
chain, MacApp calls its DoIdle method, which
determines whether auto-saving is on, and, if
so, whether the user-requested interval since the
last auto-save has elapsed. If that is so, the
document is saved by sending itself a
Save message.

 When a TAutoSaveDocument is not in the command
chain, DoIdle is not called. A future version of
this unit may implement auto-saving in the
background. That would install the document as a
cohandler, which would remain installed until
such time as it saved itself, and then de-
install itself as a cohandler. Of course, if
fChangeCount =0, or if auto-saving is off, then
it would not install itself as a cohandler in
the first place, since auto-saving would never be
called for. A de-installed auto-saver would re-
install itself when brought to the front.

 The user interface to this unit is by means of a
modeless dialog.

 If you are already working on your MacApp
application, you need simply change the ancestor
of your document classes from (TDocument) to
(TAutoSaveDocument).

 Be sure to call InitUTEView, InitUDialog, and
InitUSavedMe in your main program.

 Call IAutoSaveDocument in IYourDocument.

 If your document has a DoIdle method, be sure to
call INHERITED DoIdle. Also, to keep your idle
frequency from getting clobbered, set
fRespectsIdleTime to TRUE immediately after
calling IAutoSaveDocument. If you don’t care about
your idle frequency, you can accept the default
(FALSE).

 In the USES clause of your main program, insert
“UTEView, UDialog, USavedMe,” and make the same
insertion in the USES clause of UYourProgram.p.

 The name of this unit is a pun. Sorry. I
couldn’t help it. }
{[f+]}

 INTERFACE

  USES UMacApp,UTEView,UDialog,SANE,Script,
       ToolUtils;

  CONST
   cToggleAutoSave = 5000; { Change these here and
                            in USavedMe.r }
   cSetAutoSave = 5001; { if they conflict with
                         your application. }
   kAutoSaveDialogID = 5000;
   kMinutesID = 18950; { Resource ID of STR# containing
  singular and plural of ‘minute’. }

  TYPE
   TAutoSaveDocument = OBJECT (TDocument)
    fSaving: Boolean; { Whether autosaving is on. }
    fHasEverBeenSet: Boolean; { Whether the dialog
                               has ever been summoned. }
    fRespectIdleTime: Boolean; { Default FALSE. If
        TRUE, TAutoSaveDocument won’t stomp on your idle
        frequency. This is useful if your document is a
  cohandler or otherwise needs idle processing. }
    fLastSave: Longint; { Time that doc was last autosaved. }
    fDesiredInterval: Longint; { Time in ticks set by user. }
    fAutoSaveWindow: TWindow; { AutoSave dialog window. }

                    { Init & Free }

    PROCEDURE TAutoSaveDocument.IAutoSaveDocument(
                  itsFileType, itsCreator: OSType;
              usesDataFork, usesRsrcFork: Boolean;
           keepsDataOpen, keepsRsrcOpen: Boolean);
           
    PROCEDURE TAutoSaveDocument.Free; OVERRIDE;

    { Meat & Potatoes }
    FUNCTION TAutoSaveDocument.DoIdle
    (phase: IdlePhase): Boolean; OVERRIDE;
    
    PROCEDURE TAutoSaveDocument.DoSetupMenus;
     OVERRIDE;
     
    FUNCTION TAutoSaveDocument.DoMenuCommand(
     aCmdNumber: CmdNumber): TCommand; OVERRIDE;
     
    PROCEDURE
     TAutoSaveDocument.PoseAutoSaveDialog;

    { Debugging }
    PROCEDURE TAutoSaveDocument.Fields
    (PROCEDURE DoToField(fieldName: Str255;
      fieldAddr: Ptr; fieldType: Integer));
      OVERRIDE;
    END;

   TAutoSaveDialogView = OBJECT (TDialogView)
    fAutoSaveDocument: TAutoSaveDocument; { The
     autosaver this dialog will modify. }

    { Init & Free }

    PROCEDURE TAutoSaveDialogView.Free;
    OVERRIDE;
    PROCEDURE TAutoSaveDialogView.Close;
    OVERRIDE;

    { A nice fish dinner }
    PROCEDURE TAutoSaveDialogView.DismissDialog
     (dismisser: IDType;
      flashDismisser: Boolean);
     OVERRIDE;
     
    PROCEDURE 
     TAutoSaveDialogView.UpdateDocumentName;

    PROCEDURE TAutoSaveDialogView.Fields
     (PROCEDURE DoToField(fieldName: Str255;
      fieldAddr: Ptr; fieldType: Integer));
      OVERRIDE;
    END;

   { GLOBAL PROCEDURE }

  PROCEDURE InitUSavedMe;

 IMPLEMENTATION

  {$I USavedMe.inc1.p}

END.
Listing 7: USavedMe.inc1.p
{ USavedMe.inc1.p
    Copyright © 1989 Fred J. Condo Jr.
    All rights reserved. }

CONST
 autoSaving = TRUE;

 {$S AOpen}

PROCEDURE TAutoSaveDocument.IAutoSaveDocument(
                  itsFileType, itsCreator: OSType;
              usesDataFork, usesRsrcFork: BOOLEAN;
           keepsDataOpen, keepsRsrcOpen: BOOLEAN);

 BEGIN
  {$IFC qDebug}
  IF gIntenseDebugging THEN
   WriteLn(‘TAutoSaveDocument.IAutoSaveDocument’);
  {$ENDC}
  IDocument(itsFileType,itsCreator,usesDataFork,
            usesRsrcFork,keepsDataOpen,
            keepsRsrcOpen);
  { Initial conditions are... }
  fSaving:=NOT autoSaving;
  fRespectIdleTime:=FALSE;
  fHasEverBeenSet:=FALSE; fLastSave:=0; { never
   saved }
  fDesiredInterval:=MAXLONGINT; { never want it saved. (This
  is 1 yr., 1 mo., 19 days) }
  fAutoSaveWindow:=NIL { no dialog exists }
 END;

{$S AClose}

PROCEDURE TAutoSaveDocument.Free; OVERRIDE;

 BEGIN
{ If this object is being freed and its dialog is
  still open (which can happen because it is a
  modeless dialog), then close the dialog. }

  {$IFC qDebug}
  IF gIntenseDebugging THEN
   WriteLn(‘TAutoSaveDocument.Free’);
  {$ENDC}

  IF fAutoSaveWindow<>NIL THEN
   fAutoSaveWindow.Close;
  INHERITED Free
 END;

{$S ARes}

FUNCTION TAutoSaveDocument.DoIdle(Phase: IdlePhase
                                  ): BOOLEAN;
 OVERRIDE;

 VAR
  dontCare: TCommand;
  currentTicks: LongInt;

 BEGIN
  DoIdle:=FALSE; { I don’t free myself }
  currentTicks:=TickCount;

  {$IFC qDebug}
  IF gIntenseDebugging THEN
   WriteLn(fTitle^^,’ has idled’);
  IF fSaving&(currentTicks-fLastSave>=
     fDesiredInterval) THEN
   WriteLn(‘Enough time has elapsed to save.’);
  {$ENDC}

  IF fSaving&(fChangeCount>0)&(currentTicks-
     fLastSave>=fDesiredInterval) THEN BEGIN
        Save(cSave, FALSE, FALSE);
        fLastSave := currentTicks
  END
 END;

{$S ARes}

PROCEDURE TAutoSaveDocument.DoSetupMenus; OVERRIDE
 ;

 BEGIN
  {$IFC qDebug}
  WriteLn(‘AutoSaveDocument.DoSetupMenus:’);
  {$ENDC}

  INHERITED DoSetupMenus;
  EnableCheck(cToggleAutoSave,fHasEverBeenSet, fSaving);
  Enable(cSetAutoSave,fSaveExists) { Allow auto-saving
            only after the file has a name }
 END;

{$S ARes}

FUNCTION TAutoSaveDocument.DoMenuCommand(
       aCmdNumber: CmdNumber): TCommand; OVERRIDE;

 VAR
  anAutoSaveDialogView: TAutoSaveDialogView;

 BEGIN

  {$IFC qDebug}
  IF gIntenseDebugging THEN
   WriteLn(‘TAutoSaveDocument.DoMenuCommand:’, aCmdNumber);
  {$ENDC}

  DoMenuCommand:=gNoChanges;
  CASE aCmdNumber OF
   cToggleAutoSave: BEGIN
    fSaving:=NOT fSaving;
    IF NOT fSaving&NOT fRespectIdleTime THEN
     fIdleFreq:=MAXLONGINT
   END;

   cSetAutoSave:
    IF fAutoSaveWindow=NIL { Open a dialog only if
                            one is not already open }
       THEN
     PoseAutoSaveDialog
    ELSE fAutoSaveWindow.Select;

   cSave,cSaveAs: BEGIN
 { Intercept cSave and cSaveAs commands to make
   sure that the document name given in the 
   AutoSave Dialog is still correct. If there is
   no dialog open, skip it. }
    Save(aCmdNumber,((aCmdNumber=cSaveAs)|
         (NOT fSaveExists)) { Put up SFPutFile if
                             Save As  } ,FALSE);
    IF (fAutoSaveWindow<>NIL) THEN BEGIN
     anAutoSaveDialogView:=TAutoSaveDialogView(
                            fAutoSaveWindow.FindSubView(‘DLOG’));
     FailNIL(anAutoSaveDialogView);
     anAutoSaveDialogView.UpdateDocumentName
    END
   END; { cSave }
   OTHERWISE
    DoMenuCommand:=INHERITED DoMenuCommand(aCmdNumber)
  END { Case }
 END; { DoMenuCommand }

{$S ANonRes}

PROCEDURE TAutoSaveDocument.PoseAutoSaveDialog;
{ This method creates and opens a modeless dialog }

 VAR
  theDialog: TAutoSaveDialogView;
  theTimeText: TNumberText;
  theWordMinutes: TStaticText;
  minutesStr: Str255;
  whichStr: 1..2;

 BEGIN

  {$IFC qDebug}
  IF gIntenseDebugging THEN
   WriteLn(‘TAutoSaveDocument.PoseAutoSaveDialog’)
           ;
  {$ENDC}

  fAutoSaveWindow { This field will allow the
   auto-save object to keep track of its dialog }
   :=NewTemplateWindow(kAutoSaveDialogID,NIL);
  FailNIL(fAutoSaveWindow);
  theDialog:=TAutoSaveDialogView(fAutoSaveWindow.
                                 FindSubView(‘DLOG’));
  FailNIL(theDialog);

        { Now give the TAutoSaveDialogView a
          reference to the auto-save object so
          that it can update the appropriate
          fields when it is dismissed. }
  theDialog.fAutoSaveDocument:=SELF;
  theDialog.UpdateDocumentName;

    { Now display the proper desired interval, if
      one’s been previously set, or the default;
      and determine whether to use the singular or
      plural of “minute.” }
  theTimeText:=TNumberText(theDialog.FindSubView(‘time’));
  FailNIL(theTimeText);
  IF fHasEverBeenSet THEN
   theTimeText.SetValue(fDesiredInterval DIV 3600,
                        FALSE {no redraw; not open yet} );
  IF theTimeText.GetValue=1 THEN whichStr:=1
  ELSE whichStr:=2;
  theWordMinutes:=TStaticText(theDialog.FindSubView(‘mnut’))
                   ; FailNIL(theWordMinutes);
  GetIndString(minutesStr,kMinutesID,whichStr);
  theWordMinutes.SetText(minutesStr,FALSE);
  fAutoSaveWindow.Open
 END;

{$S AFields}

PROCEDURE TAutoSaveDocument.Fields
 (PROCEDURE DoToField(fieldName: Str255;
 fieldAddr: Ptr; fieldType: Integer));
 OVERRIDE;

 BEGIN
  DoToField(‘TAutoSaveDocument’,NIL,bClass);
  DoToField(‘fSaving’,@fSaving,bBoolean);
  DoToField(‘fHasEverBeenSet’,@fHasEverBeenSet, bBoolean);
  DoToField(‘fRespectIdleTime’,@fRespectIdleTime, bBoolean);
  DoToField(‘fLastSave’,@fLastSave,bLongInt);
  DoToField(‘fDesiredInterval’,@fDesiredInterval, bLongInt);
  DoToField(‘fAutoSaveWindow’,@fAutoSaveWindow, bObject);
  INHERITED Fields(DoToField)
 END;

{$S ANonRes}

PROCEDURE TAutoSaveDialogView.DismissDialog
 (dismisser: IDType;
  flashDismisser: Boolean);
 OVERRIDE;

 BEGIN

  {$IFC qDebug}
  IF gIntenseDebugging THEN
   WriteLn(‘TAutoSaveDialogView.DismissDialog’);
  {$ENDC}

  IF NOT fDismissed AND
     DeselectCurrentEditText THEN BEGIN
   INHERITED DismissDialog(dismisser, flashDismisser);
   IF dismisser=’ok  ‘ THEN BEGIN
    fAutoSaveDocument.fSaving:=autoSaving;
    fAutoSaveDocument.fHasEverBeenSet:=TRUE;
    fAutoSaveDocument.fDesiredInterval:=60*60* {
      The interval is specified in minutes by the
      user. We want ticks. }
     TNumberText(FindSubView(‘time’)).GetValue;
    IF fAutoSaveDocument.fRespectIdleTime THEN
     BEGIN
     IF fAutoSaveDocument.fDesiredInterval<
        fAutoSaveDocument.fIdleFreq THEN
      fAutoSaveDocument.fIdleFreq:=MAX(1,
        fAutoSaveDocument.fDesiredInterval DIV 2)
        { This  way, we don’t stomp on a
          co-handler’s idle frequency. }
    END
    ELSE
     fAutoSaveDocument.fIdleFreq:=
       fAutoSaveDocument.fDesiredInterval
                                   DIV 2
   END;
   fAutoSaveDocument.fAutoSaveWindow:=NIL; { Tell
    the document there’s no dialog to close }
   TWindow(fSuperView).Close
  END
 END;

{$S ANonRes}

PROCEDURE TAutoSaveDialogView.UpdateDocumentName;

 VAR
  theDocName: TStaticText;
  theDocTitle: Str255;

 BEGIN

  {$IFC qDebug}
  IF gIntenseDebugging THEN
   WriteLn(‘TAutoSaveDialogView.UpdateDocumentName’);
  {$ENDC}

{ Get the name of the document so the dialog can
  display it for the user }
  theDocName:=TStaticText(FindSubView(‘docn’));
  FailNIL(theDocName);
  theDocTitle:=fAutoSaveDocument.fTitle^^;
  theDocName.SetText(theDocTitle,TRUE {redraw} )
 END;

{$S AClose}
PROCEDURE TAutoSaveDialogView.Free; OVERRIDE;
{ This is overridden to inform the document that
  it has no dialog available. This is done so
  that, if the user clicks the goAway of the
  dialog, the document is informed. }

 BEGIN
  {$IFC qDebug}
  IF gIntenseDebugging THEN
   WriteLn(‘TAutoSaveDialogView.Free’);
  {$ENDC}

  fAutoSaveDocument.fAutoSaveWindow:=NIL;
  INHERITED Free
 END;

{$S AClose}

PROCEDURE TAutoSaveDialogView.Close; OVERRIDE;

 BEGIN
  fDismissed:=TRUE; { Pretend we’ve dismissed with
                     the Cancel button, }
  fDismisser:=’cncl’; { but without doing any real dismissing, because 
objects may be freed. }
  INHERITED Close
 END;

{$S AFields}

PROCEDURE TAutoSaveDialogView.Fields
 (PROCEDURE DoToField(fieldName: Str255;
  fieldAddr: Ptr; fieldType: Integer));
  OVERRIDE;

 BEGIN
  DoToField(‘TAutoSaveDialogView’,NIL,bClass);
  DoToField(‘fAutoSaveDocument’,
            @fAutoSaveDocument,bObject);
  INHERITED Fields(DoToField)
 END;

{$S AInit}

PROCEDURE InitUSavedMe; { This just makes sure our dialog view classes 
don’t get stripped. }

 VAR
  g: BOOLEAN;

 BEGIN

  {$IFC qDebug}
  IF gIntenseDebugging THEN
   WriteLn(‘InitUSavedMe’);
  {$ENDC}

  IF gDeadStripSuppression THEN
   g:=Member(TObject(NIL),TAutoSaveDialogView)
 END;
Listing 8: USavedMe.r
/* USavedMe.r
  Copyright © 1989 Fred J. Condo Jr.
  All rights reserved.
  INCLUDE USavedMe.r in YourApp.r */

#define kAutoSaveDialogID 5000
#define kAutoSaveMenuID   5000
#define cToggleAutoSave   5000
#define cSetAutoSave 5001
#define kMinutesID 18950

include “dialog.rsrc”;

resource ‘STR#’ (kMinutesID, “Minutes”, purgeable) {
 { /* array StringArray: 2 elements */
 /* [1] */
 “minute.”,
 /* [2] */
 “minutes.”
 }
};

resource ‘view’ (kAutoSaveDialogID, purgeable) {
 { /* array viewArray: 10 elements */
 /* [1] */
 root, ‘root’,
 { /* array: 1 elements */
 /* [1] */
 50, 40
 },
 { /* array: 1 elements */
 /* [1] */
 226, 192
 }, sizeVariable, sizeVariable, notShown, enabled,
 Window {
 “TWindow”,
 16,
 goAwayBox,
 notResizable,
 modeless,
 ignoreFirstClick,
 freeOnClosing,
 disposeOnFree,
 doesntCloseDocument,
 dontOpenWithDocument,
 dontAdaptToScreen,
 dontStagger,
 forceOnScreen,
 dontCenter,
 ‘time’,
 “Auto-save”
 },
 /* [2] */
 ‘root’, ‘DLOG’,
 { /* array: 1 elements */
 /* [1] */
 0, 0
 },
 { /* array: 1 elements */
 /* [1] */
 224, 192
 }, sizeVariable, sizeVariable, shown, enabled,
 DialogView {
 “TAutoSaveDialogView”,
 ‘ok  ‘,
 ‘cncl’
 },
 /* [3] */
 ‘DLOG’, ‘VW06’,
 { /* array: 1 elements */
 /* [1] */
 8, 8
 },
 { /* array: 1 elements */
 /* [1] */
 64, 176
 }, sizeFixed, sizeFixed, shown, disabled,
 Cluster {
 “TCluster”,
 0b0,
 {1, 1},
 sizeable,
 notDimmed,
 notHilited,
 doesntDismiss,
 {0, 0, 0, 0},
 plain, 9,
 { /* array: 3 elements */
 /* [1] */
 0x0,
 /* [2] */
 0x0,
 /* [3] */
 0x0
 },
 “A”,
 “Document to Auto-save”
 },
 /* [4] */
 ‘VW06’, ‘docn’,
 { /* array: 1 elements */
 /* [1] */
 16, 8
 },
 { /* array: 1 elements */
 /* [1] */
 40, 160
 }, sizeFixed, sizeFixed, shown, disabled,
 StaticText {
 “TStaticText”,
 0b0,
 {1, 1},
 sizeable,
 notDimmed,
 notHilited,
 doesntDismiss,
 {0, 0, 0, 0},
 plain, 0,
 { /* array: 3 elements */
 /* [1] */
 0x0,
 /* [2] */
 0x0,
 /* [3] */
 0x0
 },
 “”,
 justLeft,
 “static text”
 },
 /* [5] */
 ‘DLOG’, ‘VW08’,
 { /* array: 1 elements */
 /* [1] */
 80, 8
 },
 { /* array: 1 elements */
 /* [1] */
 88, 176
 }, sizeFixed, sizeFixed, shown, disabled,
 Cluster {
 “TCluster”,
 0b0,
 {1, 1},
 sizeable,
 notDimmed,
 notHilited,
 doesntDismiss,
 {0, 0, 0, 0},
 plain, 9,
 { /* array: 3 elements */
 /* [1] */
 0x0,
 /* [2] */
 0x0,
 /* [3] */
 0x0
 },
 “A”,
 “Set the Auto-save interval”
 },
 /* [6] */
 ‘VW08’, ‘VW03’,
 { /* array: 1 elements */
 /* [1] */
 16, 8
 },
 { /* array: 1 elements */
 /* [1] */
 16, 80
 }, sizeFixed, sizeFixed, shown, disabled,
 StaticText {
 “TStaticText”,
 0b0,
 {1, 1},
 sizeable,
 notDimmed,
 notHilited,
 doesntDismiss,
 {0, 0, 0, 0},
 plain, 0,
 { /* array: 3 elements */
 /* [1] */
 0x0,
 /* [2] */
 0x0,
 /* [3] */
 0x0
 },
 “”,
 justLeft,
 “Save every”
 },
 /* [7] */
 ‘VW08’, ‘time’,
 { /* array: 1 elements */
 /* [1] */
 40, 60
 },
 { /* array: 1 elements */
 /* [1] */
 20, 48
 }, sizeFixed, sizeFixed, shown, enabled,
 NumberText {
 “TNumberText”,
 0b1111,
 {1, 1},
 notSizeable,
 notDimmed,
 notHilited,
 doesntDismiss,
 {2, 2, 2, 2},
 plain, 0,
 { /* array: 3 elements */
 /* [1] */
 0x0,
 /* [2] */
 0x0,
 /* [3] */
 0x0
 },
 “”,
 justLeft,
 “15”,
 5,
 0b110000000000000000000100000000,
 15, 1, 65535
 },
 /* [8] */
 ‘VW08’, ‘mnut’,
 { /* array: 1 elements */
 /* [1] */
 64, 96
 },
 { /* array: 1 elements */
 /* [1] */
 16, 72
 }, sizeFixed, sizeFixed, shown, disabled,
 StaticText {
 “TStaticText”,
 0b0,
 {1, 1},
 sizeable,
 notDimmed,
 notHilited,
 doesntDismiss,
 {0, 0, 0, 0},
 plain, 0,
 { /* array: 3 elements */
 /* [1] */
 0x0,
 /* [2] */
 0x0,
 /* [3] */
 0x0
 },
 “”,
 justLeft,
 “x”
 },
 /* [9] */
 ‘DLOG’, ‘cncl’,
 { /* array: 1 elements */
 /* [1] */
 184, 16
 },
 { /* array: 1 elements */
 /* [1] */
 24, 72
 }, sizeFixed, sizeFixed, shown, enabled,
 Button {
 “TButton”,
 0b0,
 {1, 1},
 sizeable,
 notDimmed,
 notHilited,
 dismisses,
 {0, 0, 0, 0},
 plain, 0,
 { /* array: 3 elements */
 /* [1] */
 0x0,
 /* [2] */
 0x0,
 /* [3] */
 0x0
 },
 “”,
 “Cancel”
 },
 /* [10] */
 ‘DLOG’, ‘ok  ‘,
 { /* array: 1 elements */
 /* [1] */
 182, 94
 },
 { /* array: 1 elements */
 /* [1] */
 28, 80
 }, sizeFixed, sizeFixed, shown, enabled,
 Button {
 “TButton”,
 0b1000000,
 {3, 3},
 sizeable,
 notDimmed,
 notHilited,
 dismisses,
 {4, 4, 4, 4},
 plain, 0,
 { /* array: 3 elements */
 /* [1] */
 0x0,
 /* [2] */
 0x0,
 /* [3] */
 0x0
 },
 “”,
 “OK”
 }
 }
};

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Ableton Live 10.1.1 - Record music using...
Ableton Live lets you create and record music on your Mac. Use digital instruments, pre-recorded sounds, and sampled loops to arrange, produce, and perform your music like never before. Ableton Live... Read more
BetterTouchTool 3.202 - Customize multi-...
BetterTouchTool adds many new, fully customizable gestures to the Magic Mouse, Multi-Touch MacBook trackpad, and Magic Trackpad. These gestures are customizable: Magic Mouse: Pinch in / out (zoom)... Read more
Fission 2.4.6 - Streamlined audio editor...
Fission can crop and trim audio, paste in or join files, or just rapidly split one long file into many. It's streamlined for fast editing. Plus, it works without the quality loss caused by other... Read more
Drama 1.0.27 - Prototyping, animation...
Drama's handy 3-in-1 functionality uniquely integrates design, animation and prototyping into a single familiar tool. No more frustrating switching between apps or learning new stuff. And by... Read more
Adobe Lightroom Classic CC 8.4.1 - Impor...
Adobe Lightroom Classic is available as part of Adobe Creative Cloud for as little as $9.99/month bundled with Photoshop CC as part of the photography package. Adobe Lightroom Classic CC (was Adobe... Read more
iExplorer 4.3.2 - View and transfer file...
iExplorer is an iPhone browser for Mac lets you view the files on your iOS device. By using a drag and drop interface, you can quickly copy files and folders between your Mac and your iPhone or... Read more
Adobe After Effects CC 2018 16.1.3 - Cre...
After Effects CC 2018 is available as part of Adobe Creative Cloud for $52.99/month (or $20.99/month for a single app license). The new, more connected After Effects CC 2018 can make the impossible... Read more
Adobe Audition CC 2019 12.1.4 - Professi...
Audition CC 2019 is available as part of Adobe Creative Cloud for as little as $20.99/month (or $9.99/month if you're a previous Audition customer). Adobe Audition CC 2019 empowers you to create and... Read more
Adobe Premiere Pro CC 2019 13.1.5 - Digi...
Premiere Pro CC 2019 is available as part of Adobe Creative Cloud for as little as $52.99/month. The price on display is a price for annual by-monthly plan for Adobe Premiere Pro only Adobe Premiere... Read more
Navicat Premium Essentials 12.1.25 - Pro...
Navicat Premium Essentials is a compact version of Navicat which provides basic and necessary features you will need to perform simple administration on a database. It supports the latest features... 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

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
Save $150-$250 on 10.2″ WiFi + Cellular iPads...
Verizon is offering $150-$250 discounts on Apple’s new 10.2″ WiFi + Cellular iPad with service. Buy the iPad itself and save $150. Save $250 on the purchase of an iPad along with an iPhone. The fine... Read more
Apple continues to offer 13″ 2.3GHz Dual-Core...
Apple has Certified Refurbished 2017 13″ 2.3GHz Dual-Core non-Touch Bar MacBook Pros available starting at $1019. An standard Apple one-year warranty is included with each model, outer cases are new... Read more

Jobs Board

*Apple* Mobility Pro - Best Buy (United Stat...
**719499BR** **Job Title:** Apple Mobility Pro **Job Category:** Store Associates **Location Number:** 001266-Charleston-Store **Job Description:** At Best Buy, our Read more
Best Buy *Apple* Computing Master - Best Bu...
**733266BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Sales **Location Number:** 000144-Union City-Store **Job Description:** **What does a Read more
Best Buy *Apple* Computing Master - Best Bu...
**730765BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Sales **Location Number:** 000565-St Petersburg-Store **Job Description:** **What does Read more
*Apple* Mobile Master - Best Buy (United Sta...
**725617BR** **Job Title:** Apple Mobile Master **Job Category:** Store Associates **Location Number:** 001095-Chesterfield-Store **Job Description:** **What does a Read more
Student Employment (Blue *Apple* Cafe) Spri...
Student Employment (Blue Apple Cafe) Spring 2019 Penn State University Campus/Location: Penn State Brandywine Campus City: Media, PA Date Announced: 12/20/2018 Date Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.