// Text of project GoodForm written on 5/9/95 at 1:38 PM // Beginning of text file Project Data /* ** Newton Developer Technical Support Sample Code ** ** GoodForm, a data logging example ** ** by Newton Developer Technical Support ** ** Copyright © 1993-1995 by Apple Computer, Inc. All rights reserved. ** ** You may incorporate this sample code into your applications without ** restriction. This sample code has been provided "AS IS" and the ** responsibility for its operation is 100% yours. You are not ** permitted to modify and redistribute the source as "DTS Sample Code." ** If you are going to re-distribute the source, we require that you ** make it clear in the source that the code was descended from ** Apple-provided sample code, but that you've made changes. */ // application based constants constant kAppObject := '["Datum","Data"]; constant kAppAll:= "All Data"; // soup based constants constant kSoupName := kPackageName; constant kSoupIndexes := '[]; // a default soup entry DefConst('kDefaultEntry, {text: "", labels: nil}); // GetAppParams constants constant kMaxApplicationWidth:= 250; constant kMaxApplicationHeight:= 336; // Install and RemoveScripts InstallScript := func(partFrame) begin // register support for routing GetGlobals().routing.(kAppSymbol) := partFrame.theForm.entryRoutingFrame; // Register global find support call kRegFindAppsFunc with (kAppSymbol); // register filing support AddArraySlot(soupNotify, kSoupName); AddArraySlot(soupNotify, kAppSymbol); end; RemoveScript := func(packageFrame) begin // Unregister global find support call kUnRegFindAppsFunc with (kAppSymbol); // un-register filing support local soupNotifyPos:= ArrayPos(soupNotify, kAppSymbol, 0, nil); ArrayRemoveCount(soupNotify, soupNotifyPos - 1, 2); // un-register routing support RemoveSlot(GetGlobals().routing, kAppSymbol); // End of text file Project Data // Beginning of file GoodForm.t // Before Script for "GoodFormBase" // Copyright © 1993 - 1995 Apple Computer, Inc. All rights reserved GoodFormBase := { PutAway: func(item) // this might be called while we are not running, hense the // reason to check for the existance of uSoup. begin // get the entry from the system local newEntry := item.body; local theSoup := nil; // make sure we have the correct folder // for the labels slot CheckThatFolderExists(newEntry); if uSoup then theSoup := uSoup else theSoup := :RegisterCardSoup(kSoupName, kSoupIndexes, kAppSymbol, kAppObject); theSoup:AddToDefaultStore(newEntry); // add the entry if not uSoup then :UnRegisterCardSoup(kSoupName); // clean up BroadcastSoupChange(kSoupName); // tell everyone we've changed end, dirtyFlag: nil, SetTarget: func(entry) // used by several of our methods, this function // sets the target and adjusts both the cursor // and the display appropriately begin if entry and cursor:Goto(entry) then Target := entry else Target:= NIL; :UpdateFieldsFromEntry(target); end, viewSetupDoneScript: func() begin // try to restore the old Target local testTarget:= :GetLastTargetFromPrefsID(); if testTarget and cursor:Goto(testTarget) then :SetTarget(testTarget) else begin cursor:Reset(); :SetTarget(cursor:Entry()) end; // add support for Newton Connection Kit :AddNCKSupport(); end, SaveToSoup: func() // Save Entry to Soup begin target.text := Clone(inputField.text); EntryChange(target); :SetDirtyFlag(nil); end, viewFormat: 524624, viewQuitScript: func() // Remember to unregister the soup, and zap the currently used // references here, otherwise you get into trouble when removing // the PCMCIA card with the app. begin // make sure we can restore the target if the card is removed if Target then prefsEntry:= EntryUniqueID(Target) else prefsEntry:= nil; // if the view has been changed, make sure the entry is changed if dirtyFlag then :SaveToSoup(); // nil out all of our references and un-register the card soup call kUnRegisterCardSoupFunc with (kSoupName); uSoup:= nil; cursor:= nil; Target:= nil; end, appAll: kAppAll, target: nil, folderChanged: func(soupname, oldLabel, newLabel) // basic task is to assign new values to // the 'labels slot in each check because // the user has changed the name of one // of the folders, NOTE this can be executed // when this app is not running. begin local soup := GetUnionSoup(kSoupName); if soup then begin local localCursor := query(soup, {type: 'index, validTest: func(entry) begin return entry.labels = oldLabel; end }); // change the labels of each entry in the soup to newLabel local localEntry := localCursor:Entry(); while localEntry do begin localEntry.labels := newLabel; EntryChange(localEntry); localEntry := localCursor:Next(); end; // tell everyone else we've done it BroadcastSoupChange(kSoupName); end; // redraw the folder tab if we are running if folderTab then folderTab:UpdateFilter(oldLabel, newLabel) end, viewFlags: 4, UndoDuplicateEntry: func(entry) // use the delete message and the DeleteAnEntry method // to crumple the view and remove the duplicate item begin inputField:Delete('DeleteAnEntry, [entry]); end, viewIdleScript: func() begin if dirtyFlag then :SaveToSoup(); return 5000; // we will get called every 5 seconds end, AddNCKSupport: func() begin // remove any old soup MetaData then add the latest local addEntry; local directorySoup; // addedMetaData will prevent this code from executing every launch. if not addedMetaData then directorySoup := GetStores()[0]:GetSoup("Directory"); else return; if directorySoup then begin // get a the metaData soup entry myCursor := Query(directorySoup, {type:'index, indexPath:'soup, startKey: kSoupName, endTest: func(entry) NOT(StrEqual(entry.soup, kSoupName))}); // if the metaData spec is not the latest version // for the app, remove it while myCursor:Entry() do begin if (myCursor:Entry().version < nckMetaData.version) then EntryRemoveFromSoup(myCursor:Entry()); myCursor:next(); end; // move the cursor back to the front and if needed, add a metaData entry if not myCursor:reset() then directorySoup:Add(Clone(nckMetaData)); // toggle the check in the base view, this will speed the next launch. addedMetaData := true; end; end, DeleteActionScript: func(entry, targetView) begin if Target then begin inputField:Delete('DeleteAnEntry, [entry]); // Delete does a crumple AddUndoAction('undoDeleteEntry, [entry]) end else :Notify(kNotifyAlert, EnsureInternal(kAppName), EnsureInternal("There is no Data to delete")); end, viewBounds: {top: 20, left: -2, right: 231, bottom: 280}, appObject: kAppObject, filingChanged: func() // basic task, decide if the target should be displayed // using the current filter since the user has changed // the way the entry is filed begin // make sure that the changes done to the labels slot // are flushed from the cache :SaveToSoup(); if cursor:Goto(Target) then begin // do nothing, leave display alone end else begin cursor:Reset(); :SetTarget(cursor:Entry()); // this displays the target end end, cursor: // keep track of the current cursor pointing at soup entries nil, filterChanged: func() // basic task is to redraw the item(s) based // on the new filter selected by the user begin if Target and cursor:Goto(Target) then begin // do nothing, leave display alone end else begin // it must be filtered out, so display the first // non-filtered entry if dirtyFlag then :SaveToSoup(); cursor:Reset(); :SetTarget(cursor:Entry()); // this displays the target end end, labelsFilter: nil, addedMetaData: nil, ShowFoundItem: func(entry, finder) // shows the found item begin if labelsFilter <> '_all then begin folderTab:UpdateFilter(labelsFilter, entry.labels); labelsFilter := entry.labels; end; :SetTarget(entry); // go to the one selected, present it end, viewScrollDownScript: func() begin // we use local variables in case the call to Next() // fails, and we avoid restoring the cursor local localCursor:= cursor:Clone(); local localEntry:= localCursor:Next(); if localEntry then begin if dirtyFlag then :SaveToSoup(); :SetTarget(localEntry); end else GetRoot():SysBeep() end, viewOverviewScript: func() begin :Notify(kNotifyAlert, EnsureInternal(kAppName), EnsureInternal("This application does not support an overview mode")); end, appSymbol: kAppSymbol, viewJustify: 16, nckMetaData: { // The metadata description is rich and powerful. Please refer to // the documentation for more details which are beyond the scope of // this simple example. soup: kSoupName, name: kAppName, version: 1, // this code supports only version 1.0 of NCK // but 2.0 is backward compatible defaultDefinitions: { textEntry: {label: "Text Entry:", size: 100}, }, displayInfo: [ // each entry here is a slot name in the textFrame // and the order here is the order they will appear // in NCK's DataBrowser Window. 'textEntry, ], editInfo: [ // each entry here is a slot name in the textFrame // and the order here is the order they will appear // in NCK's Detail Window. 'textEntry, ], exportInfo: [ // ??? 'textEntry, ], // The import() (from NCK to Newton) and the export() (from Newton // to NCK) functions do the actual conversion of the data. Note // the structure of the textFrame used by NCK, it's easiest to // see by looking in the export() function. The "OriginalFrame" // slot is actually storage for the soupFrame, and the other frames // store conversions of those slots in the soupFrame to a textual // format. import: func(textFrame) // converts text frame to soup entry begin if textFrame.originalFrame then textFrame.originalFrame.text:= "LIKE WOW MAN" // textFrame.textEntry else textFrame.originalFrame:= {text: "totally cool"};// textFrame.textEntry}; textFrame.originalFrame // this is the soupFrame!!! end, export: func(soupFrame) // converts soup entry to text frame for edit/display begin local textFrame; textFrame:= { originalFrame: soupFrame, textEntry: soupFrame.text}; textFrame; end, }, Find: func(what, results, scope, statusForm) begin if statusForm then statusForm:SetStatus("Searching in" && kAppName & $\u2026); // application can be closed when this call is made, so // get a local cursor to the soup local theSoup := GetUnionSoup(kSoupName); if theSoup then begin local theCursor := Query(theSoup, {type: 'text, text: what}); if theCursor:Entry() then begin local theResult := { _proto: GetRoot().soupFinder, owner: self, title: kAppName, findType: 'text, findWords: [what], cursor: theCursor, }; AddArraySlot(results,theResult); end end end, usoup: nil, viewScrollUpScript: func() begin // we use local variables in case the call to Prev() // fails, and we avoid restoring the cursor local localCursor:= cursor:Clone(); local localEntry:= localCursor:Prev(); if localEntry then begin if dirtyFlag then :SaveToSoup(); :SetTarget(localEntry); end else GetRoot():SysBeep() end, DeleteAnEntry: func(entry) // remove the entry from our soup, // adjust the target and the display // note: all of our methods are built // to handle a NIL Target begin EntryRemoveFromSoup(entry); local newTarget:= cursor:Next(); if not newTarget then newTarget:= cursor:Prev(); :SetTarget(newTarget); end, declareSelf: 'base, viewSetupFormScript: func() begin uSoup := call kRegisterCardSoupFunc with (kSoupName,kSoupIndexes,kAppSymbol,kAppObject); cursor := Query(uSoup, {type: 'index, ValidTest: func(entry) begin return labelsFilter = '_all or entry.labels = labelsFilter; end}); :SetupIdle(5000); :ResizeToFitScreen(); TargetView:= self; end, DuplicateActionScript: func(entryToDuplicate, targetView) // copy the entry into our soup and // adjust the target and display begin if entryToDuplicate then begin local newEntry:= EntryCopy(entryToDuplicate, uSoup); :SetTarget(newEntry); AddUndoAction('UndoDuplicateEntry, [newEntry]) end else GetRoot():SysBeep(); end, prefsEntry: nil, SetupRoutingSlip: func(fields) // required to support routing, this function stuffs // the slots of a frame passed to it by the system begin fields.title:= "Beamed GoodFormDatum:" && Target.text end, GetLastTargetFromPrefsID: func() // uses a soup query to get an entry using the old target's _uniqueID begin local localCursor := Query(uSoup, { type: 'index, validTest: func(entry) begin return entry._uniqueID = prefsEntry; end}); return localCursor:Entry(); end, viewClass: 74, MakeNewEntry: func() // Create a new soup entry, the labels slot is used to support filing begin if dirtyFlag then :SaveToSoup(); local newEntry := Clone(kDefaultEntry); newEntry.labels := if labelsFilter = '_all then nil else labelsFilter ; uSoup:AddToDefaultStore(newEntry); newEntry; end, UpdateFieldsFromEntry: func(entry) // Update the view field with the entry from the soup // and if the entry is nil, display a notification begin if entry then begin inputField:Show(); itemLabel:Show(); SetValue(inputField,'text, Clone(entry.text)); SetValue(infoField, 'text, "Entry No:" && entry._uniqueID) end else begin inputField:Hide(); itemLabel:Hide(); // Should really differentiate between no entries in folder // and no entries at all (i.e., labelsFilter <> '_all and // no entries at all versus labelsFilter <> '_all and // entries in another folder SetValue(infoField,'text, if labelsFilter = '_all then "There is no Data to display." else "There is no Data in this folder"); end; :SetDirtyFlag(nil); end, debug: "GoodFormBase", SetDirtyFlag: func(IsDirty) begin dirtyFlag := IsDirty; end, undoDeleteEntry: func(entry) begin uSoup:AddToDefaultStore(entry); :SetTarget(entry); end, targetView: nil, ResizeToFitScreen: func() // this function uses the global function, GetAppParams() // to get the current size of the screen and adjusts our // base view's size accordingly begin local params:= GetAppParams(); self.viewBounds := RelBounds( params.appAreaLeft, params.appAreaTop, MIN(kMaxApplicationWidth, params.appAreaWidth), MIN(kMaxApplicationHeight, params.appAreaHeight) ); end, entryRoutingFrame: { zap: { Title: "Beam Datum", routeForm: 'zapSlip, }, separator: NIL, delete: { title: "Delete", routeScript: 'DeleteActionScript, }, duplicate: { title: "Duplicate", routeScript: 'DuplicateActionScript, }, card: ROM_CardAction, } }; InfoLabel := {text: "Status:", viewBounds: {left: 5, top: 30, right: 90, bottom: 45}, debug: "InfoLabel", _proto: @218 }; AddStepForm(GoodFormBase, InfoLabel); infoField := { text: "Tap the up and down arrows to scroll in the database. Tap the \"New\" button to create a new record." , viewBounds: {left: 0, top: 0, right: 220, bottom: 55}, viewFormat: 336, viewJustify: 10368, debug: "infoField", _proto: @218 }; AddStepForm(GoodFormBase, infoField); StepDeclare(GoodFormBase, infoField, 'infoField); ItemLabel := {text: "Current Item:", viewBounds: {left: 0, top: 12, right: 85, bottom: 27}, viewJustify: 8398976 , debug: "ItemLabel", _proto: @218 }; AddStepForm(GoodFormBase, ItemLabel); StepDeclare(GoodFormBase, ItemLabel, 'ItemLabel); inputField := {viewFlags: 6657, viewFormat: 12625, viewLineSpacing: 20, viewFont: userFont18, viewBounds: {left: 0, top: 0, right: 220, bottom: 102}, viewJustify: 10368, viewChangedScript: func(slot, view) begin :SetDirtyFlag(true); end, debug: "inputField", viewClass: 81 }; AddStepForm(GoodFormBase, inputField); StepDeclare(GoodFormBase, inputField, 'inputField); _view000 := {_proto: @219}; AddStepForm(GoodFormBase, _view000); _view001 := {_proto: @209}; AddStepForm(_view000, _view001); _view002 := {viewBounds: {left: -23, top: 2, right: -6, bottom: 15}, _proto: @176}; AddStepForm(_view000, _view002); New := {text: "New", buttonClickScript: func() begin :SetTarget(:MakeNewEntry()); end, viewBounds: {left: 28, top: 2, right: 54, bottom: 15}, viewJustify: 8388614, debug: "New", _proto: @226 }; AddStepForm(_view000, New); // After Script for "New" thisView := New; // set the bounds of the button so that it is to the right // of the clock and the correct height for the status bar thisView.viewBounds := ButtonBounds(-(thisView.viewBounds.right - thisView.viewBounds.left)); folderTab := {viewFlags: 33, debug: "folderTab", _proto: @211}; AddStepForm(GoodFormBase, folderTab); StepDeclare(GoodFormBase, folderTab, 'folderTab); constant |layout_GoodForm.t| := GoodFormBase; // End of file GoodForm.t