// Copyright © 1993,4 Apple Computer, Inc. All rights reserved // application constants constant kAppSymbol := '|SoupTDS:PIEDTS| ; constant kPackageName := "soupTDS:PIEDTS" ; constant kAppName := "MetaData"; // soup constants constant kAppSoupName := kPackageName ; constant kSoupIndices := '[] ; constant kAppObject := '["Thing that doesn't suck", "Things that don't suck"]; // metadata constants constant kMetaDataVersion := 2 ; // local revision number of our metadata spec // a date format to use DefConst('kAppDateFormat, GetDateStringSpec([[kElementHour, kFOrmatNumeric], [kElementMinute, kFormatNumeric], [kElementAMPM, kFormatAbbr]])); // ---- End Project Data ---- // ---- File Sync Another One.t ---- // Before Script for "MetaDataApp" /* ** Newton Developer Technical Support Sample Code ** ** Sync Another One ** ** Newton Developer Technical Support ** ** Copyright © 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. */ MetaDataApp := { viewSetupDoneScript: func() begin tdsSoup := call kRegisterCardSoupFunc with (kAppSoupName, kSoupIndices, kAppSymbol, kAppObject); // tdsCursor := Query(tdsSoup, {type: 'index, indexPath: 'aString}); tdsCursor := Query(tdsSoup, {type: 'index}); if NOT tdsCursor:Entry() then :FillSoup(); :InitMetaData(); theSoupFrame := tdsCursor:Reset(); end, viewQuitScript: func() begin // unregister card soups call kUnregisterCardSoupFunc with (kAppSoupName) ; // cleanup soup and cursor references tdsCursor := nil ; tdsSoup := nil ; // cleanup other extraneous references theSoupFrame := nil ; theTextFrame := nil ; end, theTextFrame: nil, tdsSoup: nil // holds the soup at runtime, TestImport: func() begin if theTextFrame then begin theSoupFrame := MetaData:import(theTextFrame); print(theSoupFrame); end else :Notify(kNotifyAlert, EnsureInternal(kAppName), EnsureInternal("You must first test exporting by pressing the 'Test Export Script' button")); end, viewBounds: {left: 0, top: 0, right: 240, bottom: 150}, appObject: kAppObject, _proto: protoApp, FillSoup: func() begin local i; print("Filling Soup"); for i := 1 to 20 do tdsSoup:AddToDefaultStore({ aString: GetRandomWord(5,20) & $- & GetRandomWord(5,20), anInteger: Random(0,10000), aReal: Random(0, 10000)/100, aSymbol: MySymbolArray[Random(0, length(MySymbolArray)-1)], aDate: Time(), aTime: Time(), aDateAndTime: Time(), aCharacter: CHR(Random(ORD($A),ORD($Z))), aBoolean: if Random(0, 1) = 1 then TRUE else NIL, aFolder: if Random(0, 2) = 0 then nil /* Unfiled */ else (foreach folder, name in userConfiguration.userFolders collect folder)[ Random(0, length(userConfiguration.userFolders)-1)], aFrame: { aStringSlot: GetRandomWord(1,5), anIntegerSlot: Random(0, 100), }, anArray: [Random(0, 9), Random(10,19), Random(20,29)], }); if NOT tdsCursor then tdsCursor := Query(tdsSoup, {type: 'index}); theSoupFrame := tdsCursor:Reset(); end, DestroySoup: func() begin local store, myCursor, mySoup, directorySoup; // unregister soups with system call kUnRegisterCardSoupFunc with (kAppSoupName); // remove the soup from each store currently installed foreach store in GetStores() do if mySoup := store:GetSoup(kAppSoupName) then mySoup:RemoveFromStore(); // remove the soup MetaData directorySoup := GetStores()[0]:GetSoup("Directory"); if directorySoup then begin myCursor := Query(directorySoup, {type:'index, indexPath:'soup, startKey: kAppSoupName, endTest: func(entry) NOT(StrEqual(entry.soup, kAppSoupName))}); while myCursor:Entry() do begin EntryRemoveFromSoup(myCursor:Entry()); myCursor:Next(); end; end; // nil out the references to the soup and cursors tdsSoup := nil ; tdsCursor := nil ; theSoupFrame := nil; end, addedMetaData: nil, theSoupFrame: nil, appSymbol: kAppSymbol, title: "Sync Another One", MetaData: { // name of soup within Newton soup: kAppSoupName, // alternate name to show the user name: "Meta Data Sample Soup", // version number, default 1 version: kMetaDataVersion, // default frame for a new entry newEntry: { aString: "default", anInteger: 0, aReal: 0.0, aSymbol: nil, aDate: nil, // check out the export script (heh) aTime: nil, // check out the export script (heh) aDateAndTime: nil, // check out the export script (heh) aCharacter: $?, aBoolean: NIL, aFolder: NIL, aFrame: { aStringSlot: "default", anIntegerSlot: 0, }, anArray: Array(3, 0), }, // displayInfo/editInfo/exportInfo entries can have the folowing slots: // slot: symbol that refers to slot in soup entry being described // type: type of editor for slot, default 'string // 'string, 'char, 'text, 'integer, 'picklist, 'folder, 'e_line // 'date, 'time, 'real, 'phone (last 4 not fully implemented) // choices: in editInfo, array of strings for popups ('picklist) // size: number of '0' characters that should fit, default 15 // hidden: if true, column will initially be hidden // label: string for label on Mac/PC, default is slot name // sameLine: for editInfo only, if true will put on same line as previous // unsearchable: for displayInfo, if true will not allow searches on this field // path: specifies path to data for search comparisons, defaults to slot // compareScript: symbol for which compare script to use. // These are reserved for future use, not implemented now // justify: 'right, 'left, 'center, or 'system // maxChars: upper limit for input in editInfo // minChars: lower limit for input in editInfo // maxValue: max for numeric fields in editInfo // minValue: min for numeric fields in editInfo // validChar: script (in meta data) that will be called for // each character in editInfo to validate it. // validField: script (in meta data) that will be called when // the user finishes the field. // numLines: specifies the number of lines for editInfo // used in overview list on Mac/PC, order in array controls Mac/PC displayInfo: ['aString, 'anInteger, 'aReal, 'aSymbol, 'aDate, 'aTime, 'aDateAndTime, 'aCharacter, 'aBoolean, 'aFolder,], // 'aFrame1, 'aFrame2, 'anArray1, 'anArray2, 'anArray3], // used in item list on Mac/PC editInfo: ['aString, 'anInteger, 'aReal, 'aSymbol, 'e_line, 'aDate, 'aTime, 'aDateAndTime, 'e_line, 'aCharacter, 'aBoolean, 'aFolder, 'e_line, 'aFrame1, 'aFrame2, 'anArray1, 'anArray2, 'anArray3], // used for import/export on Mac/PC exportInfo: ['aString, 'anInteger, 'aReal, 'aSymbol, 'aDate, 'aTime, 'aDateAndTime, 'aCharacter, 'aBoolean, 'aFolder, 'aFrame1, 'aFrame2, 'anArray1, 'anArray2, 'anArray3], // specifies defaults in displayInfo, editInfo, exportInfo defaultDefinitions: { aString: {slot: 'aString, size: 41, }, anInteger: {slot: 'anInteger, type: 'integer, }, aReal: {slot: 'aReal, type: 'real, }, aSymbol: {slot: 'aSymbol, type: 'mySymbolType, size: 12, }, aDate: {slot: 'aDate, type: 'date, }, aTime: {slot: 'aTime, type: 'time, sameline: TRUE, }, aDateAndTime: {slot: 'aDateAndTime, type: 'myDateAndTime, }, aCharacter: {slot: 'aCharacter, type: 'char, }, aBoolean: {slot: 'aBoolean, type: 'picklist, label: "Boolean", choices: ["TRUE", "FALSE"], }, aFolder: {slot: 'aFolder, type: 'folder, }, aFrame1: {slot: 'aFrame.aStringSlot, hidden: TRUE, size: 11, }, aFrame2: {slot: 'aFrame.anIntegerSlot, type: 'integer, hidden: TRUE, }, anArray1: {slot: [pathExpr: 'anArray, 0], type: 'integer, hidden: TRUE, }, anArray2: {slot: [pathExpr: 'anArray, 1], type: 'integer, hidden: TRUE, }, anArray3: {slot: [pathExpr: 'anArray, 2], type: 'integer, hidden: TRUE, }, }, // used to lookup values of type slots // must be one of: // 'string, 'char, 'text, 'integer, 'picklist, 'folder, 'e_line // 'date, 'time, 'real, 'phone (last 4 not fully implemented) editTypes: { mySymbolType: {type: 'picklist, label: "Symbol", choices: ["first", "second", "third", ], }, }, // text frames have same slots as soup frames, but value // for each slot is a string. The textFrame should also have a slot called // originalFrame, which should point to the soup frame (just a convention) // Note that the text frame passed to the import script will NOT be the same // frame returned from the export script. (Any slots not mentioned in editInfo // won't appear in the frame passed to the import script.) export: func(theSoupFrame) begin local theTextFrame := { // originalFrame: theSoupFrame, aString: theSoupFrame.aString, anInteger: SPrintObject(theSoupFrame.anInteger), aReal: SPrintObject(theSoupFrame.aReal), aSymbol: :MySymbolToString(theSoupFrame.aSymbol), aDate: ShortDateStr(if NOT theSoupFrame.aDate then Time() else theSoupFrame.aDate, kIncludeAllElements), aTime: TimeStr(if NOT theSoupFrame.aTime then Time() else theSoupFrame.aTime,kAppDateFormat), aDateAndTime: ShortDateStr(if NOT theSoupFrame.aDate then Time() else theSoupFrame.aDate, kIncludeAllElements) && TimeStr(if NOT theSoupFrame.aTime then Time() else theSoupFrame.aTime, kAppDateFormat), aCharacter: SPrintObject(theSoupFrame.aCharacter), aBoolean: defaultDefinitions.aBoolean.choices[if theSoupFrame.aBoolean then 0 else 1], aFolder: if theSoupFrame.aFolder then SPrintObject(theSoupFrame.aFolder), aFrame1: theSoupFrame.aFrame.aStringSlot, aFrame2: SPrintObject(theSoupFrame.aFrame.anIntegerSlot), anArray1: SPrintObject(theSoupFrame.anArray[0]), anArray2: SPrintObject(theSoupFrame.anArray[1]), anArray3: SPrintObject(theSoupFrame.anArray[2]), }; return theTextFrame end, // convert a text frame to a soup entry frame import: func(theTextFrame) begin local n := StringToNumber(theTextFrame.anInteger); local m := StringToNumber(theTextFrame.aFrame2); local o := [ StringToNumber(theTextFrame.anArray1), StringToNumber(theTextFrame.anArray2), StringToNumber(theTextFrame.anArray3), ]; local theSoupFrame := { aString: theTextFrame.aString, anInteger: if n = abs(n) then floor(n) else ceiling(n), aReal: StringToNumber(theTextFrame.aReal), aSymbol: :StringToMySymbol(theTextFrame.aSymbol), aDate: StringToDate(theTextFrame.aDate), aTime: :GetTime(theTextFrame.atime), aDateAndTime: StringToDate(theTextFrame.aDateAndTime), aCharacter: if length(theTextFrame.aCharacter) > 0 then theTextFrame.aCharacter[0] else $ , aBoolean: StrCompare(theTextFrame.aBoolean, defaultDefinitions.aBoolean.choices[0]) = 0, aFolder: if theTextFrame.aFolder then :GetSymbol(theTextFrame.aFolder), aFrame: { aStringSlot: theTextFrame.aFrame1, anIntegerSlot: if m = abs(m) then floor(m) else ceiling(m), }, anArray: [ if o[0] = abs(o[0]) then floor(o[0]) else ceiling(o[0]), if o[1] = abs(o[1]) then floor(o[1]) else ceiling(o[1]), if o[2] = abs(o[2]) then floor(o[2]) else ceiling(o[2]), ], }; return theSoupFrame; end, // these supporting functions are used by import/export to // convert strings to symbols and vice-versa. They're accessible // because the context for the import and export scripts is a copy // of the meta data frame itself. MySymbolToString: func(theSymbol) editTypes.mySymbolType.choices[ if NOT theSymbol or theSymbol = 'firstSymbol then 0 else if theSymbol = 'secondSymbol then 1 else if theSymbol = 'thirdSymbol then 2 ], StringToMySymbol: func(theString) if StrCompare(theString, editTypes.mySymbolType.choices[0]) = 0 then 'firstSymbol else if StrCompare(theString, editTypes.mySymbolType.choices[1]) = 0 then 'secondSymbol else if StrCompare(theString, editTypes.mySymbolType.choices[2]) = 0 then 'thirdSymbol else 'firstSymbol, // need to put a wrapper around the functions that could make a symbol // 1.0 uses MakeSymbol, 2.0 uses Intern GetSymbol: func(string) if functions.Intern then Intern(string) else MakeSymbol(String), // yet another function to account for differences between 1.0 and 2.0 // in 1.0, StringToTime is broken, so you have to use StringToDate, // but this will add todays date to the time. GetTime: func(string) if ClassOf(StringToTime("11:00 am")) = 'frame then StringToDate(string) else StringToTime(string), // used to write custom comparisons for fields. // Slots are references by compareScript slots in displayInfo array. // description contains: // slotname: path to data within the current frame // datatype: value of 'type slot in frame // searchtype: search requested by user (0-9) // searchtext: what the user typed in // searchvalue: use to store converted values from searchtext RealCompare: func(entry, description) begin TRUE; end, compareScripts: { aRealCompare: func (entry, description) :RealCompare(entry, description), aSymbolCompare: func (entry, description) TRUE, }, }, tdsCursor: nil, TestExport: func() begin if theSoupFrame then begin theTextFrame := MetaData:export(theSoupFrame); print(theTextFrame); end else :Notify(kNotifyAlert, EnsureInternal(kAppName), EnsureInternal("There is no soup, create one by pressing the 'Rebuild the Soup!' button")); end, MySymbolArray: [ 'firstSymbol, 'secondSymbol, 'thirdSymbol, ], TestNewEntry: func() begin if tdsSoup then begin theSoupFrame := DeepClone(MetaData.newEntry); tdsSoup:Add(theSoupFrame); theTextFrame := MetaData:export(theSoupFrame); print(theTextFrame); end else :Notify(kNotifyAlert, EnsureInternal(kAppName), EnsureInternal("There is no soup to add to. Rebuild using the 'Rebuild the Soup!' button")); end, debug: "MetaDataApp", InitMetaData: func() begin // remove any old soup MetaData then add the latest local addEntry := nil ; 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: kAppSoupName, endTest: func(entry) NOT(StrEqual(entry.soup, kAppSoupName))}); // if the metaData spec is not the latest version // for the app, remove it while myCursor:Entry() do begin if (myCursor:Entry().version < kMetaDataVersion) 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(MetaData)); // toggle the check in the base view, this will speed the next launch. addedMetaData := true; end; end }; testExportButton := /* child of MetaDataApp */ {text: "Test Export Script", buttonClickScript: func() begin :Parent():TestExport(); end, viewBounds: {left: 28, top: 20, right: 212, bottom: 36}, _proto: protoTextButton, debug: "testExportButton" }; testImportButton := /* child of MetaDataApp */ {text: "Test Import Script", buttonClickScript: func() begin :Parent():TestImport(); end, viewBounds: {left: 28, top: 44, right: 212, bottom: 60}, _proto: protoTextButton, debug: "testImportButton" }; testNewButton := /* child of MetaDataApp */ {text: "Test New Entry", buttonClickScript: func() begin :Parent():TestNewEntry(); end, viewBounds: {left: 28, top: 68, right: 212, bottom: 84}, _proto: protoTextButton, debug: "testNewButton" }; rebuildSoup := /* child of MetaDataApp */ {text: "Rebuild the Soup!", buttonClickScript: func() begin foreach store in GetStores() do begin if soup := store:GetSoup(kAppSoupName) then soup:RemoveAllEntries(); end; if not tdsSoup then tdsSoup := call kRegisterCardSoupFunc with (kAppSoupName, kSoupIndices, kAppSymbol, kAppObject); :FillSoup(); end, viewBounds: {left: 28, top: 92, right: 210, bottom: 106}, _proto: protoTextButton, debug: "rebuildSoup" }; zapSoup := /* child of MetaDataApp */ {text: "Delete the Soup!", buttonClickScript: func() begin :DestroySoup(); end, viewBounds: {left: 28, top: 116, right: 210, bottom: 130}, _proto: protoTextButton, debug: "zapSoup" }; // ---- Beginning of section for non used Layout files ---- // End of output