// Text of project MinMail written on 7/8/97 at 4:38 PM // Beginning of text file Constants /* ** Newton Developer Technical Support Sample Code ** ** MinMail, a Newton 2.0 transport example ** ** by J. Christopher Bell, Newton Developer Technical Support ** ** Copyright © 1994-7 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. */ // This sample uses WeRequireARecipient to determine if it is required to select at least // one person from the "to:" picker. For final code, you probably will want to require a recipient. constant WeRequireARecipient := nil; constant kNeedsRecipientStr := "You must choose a recipient."; // This is for our "temp soup" where items go after they are "sent" but before they are "received". constant kSoupName := "InTransit:MinMail:DTS"; constant kVisibleSoupName := "MinMail Items"; constant kSoupDescription := "Temporary items 'in transit' in the MinMail transport."; // Because we use a "temp soup" to hold items after they are "sent" // but before they are "received", we need a soup def to describe our soup. DefConst('kMySoupDef, { name: kSoupName, userName: kVisibleSoupName, ownerApp: kAppSymbol, ownerAppName: kAppName, userDescr: kSoupDescription, indexes: [], // we don't care about index order; we'll just use the default index to get items }); // Create our viewDef symbol. For example, TextViewDef:MinMail:DTS DefConst('kSimpleTextViewDefSym, intern("TextViewDef:"&kAppSymbol)); constant kTextVersionNumber := 1; constant kMaxTextHeight := 20000; // End of text file Constants // Beginning of file TextViewDef.lyt SimpleViewer := {viewBounds: {left: 0, top: 0, right: 0, bottom: 0}, viewFlags: 1, viewFormat: 0, viewJustify: 240, symbol: kSimpleTextViewDefSym, name: "Plain Text", type: 'editor, viewSetupFormScript: func() begin self.viewFont := GetUserConfig(mailFontSym); self.viewLineSpacing := FontHeight(viewFont); target._tempChildren := [{ viewStationery: 'para, text: target.(textPath), viewFont: viewFont, viewLineSpacing: viewLineSpacing, viewBounds: TextBounds(target.(textPath), viewFont, SetBounds(0,0,:localBox().right,0)) }]; end, version: kTextVersionNumber, textPath: 'text, mailFontSym: 'mailFont // the symbol in the userconfiguration frame representing the font choice , debug: "SimpleViewer", viewClass: 74 }; editor := {viewBounds: {left: 0, top: 0, right: 0, bottom: 0}, viewJustify: 240, GrabText: func() begin target.(textPath) := notesText(target.(path)); end, path: '_tempChildren, optionFlags: kHasScrollersOption, viewSetupFormScript: func() begin // Set the maximum height for the scrollable area. The newtEditView.noteSize // slot is not documented in the NPG, but will be mentioned in a future Q&A. noteSize := [:localBox().right, kMaxTextHeight]; inherited:?viewSetupFormScript(); self.viewFont := GetUserConfig(mailFontSym); self.viewLineSpacing := FontHeight(viewFont); // do NOT display horizontal lines if read-only. This happens in the In Box. if not :entryCool(nil) then viewFormat := vfFillWhite; end, viewAddChildScript: func(newOne) begin local result := inherited:?AddChildScript(newOne); newOne.viewFont := viewFont; newOne.viewLineSpacing := FontHeight(viewFont); :GrabText(); result; end, viewChangedScript: func(slot, view) begin local result := inherited:?viewChangedScript(slot,view); :GrabText(); result; end, viewFlags: 6657, viewDropChildScript: func(oldOne) begin local result := inherited:?viewDropChildScript(oldOne); AddDeferredSend(self, 'GrabText, '[]); result; end, viewQuitScript: func() begin local result := inherited:?viewQuitScript(); RemoveSlot(target, '_tempChildren); return result; end, debug: "editor", _proto: @413 }; AddStepForm(SimpleViewer, editor); constant |layout_TextViewDef.lyt| := SimpleViewer; // End of file TextViewDef.lyt // Beginning of file MailSlip.lyt // Before Script for "MinMailSlip" /* ** Newton Developer Technical Support Sample Code ** ** MinMail, a Newton 2.x transport example ** ** by J. Christopher Bell, Newton Developer Technical Support ** ** Copyright © 1994-7 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. */ MinMailSlip := {viewBounds: {left: 8, top: 24, right: 232, bottom: 288}, PrepareToSend: func(when) begin local item := fields; if WeRequireARecipient and (not toLine.selected or length(toLine.selected) = 0) then :Notify(kNotifyAlert, kAppName, kNeedsRecipientStr); // extract information from your 'recipient pickers' and add them to the item item.toRef := toLine.selected; item.cc := ccLine.selected; if NOT TargetIsCursor(item.body) then call kItemToTextFunc with (item, target, kAppSymbol, true, nil); inherited:?PrepareToSend(when); // inherited method submits item via :ContinueSend... end;, OwnerInfoChanged: // OwnerInfoChanged is called when the user changes the sender // (the picker in the upper-left hand corner of the route slip) func() begin local item := fields; // get sender info ("who am I") local persona := GetUserConfig('currentPersona); // find the data definition for our addressing class (for instance, '|nameRef.email|) local dataDef := GetDataDefs(transport.addressingClass); // store the sender info in the item's fromRef slot if dataDef then item.fromRef := dataDef:MakeNameRef(persona, transport.addressingClass); item; end, debug: "MinMailSlip", _proto: @655 }; toLine := {viewBounds: {left: 50, top: 45, right: -10, bottom: 60}, text: "To:", OtherText: "Other Names", selected: nil, alternatives: nil, single: nil, class: '|nameRef.email|, _picker: protoPeoplePopup, viewJustify: 8388656, debug: "toLine", _proto: @259 }; AddStepForm(MinMailSlip, toLine); StepDeclare(MinMailSlip, toLine, 'toLine); ccLine := {viewBounds: {left: 50, top: 60, right: -10, bottom: 75}, text: "Cc:", OtherText: "Other Names", selected: nil, alternatives: nil, single: nil, class: '|nameRef.email|, _picker: protoPeoplePopup, viewJustify: 8388656, debug: "ccLine", _proto: @259 }; AddStepForm(MinMailSlip, ccLine); StepDeclare(MinMailSlip, ccLine, 'ccLine); ShowMessage := { buttonClickScript: func() begin local item := fields; // For consistent terminology (we usually call this the "item"). if not TargetIsCursor(item.body) then begin // CONVERT the item to text (but only if it is not a multiple item target) call kItemToTextFunc with (item, target, kAppSymbol, true, nil); // •• Most REAL transports would OPEN a text editor here, viewing item.body.text //BuildContext(kTheTextEditorFloaterThatIHaveNotWrittenForThisSample); :Notify(kNotifyQAlert, kAppSymbol, "If this were a real transport, this button would open up a text editor."); end; else :Notify(kNotifyQAlert, kAppSymbol, "You are sending multiple items. You can view and edit the items in the outbox."); end, text: "Show Message", viewBounds: {left: 10, top: 170, right: 100, bottom: 182}, viewSetupFormScript: func() begin // We must ask the slip where to put us. We use BottomOfSlip because it is the safe // way to position items in the "bottom half" of the route slip. Note that this // number changes based on some dynamic things, like whether a format picker is // currently visible. (it is not visible if there is only one format choice) local bottom := :BottomOfSlip(); viewBounds := RelBounds(10, bottom - 18, 80, 15); inherited:?viewSetupFormScript(); end, debug: "ShowMessage", _proto: @226 }; AddStepForm(MinMailSlip, ShowMessage); constant |layout_MailSlip.lyt| := MinMailSlip; // End of file MailSlip.lyt // Beginning of file Preferences.lyt // Before Script for "transportPrefsBase" /* ** Newton Developer Technical Support Sample Code ** ** ArchiveTransport, a Newton transport example ** ** by Ryan Robertson and J. Christopher Bell, Newton Developer Technical Support ** ** Copyright © 1994-7 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. */ transportPrefsBase := {appSymbol: kAppSymbol, title: kAppName, viewSetupFormScript: func() begin // Find out whether this unit supports the 'dontAutoPutaway // preference. Both the 'dontAutoPutaway preference and // the DeleteRemoteItems method were added in the same Newton // OS rev, so we can just check to see whether DeleteRemoteItems // exists in protoTransport. For more information about // using the dontAutoPutaway preference, check out the // Newton 2.x Q&A titled "Adding an Auto Putaway Transport Preference". local doesAutoputaway := protoTransport:DeleteRemoteItems EXISTS; // If the device we are running on does not support // the 'dontAutoPutaway preference, then size the slip // so that the checkbox does not show. if doesAutoPutaway then self.viewBounds := SetBounds( 0, 0, 215, 170 ); else self.viewBounds := SetBounds( 0, 0, 215, 155 ); // Be sure to call the inherited method. inherited:?ViewSetupFormScript(); end, viewBounds: {left: 0, top: 0, right: 215, bottom: 155}, viewSetupChildrenScript: func() begin // Find out whether this unit supports the 'dontAutoPutaway // preference. Both the 'dontAutoPutaway preference and // the DeleteRemoteItems method were added in the same Newton // OS rev, so we can just check to see whether DeleteRemoteItems // exists in protoTransport. For more information about // using the dontAutoPutaway preference, check out the // Newton 2.x Q&A titled "Adding an Auto Putaway Transport Preference". local doesAutoputaway := protoTransport:DeleteRemoteItems EXISTS; // If the device we are running on does not support // the 'dontAutoPutaway preference, then hide the // checkbox. if NOT doesAutoPutaway then dontPutawayCheckbox.viewFlags := BXOR( dontPutawayCheckbox.viewFlags, vVisible ); // Let the floater do its thing inherited:?viewSetupChildrenScript(); end, debug: "transportPrefsBase", _proto: @678 }; dontPutawayCheckbox := {text: "Put away items automatically", viewBounds: {left: 10, top: -18, right: -10, bottom: -3}, viewJustify: 16436, viewFont: ROM_fontSystem10, valueChanged: func() begin // Setup the value based on what the user choose. DontAutoPutaway should be // nil if the items are to be putaway automatically. It should be the // symbol 'never if items are not to be put away automatically. if viewValue then transport:SetConfig( 'dontAutoPutaway, nil ); else transport:SetConfig( 'dontAutoPutaway, 'never ); end, viewSetupFormScript: // Setup the default value of the checkbox. See the // valueChanged method for more information // on what values the 'dontAutoputaway preference are. func() begin // Setup the default value if transport:GetConfig( 'dontAutoPutaway ) = 'never then self.viewValue := nil; else self.viewValue := true; // Be sure to call the inherited method. inherited:?ViewSetupFormScript(); end, debug: "dontPutawayCheckbox", _proto: @164 }; AddStepForm(transportPrefsBase, dontPutawayCheckbox); StepDeclare(transportPrefsBase, dontPutawayCheckbox, 'dontPutawayCheckbox); constant |layout_Preferences.lyt| := transportPrefsBase; // End of file Preferences.lyt // Beginning of file MinMailTransport.lyt // Before Script for "MinMailTransport" /* ** Newton Developer Technical Support Sample Code ** ** MinMail, a Newton 2.x transport example ** ** by J. Christopher Bell, Newton Developer Technical Support ** ** Copyright © 1994-7 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. */ MinMailTransport := {actionTitle: "Mail", appSymbol: kAppSymbol, CancelRequest: func(why) begin // Most REAL transports would set a flag here and/or notify their protoBasicEndpoint that the user wants to cancel. end, dataTypes: [ 'text ]; // We only support sending text, not 'frame (like Beam) or 'view (like Print and Fax) , icon: GetPictAsBits("MinMailIcon", nil), ReceiveRequest: func(request) begin local item, newItem; // Most REAL transports process only one item at a time. // Most REAL transports will also use communication endpoints to receive the items one at a time. // Most REAL transports will use protoStatusTemplate to show the status of "receiving". // Most REAL transports recieve each item asynchronously (letting the user continue working) // THIS sample mail transport just moves ALL items to a temp soup, waiting to be "received". // During "receiving", we'll just submit all the entries in our temp soup into the In Box. // Note that you should *NOT* submit an item into the In Box unless it was transferred successfully. // (unless there's NO way to try again later. For instance, the item must always be deleted from a server) // See the "ArchiveTransport" sample (version 2 or higher) for a more realistic example of how // most transports will handle endpoints and status dialogs. // get all items (note: a nil querySpec means "in the default order") local ourQuery := GetUnionSoup(kSoupName):Query(nil); while (item := ourQuery:Entry()) do begin // We must use the NewFromItem method to process items that used to be outbox frames. // It removes a few magic slots, and correctly manipulates the slots appSymbol, // destAppSymbol, and fromAppSymbol. See the docs for more details. newItem := :NewFromItem(item); try // tell the I/O Box to add the item; it is no longer ours! :ItemCompleted(newItem, 'received, nil); // Exceptions might occur. For instance, the store may be full! // If so, try again with the next item (maybe a smaller item will fit, etc) onexception |evt.ex| do continue; // We submitted the item to the I/O Box. We can remove our "temp" copy EntryRemoveFromSoup(item); // get the next item ourQuery:Next(); end; end, routingSlip: GetLayout("MailSlip.lyt");, SendRequest: func(request) begin local item; local connectNow; // Most REAL transports process only one item at a time, using :ItemRequest() to get the next item. // Most REAL transports will also use communication endpoints to send the items one at a time. // Most REAL transports will use protoStatusTemplate to show the status of "sending". // Most REAL transports send each item asynchronously (letting the user continue working) // THIS sample mail transport just moves ALL items to a temp soup, waiting to be "received". // See the "ArchiveTransport" sample (version 2 or higher) for a more realistic example of how // most transports will handle endpoints and status dialogs. // If the user said 'Send Later', then do NOT send the items now. Leave them in the I/O Box. connectNow := request.cause <> 'submit; if not connectNow then begin while item := :ItemRequest(request) do begin // See if any item carries the recommendation to connect immediately if possible if item.connect then connectNow := true; // CONVERT the item to text, even if the item is not going to be sent right now call kItemToTextFunc with (item, item.body, kAppSymbol, true, nil); end; if connectNow then :CheckOutBox(); // after returning from this function, call SendRequest again. // When called again, we'll connect immediately with cause='user return; end; item := :ItemRequest(request); while (item) do begin // CONVERT the item to text before sending the item. call kItemToTextFunc with (item, item.body, kAppSymbol, true, nil); // Determine what store our owner (usually the I/O Box application) uses for items. local whichStore := :GetDefaultOwnerStore(); local whichSoup := GetUnionSoupAlways(kSoupName):GetMember(whichStore); try whichSoup:AddXmit(totalclone(item), nil); // "SEND" the item...(move it to temp soup) onexception |evt.ex| do begin local err := CurrentException().error; // report the error. Note: error 0 = "no error" :ItemCompleted(item, 'error, err); continue; // try to send the next item, if possible. end; :ItemCompleted(item, 'sent, nil); // tell the I/O Box to remove (or log) the item now! item := :ItemRequest(request); // get the NEXT item end; end, title: kAppName, viewBounds: {left: 10, top: 10, right: 250, bottom: 250}, preferencesForm: GetLayout("Preferences.lyt"), VerifyRoutingInfo: // This method is called multiple times when multiple items are selected from an overview. func(item, default, target, format) begin // Export the item to text call kItemToTextFunc with (item, target, kAppSymbol, true, nil); return nil; // if error, return true from VerifyRoutingInfo end;, NewItem: func(context) begin // first call inherited transport method to get default new item local item := inherited:NewItem(context); // get sender info ("who am I") local persona:= GetUserConfig('currentPersona); // find the data definition for our addressing class (for instance, '|nameRef.email|) local dataDef := GetDataDefs(addressingClass); // store the sender info in the item's fromRef slot if dataDef then item.fromRef := dataDef:MakeNameRef(persona, addressingClass); // add other slots info here, if necessary item; end;, addressingClass: '|nameRef.email|, group: 'mail, groupTitle: "Mail", groupIcon: ROM_RouteMailIcon, defaultConfiguration: { autoStatus: true, // always show dialogs outboxLogging: nil, // what to do wth sent items inboxFiling: nil, // where to file read items outboxFiling: nil, // where to file sent items nowOrLater: nil, // when to send the item dontAutoPutAway: 'never, // don't autoputaway received items }, debug: "MinMailTransport", _proto: @389 }; // After Script for "MinMailTransport" thisView := MinMailTransport; // reference this layout in the part frame. See this part's installScript to see how this is used. SetPartFrameSlot(kAppSymbol, thisView); // NTK adds a viewBounds slot to this layout, but it's unnecessary. RemoveSlot(thisview, 'viewBounds); constant |layout_MinMailTransport.lyt| := MinMailTransport; // End of file MinMailTransport.lyt // Beginning of text file Install & Remove Scripts /* ** Newton Developer Technical Support Sample Code ** ** MinMail, a Newton 2.x transport example ** ** by J. Christopher Bell, Newton Developer Technical Support ** ** Copyright © 1994-7 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. */ SetPartFrameSlot( 'theTransport, GetLayout("MinMailTransport.lyt")); InstallScript := func(partFrame,removeFrame) begin // We created a reference to our protoTransport object using // SetPartFrameSlot. See the "MinMailTransport.t" layout's beforeScript. RegTransport( kAppSymbol, partFrame.theTransport ); RegUnionSoup( kAppSymbol, kMySoupDef ); // If this device has 'text dataDefs installed, we won't need our viewDef! if NOT GetDataDefs('text) then RegisterViewDef(GetLayout("TextViewDef.lyt"), EnsureInternal('text)); end; RemoveScript := func(removeFrame) begin UnRegisterViewDef(kSimpleTextViewDefSym, 'text); UnRegUnionSoup(kSoupName, kAppSymbol); UnRegTransport(kAppSymbol); end; SetPartFrameSlot( 'DeletionScript, func() begin // If the user scrubs out the package, this function will be called. // We do NOT want to do this during debugging because it will be called often. // When you download a new version with NTK, it will delete the old one // and call it's DeletionScript. Note that DeleteTransport removes the preferences // for the transport! Here, you might want to delete your temporary data (in our // case, we might delete our temp soups entirely) if NOT kDebugOn then DeleteTransport(kAppSymbol) end); // End of text file Install & Remove Scripts