DIRECTORY Commander USING [CommandProc, Register], DefaultRemoteNames USING [Get], FS USING [ComponentPositions, defaultStreamOptions, Error, OpenFile, StreamOptions, Close, ExpandName, Open, StreamOpen], GVSend USING [Abort], Icons USING [IconFlavor, NewIconFromFile], InputFocus USING [GetInputFocus], Menus USING [AppendMenuEntry, CopyEntry, CopyMenu, CreateEntry, CreateMenu, FindEntry, GetLine, GetNumberOfLines, Menu, MenuEntry, MenuLine, MenuProc, MouseButton, ReplaceMenuEntry, SetLine, targetNotFound], IO, Process USING [Detach], Rope, TEditSplit USING[ Split ], TiogaMenuOps USING[ tiogaMenu, Empty, Load, Store ], TiogaOps USING [ Ref, Location, CallWithLocks, CancelSelection, Delete, GetSelection, InsertRope, NextPlaceholder], TypeScript USING [Create], ViewerEvents USING [EventProc, EventRegistration, RegisterEventProc, UnRegisterEventProc], ViewerOps USING [AddProp, BlinkIcon, ComputeColumn, CreateViewer, DestroyViewer, FetchProp, MoveBelowViewer, OpenIcon, PaintViewer, SetMenu], ViewerIO USING [CreateViewerStreams, GetViewerFromStream], ViewerTools USING [EnableUserEdits, GetSelectionContents, InhibitUserEdits, SelPos, SelPosRec, SetSelection, SetTiogaContents, TiogaContents, TiogaContentsRec], ViewerClasses USING [Viewer], ViewerMenus USING [ Save ], UserCredentials USING [Get], UserProfile USING [Boolean, CallWhenProfileChanges, ProfileChangedProc, ListOfTokens, Token], ViewerBLT USING[ ChangeNumberOfLines ], WalnutSendInternal, WalnutSendOps, WalnutSendOpsExtras; WalnutSendControlImpl: CEDAR MONITOR IMPORTS Commander, DefaultRemoteNames, IO, Process, Rope, FS, GVSend, Icons, InputFocus, Menus, TEditSplit, TiogaMenuOps, TiogaOps, TypeScript, ViewerEvents, ViewerIO, ViewerMenus, ViewerOps, ViewerTools, UserCredentials, UserProfile, ViewerBLT, WalnutSendInternal, WalnutSendOps EXPORTS WalnutSendInternal, WalnutSendOps, WalnutSendOpsExtras SHARES Menus = BEGIN OPEN WalnutSendOps, WalnutSendInternal; -- Global types & variables ROPE: TYPE = Rope.ROPE; Viewer: TYPE = ViewerClasses.Viewer; TiogaContents: TYPE = ViewerTools.TiogaContents; userRName: PUBLIC ROPE _ NIL; -- user name with regsitry simpleUserName: PUBLIC ROPE _ NIL; -- user name without registry replyToSelf: PUBLIC BOOL _ FALSE; alwaysOpenSender: BOOL _ FALSE; sendCaption: PUBLIC ROPE _ "Walnut Sender"; destroyAfterSend: BOOL _ FALSE; senderList: LIST OF SenderInfo _ NIL; sendMenu: PUBLIC Menus.Menu _ Menus.CreateMenu[]; formsMenu: PUBLIC Menus.Menu; sendingMenu: PUBLIC Menus.Menu _ Menus.CreateMenu[]; blankMenu: PUBLIC Menus.Menu _ Menus.CreateMenu[]; confirmMenu: PUBLIC Menus.Menu _ Menus.CreateMenu[]; replyToMenu: PUBLIC Menus.Menu _ Menus.CreateMenu[]; sendingSynch: CONDITION; needToAuthenticate: PUBLIC BOOL _ TRUE; iconicSender: BOOL _ TRUE; -- make iconic initially only senderIcon: Icons.IconFlavor _ tool; edittingIcon: Icons.IconFlavor _ tool; reporter: IO.STREAM _ NIL; senderTS: Viewer _ NIL; labelFont: PUBLIC ROPE _ NIL; -- font to use for printing of header labels senderEvent: ViewerEvents.EventRegistration; defaultText: PUBLIC ViewerTools.TiogaContents; answerText: PUBLIC ViewerTools.TiogaContents; forwardText: PUBLIC ViewerTools.TiogaContents; standardDefaultText: ViewerTools.TiogaContents = NEW[ ViewerTools.TiogaContentsRec _ [contents: "\nSubject: \001Topic\002\nTo: \001Recipients\002\nCc: \001Copies To\002\n\n\001Message\002\n", formatting: "\000\000\000\006\000\000\235\312\000<\000\000\002\230\000J\232\002\320bs\007\234\t\230\020J\232\002\235\002\234\016\230\020J\232\002\235\002\234\n\230\017J\230\000J\230\t\227\000\205\227\000\000\000\000\000>\000\000\000\200\000\000"] ]; standardAnswerText: ViewerTools.TiogaContents = NEW[ ViewerTools.TiogaContentsRec _ [contents: "\nSubject: \001Topic\002\nIn-reply-to: \001Message\002\nTo: \001Recipients\002\nCc: \001Copies To\002\n\n\001Message\002\n\000", formatting: "\000\000\000\006\000\000\235\312\000I\000\000\002\230\000J\232\002\320bs\007\234\t\230\020J\232\002\235\013\234\013\230\026J\232\002\235\002\234\016\230\020J\232\002\235\002\234\n\230\017J\230\000J\232\001\234\t\230\t\227\000\205\227\000\000\000\000\000V\000\000\000\245\000\000"] ]; standardForwardText: ViewerTools.TiogaContents = NEW[ ViewerTools.TiogaContentsRec _ [contents: "\nSubject: \001Topic\002\nTo: \001Recipients\002\nCc: \001Copies To\002\n\n\001CoveringMessage\002\n\n--------------------------------------\nForwardedMessage\n--------------------------------------\n\n\001ClosingComment\002\n", formatting: "\000\000\000\006\000\000\235\312\000W\000\000\002\230\000J\232\002\320bs\007\234\t\230\020J\232\002\235\002\234\016\230\020J\232\002\235\002\234\n\230\017J\230\000J\230\021J\230\000\002\232\001\234&\230&J\230\020\227J\232\001\234&\230&J\230\000J\230\020\227\000\205\227\000\000\000\000\000\270\000\000\001\025\000\000"] ]; defaultForm: WalnutSendOps.Form = NEW[ WalnutSendOps.FormRec _ [ formText: defaultText, fields: LIST[ NIL, NIL, simpleUserName] ] ]; viewerStart: ViewerTools.SelPos = NEW[ViewerTools.SelPosRec _ [0, 0]]; SenderPropRec: TYPE = RECORD[proc: PROC[Viewer, ATOM, REF ANY], data: REF ANY]; SenderPropValue: TYPE = REF SenderPropRec; SenderProp: TYPE = RECORD[key: ATOM, value: SenderPropValue]; senderPropsList: LIST OF SenderProp _ NIL; FileStatus: TYPE = {illegalName, local, remote, localNotFound, remoteNotFound, otherError}; -- ************************************************************************ -- Sending messages is independent of CedarDB WalnutSendMail: Commander.CommandProc = { WalnutSendProc[fromExec: TRUE]}; WalnutSendProc: PUBLIC PROC[fromExec: BOOL] = BEGIN senderInfo: SenderInfo; IF fromExec THEN fromExec _ ~alwaysOpenSender; IF ~fromExec THEN iconicSender _ FALSE; senderInfo _ BuildSender[paint: TRUE, iconic: iconicSender, form: defaultForm]; IF ~iconicSender THEN GrabFocus[senderInfo.senderV]; iconicSender _ FALSE; -- iconic only the first time END; GrabFocus: PUBLIC ENTRY PROC[senderV: Viewer] = BEGIN ENABLE UNWIND => NULL; IF senderV.iconic THEN RETURN; ViewerTools.SetSelection[senderV, viewerStart]; [] _ TiogaOps.NextPlaceholder[gotoend: FALSE]; END; ChangeUserRName: PUBLIC PROC[newUserRName: ROPE] = { userRName _ newUserRName; simpleUserName _ newUserRName.Substr[0, newUserRName.Find["."]]; }; -- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * BuildSendViewer: PUBLIC PROC[paint, iconic: BOOL, form: WalnutSendOps.Form _ NIL, who: Viewer _ NIL] RETURNS[v: Viewer] = { newForm: WalnutSendOps.Form = IF form # NIL THEN form ELSE defaultForm; RETURN[BuildSender[paint, iconic, newForm, who].senderV] }; BuildSender: ENTRY PROC[paint, iconic: BOOL, form: WalnutSendOps.Form, who: Viewer _ NIL, forceNew: BOOL _ FALSE] RETURNS[senderInfo: SenderInfo] = BEGIN ENABLE UNWIND => IF senderInfo # NIL AND senderInfo.senderV # NIL THEN senderInfo.senderV.inhibitDestroy _ FALSE; senderV: Viewer; IF ~forceNew THEN FOR sL: LIST OF SenderInfo _ senderList, sL.rest UNTIL sL=NIL DO senderInfo _ sL.first; IF ~(senderInfo.senderV.newVersion) AND (senderInfo.sendHandle = NIL) AND ~senderInfo.senderV.destroyed THEN { senderV _ senderInfo.senderV; IF senderV.link # NIL THEN InternalDestroySplits[senderV]; senderV.inhibitDestroy _ TRUE; KillFeedbackSel[senderV]; IF senderV.iconic THEN { ClearOpenEvent[senderInfo]; IF (who # NIL) AND (senderV.column = who.column) THEN { ViewerOps.OpenIcon[icon: senderV, paint: FALSE]; ViewerOps.MoveBelowViewer[altered: senderV, static: who, paint: TRUE] } ELSE ViewerOps.OpenIcon[senderV]; }; IF form # NIL THEN InsertForm[senderInfo, form, TRUE] ELSE TiogaMenuOps.Empty[senderV]; ViewerOps.PaintViewer[senderV, menu]; senderV.inhibitDestroy _ FALSE; RETURN }; ENDLOOP; -- need to create a new sender senderV _ ViewerOps.CreateViewer[ flavor: $Text, info: [ name: sendCaption, column: IF who#NIL THEN who.column ELSE left, iconic: iconic, icon: senderIcon], paint: who = NIL]; IF who # NIL THEN {ViewerOps.MoveBelowViewer[altered: senderV, static: who, paint: FALSE]; ViewerOps.ComputeColumn[column: senderV.column, paint: TRUE] }; senderV.inhibitDestroy _ TRUE; ViewerOps.SetMenu[senderV, sendMenu]; senderInfo _ NEW[SenderInfoObject _ [senderV: senderV]]; ViewerOps.AddProp[senderV, $SenderInfo, senderInfo]; FOR spl: LIST OF SenderProp _ senderPropsList, spl.rest UNTIL spl=NIL DO ViewerOps.AddProp[senderV, spl.first.key, spl.first.value]; ENDLOOP; senderList _ CONS[senderInfo, senderList]; senderInfo.destroyEvent _ ViewerEvents.RegisterEventProc[ proc: DestroySendViewer, event: destroy, filter: senderV, before: TRUE]; senderInfo.closeEvent _ ViewerEvents.RegisterEventProc[ proc: CloseSendViewer, event: close, filter: senderV, before: TRUE]; senderInfo.editEvent _ ViewerEvents.RegisterEventProc[ proc: FirstEditSendViewer, event: edit, filter: senderV, before: TRUE]; IF form # NIL THEN InsertForm[senderInfo, form, TRUE] ELSE TiogaMenuOps.Empty[senderV]; senderV.inhibitDestroy _ FALSE; IF iconic AND ~senderV.iconic THEN ViewerOps.PaintViewer[senderV, all]; END; ClearOpenEvent: PROC[senderInfo: SenderInfo] = BEGIN ra: REF ANY _ ViewerOps.FetchProp[senderInfo.senderV, $WalnutSendOpsForm]; form: WalnutSendOps.Form; IF ra = NIL THEN RETURN; form _ NARROW[ra]; ViewerEvents.UnRegisterEventProc[senderInfo.openEvent, open]; senderInfo.openEvent _ NIL; END; -- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * DoUserNameAndRegistry: PROC = { uN: ROPE = UserCredentials.Get[].name; pos: INT; IF (pos _ Rope.SkipTo[s: uN, skip: "."]) = Rope.Length[uN] THEN { simpleUserName _ uN; defaultRegistry _ DefaultRemoteNames.Get[].registry; userRName _ Rope.Cat[uN, ".", defaultRegistry]; } ELSE { simpleUserName _ Rope.Substr[uN, 0, pos]; defaultRegistry _ Rope.Substr[uN, pos+1]; userRName _ uN; }; }; WalnutSendInit: UserProfile.ProfileChangedProc = { defaultFormFile: ROPE = UserProfile.Token[key: "WalnutSend.DefaultForm", default: NIL]; answerFormFile: ROPE = UserProfile.Token[key: "WalnutSend.AnswerForm", default: NIL]; forwardFormFile: ROPE = UserProfile.Token[key: "WalnutSend.ForwardForm", default: NIL]; formContents: ViewerTools.TiogaContents _ NIL; DoUserNameAndRegistry[]; needToAuthenticate _ TRUE; replyToSelf _ UserProfile.Boolean[key: "WalnutSend.ReplyToSelf", default: FALSE]; destroyAfterSend _ UserProfile.Boolean[key: "WalnutSend.DestroyAfterSend", default: FALSE]; alwaysOpenSender _ UserProfile.Boolean[key: "WalnutSend.AlwaysOpenSender", default: FALSE]; IF defaultFormFile # NIL THEN formContents _ GetSendForm[defaultFormFile]; IF formContents # NIL THEN { defaultText _ defaultForm.formText _ formContents; defaultForm.fields _ NIL } ELSE { defaultText _ defaultForm.formText _ standardDefaultText; defaultForm.fields _ LIST[NIL, NIL, simpleUserName]}; formContents _ NIL; IF answerFormFile # NIL THEN formContents _ GetSendForm[answerFormFile]; IF formContents # NIL THEN answerText _ formContents ELSE answerText _ standardAnswerText; formContents _ NIL; IF forwardFormFile # NIL THEN formContents _ GetSendForm[forwardFormFile]; IF formContents # NIL THEN forwardText _ formContents ELSE forwardText _ standardForwardText; labelFont _ UserProfile.Token[key: "WalnutSend.MsgHeadersLooks", default: "sb"]; SetFormsMenu[]; }; GetSendForm: PROC[ fileName: ROPE ] RETURNS[ text: ViewerTools.TiogaContents ] = BEGIN s: IO.STREAM; so: FS.StreamOptions _ FS.defaultStreamOptions; so[tiogaRead] _ FALSE; s _ FS.StreamOpen[fileName: fileName, streamOptions: so ! FS.Error => { s _ NIL; CONTINUE }]; IF s # NIL THEN { text _ TiogaTextFromStrm[s ! FS.Error => { text _ NIL; CONTINUE }]; s.Close[ ! FS.Error => { s _ NIL; CONTINUE }] } END; DisplayTxtInSender: ENTRY PROC[senderV: Viewer, txt: ROPE] = BEGIN ENABLE UNWIND => NULL; senderInfo: SenderInfo _ NARROW[ViewerOps.FetchProp[senderV, $SenderInfo]]; InternalDisplayTxt[senderInfo, txt]; END; InternalDisplayTxt: INTERNAL PROC[senderInfo: SenderInfo, txt: ROPE] = BEGIN tc: ViewerTools.TiogaContents _ NEW[ViewerTools.TiogaContentsRec]; tc.contents _ txt; InternalDisplayTioga[senderInfo, tc, TRUE]; END; InternalDisplayTioga: INTERNAL PROC[senderInfo: SenderInfo, tc: TiogaContents, grab: BOOL] = BEGIN senderV: Viewer _ senderInfo.senderV; iHadFocus: BOOL _ InputFocus.GetInputFocus[].owner = senderV; IF senderV.link # NIL THEN InternalDestroySplits[senderV]; KillFeedbackSel[senderV]; ViewerTools.SetTiogaContents[senderV, tc]; -- test if I had the focus & no-one else has it now IF grab AND iHadFocus AND InputFocus.GetInputFocus[].owner = NIL THEN { ViewerTools.SetSelection[senderV, viewerStart]; [] _ TiogaOps.NextPlaceholder[gotoend: FALSE] }; UnsetNewVersion[senderV]; senderInfo.successfullySent _ FALSE; END; ClearSendViewer: PUBLIC ENTRY PROC[senderV: Viewer] = { ENABLE UNWIND => NULL; senderInfo: SenderInfo _ NARROW[ViewerOps.FetchProp[senderV, $SenderInfo]]; IF senderInfo = NIL THEN RETURN; -- not a sender InsertForm[senderInfo, defaultForm, TRUE] }; StuffForm: PUBLIC ENTRY PROC[v: Viewer, contents: TiogaContents] = { ENABLE UNWIND => NULL; senderInfo: SenderInfo _ NARROW[ViewerOps.FetchProp[v, $SenderInfo]]; IF senderInfo = NIL THEN RETURN; -- not a sender InternalDisplayTioga[senderInfo, contents, TRUE] }; InsertForm: INTERNAL PROC[ sender: SenderInfo, form: WalnutSendOps.Form, force: BOOL ] = BEGIN senderV: Viewer = sender.senderV; whoHasIt: Viewer; IF senderV.iconic THEN { ViewerOps.AddProp[senderV, $WalnutSendOpsForm, form]; sender.openEvent _ ViewerEvents.RegisterEventProc[ proc: OpenSendViewer, event: open, filter: senderV, before: FALSE]; UnsetNewVersion[senderV]; -- not strictly true but want to reuse RETURN}; -- first stuff the text of the form into the Viewer InternalDisplayTioga[sender, form.formText, FALSE]; IF force OR ((whoHasIt _ InputFocus.GetInputFocus[].owner) = NIL) OR (whoHasIt = senderV) THEN DoPlaceHolders[senderV, form.fields] ELSE { ViewerOps.AddProp[senderV, $WalnutSendOpsFields, form.fields]; sender.focusEvent _ ViewerEvents.RegisterEventProc[ proc: SetFocusInSendViewer, event: setInputFocus, filter: senderV, before: FALSE]; UnsetNewVersion[senderV]; -- not strictly true but want to reuse }; END; DoPlaceHolders: INTERNAL PROC[senderV: Viewer, fieldsList: LIST OF ROPE] = BEGIN AddFields: PROC[ root: TiogaOps.Ref ] = { -- now try to find all of the placeholders in the text and match them with the entries in the fields list FOR rl: LIST OF ROPE _ fieldsList, rl.rest UNTIL rl = NIL DO field: ROPE = rl.first; IF NOT TiogaOps.NextPlaceholder[gotoend: FALSE].found THEN EXIT; IF field = NIL THEN LOOP; TiogaOps.Delete[]; TiogaOps.InsertRope[field] ENDLOOP }; IF senderV.destroyed OR senderV.iconic THEN RETURN; --woops ViewerTools.SetSelection[ senderV, viewerStart]; TiogaOps.CallWithLocks[ AddFields ]; UnsetNewVersion[senderV]; ViewerTools.SetSelection[ senderV, viewerStart]; [] _ TiogaOps.NextPlaceholder[gotoend: FALSE]; END; AddToSenderProps: PUBLIC ENTRY PROC [key: ATOM, proc: PROC[Viewer, ATOM, REF ANY], data: REF ANY] = { ENABLE UNWIND => NULL; senderPropsList _ CONS[[key: key, value: NEW[SenderPropRec _ [proc, data]]], senderPropsList]; }; RemoveFromSenderProps: PUBLIC ENTRY PROC[key: ATOM] = { ENABLE UNWIND => NULL; sp: LIST OF SenderProp; IF senderPropsList = NIL THEN RETURN; FOR spl: LIST OF SenderProp _ senderPropsList, spl.rest UNTIL spl = NIL DO IF spl.first.key = key THEN IF spl = senderPropsList THEN { senderPropsList _ senderPropsList.rest; RETURN} ELSE { sp.rest _ spl.rest; RETURN}; sp _ spl; ENDLOOP; }; AddToSendMenu: PUBLIC ENTRY PROC[label: ROPE, proc: Menus.MenuProc] = BEGIN ENABLE UNWIND => NULL; old: Menus.MenuEntry _ Menus.FindEntry[sendMenu, label]; IF old = NIL THEN Menus.AppendMenuEntry[sendMenu, Menus.CreateEntry[label, proc]]; FOR sL: LIST OF SenderInfo _ senderList, sL.rest UNTIL sL = NIL DO thisMenu: Menus.Menu; v: Viewer = sL.first.senderV; IF v = NIL OR v.destroyed THEN LOOP; old _ Menus.FindEntry[thisMenu _ sL.first.senderV.menu, label]; IF old = NIL THEN { Menus.AppendMenuEntry[thisMenu, Menus.CreateEntry[label, proc]]; ViewerOps.PaintViewer[sL.first.senderV, menu]; }; ENDLOOP; END; RemoveFromSendMenu: PUBLIC PROC[name: ROPE] = BEGIN old: Menus.MenuEntry _ Menus.FindEntry[sendMenu, name]; IF old = NIL THEN RETURN; Menus.ReplaceMenuEntry[sendMenu, old ! Menus.targetNotFound => CONTINUE]; FOR sL: LIST OF SenderInfo _ senderList, sL.rest UNTIL sL = NIL DO thisMenu: Menus.Menu; v: Viewer = sL.first.senderV; IF v = NIL OR v.destroyed THEN LOOP; old _ Menus.FindEntry[thisMenu _ sL.first.senderV.menu, name]; IF old # NIL THEN { Menus.ReplaceMenuEntry[thisMenu, old ! Menus.targetNotFound => CONTINUE]; ViewerOps.PaintViewer[sL.first.senderV, menu]; }; ENDLOOP; END; -- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -- Menu procedures NewMsgFormProc: ENTRY Menus.MenuProc = BEGIN ENABLE UNWIND => NULL; viewer: Viewer _ NARROW[parent]; NewMsgForm[viewer] END; PrevMsgProc: ENTRY Menus.MenuProc = BEGIN ENABLE UNWIND => NULL; viewer: Viewer _ NARROW[parent]; senderInfo: SenderInfo _ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]]; IF viewer.link#NIL THEN InternalDestroySplits[viewer]; ClearFileAssoc[viewer]; IF senderInfo.prevMsg = NIL THEN InsertForm[senderInfo, defaultForm, TRUE] ELSE { KillFeedbackSel[viewer]; ViewerTools.SetTiogaContents[viewer, senderInfo.prevMsg]; UnsetNewVersion[viewer] } END; NewSenderProc: Menus.MenuProc = BEGIN senderInfo: SenderInfo _ BuildSender[paint: TRUE, iconic: FALSE, form: defaultForm, forceNew: TRUE]; GrabFocus[senderInfo.senderV]; END; NewMsgForm: INTERNAL PROC[viewer: Viewer] = BEGIN senderInfo: SenderInfo _ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]]; InsertForm[senderInfo, defaultForm, TRUE]; ClearFileAssoc[viewer]; END; -- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * DestroySendViewer: ViewerEvents.EventProc = BEGIN senderInfo: SenderInfo _ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]]; IF senderInfo = NIL THEN RETURN[FALSE]; IF senderInfo.senderV # NIL THEN senderInfo.senderV.file _ NIL; -- make sure that there is no file pointed to so that you don't get something saved in UnSavedDocumentCache IF ViewerOps.FetchProp[viewer, $DestroySenderProc] = NIL THEN RETURN[FALSE]; TRUSTED {Process.Detach[FORK CheckForDestroy[viewer, senderInfo]]}; RETURN[TRUE]; END; OpenSendViewer: ViewerEvents.EventProc = BEGIN OPEN Menus; senderInfo: SenderInfo _ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]]; form: WalnutSendOps.Form; ra: REF ANY; IF senderInfo = NIL THEN RETURN[FALSE]; IF (ra _ ViewerOps.FetchProp[viewer, $WalnutSendOpsForm]) = NIL THEN RETURN[FALSE]; form _ NARROW[ra]; ViewerEvents.UnRegisterEventProc[senderInfo.openEvent, open]; senderInfo.openEvent _ NIL; TRUSTED { Process.Detach[FORK EntryInsertForm[senderInfo, form]]}; RETURN[FALSE]; END; EntryInsertForm: ENTRY PROC[senderInfo: SenderInfo, form: WalnutSendOps.Form] = BEGIN ENABLE UNWIND => NULL; InsertForm[senderInfo, form, TRUE]; END; SetFocusInSendViewer: ViewerEvents.EventProc = BEGIN OPEN Menus; senderInfo: SenderInfo _ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]]; fieldsList: LIST OF ROPE; ra: REF ANY; IF senderInfo = NIL THEN RETURN[FALSE]; IF (ra _ ViewerOps.FetchProp[viewer, $WalnutSendOpsFields]) = NIL THEN RETURN[FALSE]; fieldsList _ NARROW[ra]; ViewerEvents.UnRegisterEventProc[senderInfo.focusEvent, setInputFocus]; senderInfo.focusEvent _ NIL; TRUSTED { Process.Detach[FORK EntryPlaceHolders[senderInfo, fieldsList]]}; RETURN[FALSE]; END; EntryPlaceHolders: ENTRY PROC[senderInfo: SenderInfo, fieldsList: LIST OF ROPE] = BEGIN ENABLE UNWIND => NULL; DoPlaceHolders[senderInfo.senderV, fieldsList]; END; CloseSendViewer: ViewerEvents.EventProc = BEGIN senderInfo: SenderInfo _ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]]; IF senderInfo = NIL THEN RETURN[FALSE]; IF senderInfo.dontClose THEN { SenderReport[" **** Sender cannot be closed right now\n", TRUE]; RETURN[TRUE] }; viewer.icon _ IF viewer.newVersion THEN edittingIcon ELSE senderIcon; RETURN[FALSE]; END; FirstEditSendViewer: ViewerEvents.EventProc = BEGIN OPEN Menus; senderInfo: SenderInfo _ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]]; IF senderInfo = NIL THEN RETURN[FALSE]; SenderNewVersion[viewer]; -- show as guarded RETURN[FALSE]; END; CheckForDestroy: ENTRY PROC[senderV: Viewer, senderInfo: SenderInfo] = BEGIN ENABLE UNWIND => NULL; okToDestroy: BOOL _ TRUE; IF senderV.destroyed THEN RETURN; -- woops IF senderV.newVersion THEN {InternalReport["\nConfirm quitting with last message unsent"]; IF okToDestroy _ SendingConfirm[senderInfo] THEN senderV.newVersion _ FALSE; }; IF ~okToDestroy THEN RETURN; IF senderInfo.destroyEvent # NIL THEN ViewerEvents.UnRegisterEventProc[senderInfo.destroyEvent, destroy]; IF senderInfo.closeEvent # NIL THEN ViewerEvents.UnRegisterEventProc[senderInfo.closeEvent, close]; IF senderInfo.editEvent # NIL THEN ViewerEvents.UnRegisterEventProc[senderInfo.editEvent, edit]; IF senderInfo.openEvent # NIL THEN ViewerEvents.UnRegisterEventProc[senderInfo.openEvent, open]; IF senderInfo.focusEvent # NIL THEN ViewerEvents.UnRegisterEventProc[senderInfo.focusEvent, setInputFocus]; senderInfo.destroyEvent _ NIL; senderInfo.closeEvent _ NIL; senderInfo.editEvent _ NIL; senderInfo.openEvent _ NIL; senderInfo.focusEvent _ NIL; TRUSTED {Process.Detach[FORK ViewerOps.DestroyViewer[senderV]]}; END; LockViewerOpen: PUBLIC ENTRY PROC[viewer: Viewer] = BEGIN ENABLE UNWIND => NULL; senderInfo: SenderInfo _ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]]; IF senderInfo = NIL THEN RETURN; senderInfo.dontClose _ TRUE; END; ReleaseOpenViewer: PUBLIC ENTRY PROC[viewer: Viewer] = BEGIN ENABLE UNWIND => NULL; senderInfo: SenderInfo _ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]]; IF senderInfo = NIL THEN RETURN; senderInfo.dontClose _ FALSE; END; SenderReport: PUBLIC ENTRY PROC[msg: ROPE, blink: BOOL _ FALSE] = BEGIN ENABLE UNWIND => NULL; InternalReport[msg, blink]; END; InternalReport: INTERNAL PROC[msg: ROPE, blink: BOOL _ FALSE] = BEGIN IF reporter = NIL THEN { senderTS _ TypeScript.Create[ info: [name: "WalnutSend", iconic: FALSE, column: right, openHeight: 64]]; reporter _ ViewerIO.CreateViewerStreams[NIL, senderTS].out; senderEvent _ ViewerEvents.RegisterEventProc[ proc: DestroyReporter, event: destroy, filter: senderTS, before: TRUE]; }; IF blink THEN { viewer: Viewer = ViewerIO.GetViewerFromStream[reporter]; -- NIL for $Dribble class IF viewer # NIL THEN ViewerOps.BlinkIcon[viewer]; }; reporter.PutRope[msg]; END; DestroyReporter: ViewerEvents.EventProc = BEGIN IF senderEvent # NIL THEN ViewerEvents.UnRegisterEventProc[senderEvent, destroy]; senderEvent _ NIL; senderTS _ NIL; reporter _ NIL; RETURN[FALSE]; END; RegisterReporter: PUBLIC ENTRY PROC[strm: IO.STREAM] = BEGIN ENABLE UNWIND => NULL; IF senderTS # NIL THEN { reporter.Close[]; ViewerEvents.UnRegisterEventProc[senderEvent, destroy]; senderEvent _ NIL; ViewerOps.DestroyViewer[senderTS]; senderTS _ NIL; }; reporter _ strm; END; UnregisterReporter: PUBLIC ENTRY PROC[strm: IO.STREAM] = BEGIN ENABLE UNWIND => NULL; IF reporter = strm THEN reporter _ NIL; END; DenySendProc: ENTRY Menus.MenuProc = BEGIN ENABLE UNWIND => NULL; viewer: Viewer _ NARROW[parent]; senderInfo: SenderInfo _ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]]; senderInfo.confirmed _ FALSE; senderInfo.userResponded _ TRUE; BROADCAST sendingSynch; END; ConfirmSendProc: ENTRY Menus.MenuProc = BEGIN ENABLE UNWIND => NULL; viewer: Viewer _ NARROW[parent]; senderInfo: SenderInfo _ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]]; senderInfo.confirmed _ TRUE; senderInfo.userResponded _ TRUE; BROADCAST sendingSynch; END; AbortSendProc: ENTRY Menus.MenuProc = BEGIN ENABLE UNWIND => NULL; viewer: Viewer _ NARROW[parent]; senderInfo: SenderInfo _ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]]; ViewerOps.SetMenu[viewer, blankMenu]; senderInfo.aborted _ TRUE; END; ReplyToSelfProc: ENTRY Menus.MenuProc = BEGIN ENABLE UNWIND => NULL; viewer: Viewer _ NARROW[parent]; senderInfo: SenderInfo _ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]]; senderInfo.replyToResponse _ self; senderInfo.userResponded _ TRUE; BROADCAST sendingSynch; END; ReplyToAllProc: ENTRY Menus.MenuProc = BEGIN ENABLE UNWIND => NULL; viewer: Viewer _ NARROW[parent]; senderInfo: SenderInfo _ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]]; senderInfo.replyToResponse _ all; senderInfo.userResponded _ TRUE; BROADCAST sendingSynch; END; ReplyToCancelProc: ENTRY Menus.MenuProc = BEGIN ENABLE UNWIND => NULL; viewer: Viewer _ NARROW[parent]; senderInfo: SenderInfo _ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]]; senderInfo.replyToResponse _ cancel; senderInfo.userResponded _ TRUE; BROADCAST sendingSynch; END; ---------------------------- ReplyToResponse: PUBLIC ENTRY PROC[senderInfo: SenderInfo] RETURNS [HowToReplyTo] = BEGIN ENABLE UNWIND => NULL; senderV: Viewer _ senderInfo.senderV; ViewerOps.BlinkIcon[senderV, IF senderV.iconic THEN 0 ELSE 1]; UNTIL senderInfo.userResponded DO WAIT sendingSynch; ENDLOOP; senderInfo.userResponded _ FALSE; RETURN[senderInfo.replyToResponse] END; CheckForAbortSend: PUBLIC ENTRY PROC[senderInfo: SenderInfo] RETURNS[BOOL] = BEGIN ENABLE UNWIND => NULL; IF senderInfo.aborted THEN { InternalReport[" Aborting delivery ...\n"]; IF senderInfo.sendHandle#NIL THEN GVSend.Abort[senderInfo.sendHandle]; IF senderInfo.aborted THEN senderInfo.senderV.inhibitDestroy _ FALSE; }; RETURN[senderInfo.aborted]; END; Confirmation: PUBLIC ENTRY PROC[senderInfo: SenderInfo] RETURNS [BOOL] = { ENABLE UNWIND => NULL; RETURN[SendingConfirm[senderInfo]]}; SendingConfirm: INTERNAL PROC[senderInfo: SenderInfo] RETURNS [BOOL] = BEGIN senderV: Viewer _ senderInfo.senderV; oldM: Menus.Menu _ ChangeMenu[senderV, confirmMenu]; ViewerOps.BlinkIcon[senderV, IF senderV.iconic THEN 0 ELSE 1]; UNTIL senderInfo.userResponded DO WAIT sendingSynch; ENDLOOP; ViewerOps.SetMenu[senderV, oldM]; senderInfo.userResponded _ FALSE; RETURN[senderInfo.confirmed]; END; ChangeMenu: PROC[v: Viewer, menu: Menus.Menu] RETURNS [oldM: Menus.Menu] = BEGIN oldM _ v.menu; ViewerOps.SetMenu[v, menu]; END; MessageSendProc: PUBLIC Menus.MenuProc = BEGIN self: Viewer _ NARROW[parent]; iHadFocus: BOOL _ InputFocus.GetInputFocus[].owner = self; ViewerTools.InhibitUserEdits[self]; IF self.link#NIL THEN DestroySplits[self]; TRUSTED {Process.Detach[FORK SendProc[self, mouseButton, iHadFocus]]}; END; SendProc: PROC[viewer: Viewer, mouseButton: Menus.MouseButton, iHadFocus: BOOL] = BEGIN OPEN ViewerOps; sendOK: BOOL; senderInfo: SenderInfo = NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]]; viewer.newVersion _ TRUE; viewer.name _ "Sending ..."; PaintViewer[viewer, caption]; viewer.file _ NIL; sendOK _ WalnutSendOps.Send[v: viewer, doClose: mouseButton=blue]; IF ~sendOK THEN SenderNewVersion[viewer]; viewer.name _ sendCaption; PaintViewer[viewer, caption]; viewer.icon _ senderIcon; ViewerTools.EnableUserEdits[viewer]; IF sendOK THEN { AfterSendInsert: ENTRY PROC = BEGIN ENABLE UNWIND => NULL; InsertForm[senderInfo, defaultForm, FALSE]; END; IF mouseButton=blue AND destroyAfterSend THEN {DestroyViewer[viewer]; RETURN}; UnsetNewVersion[viewer]; AfterSendInsert[]; }; END; SenderSplitProc: Menus.MenuProc = BEGIN self: Viewer = NARROW[parent]; newV: Viewer; nsI, senderInfo: SenderInfo; TEditSplit.Split[self]; -- now find the newest link in the chain to copy properties FOR newV _ self.link, newV.link UNTIL newV.link = self DO ENDLOOP; senderInfo _ NARROW[ViewerOps.FetchProp[self, $SenderInfo]]; ViewerOps.AddProp[newV, $SenderInfo, nsI _ NEW[SenderInfoObject _ senderInfo^]]; nsI.senderV _ newV; nsI.destroyEvent _ ViewerEvents.RegisterEventProc[ proc: DestroySendViewer, event: destroy, filter: newV, before: TRUE]; nsI.closeEvent _ ViewerEvents.RegisterEventProc[ proc: CloseSendViewer, event: close, filter: newV, before: TRUE]; nsI.editEvent _ ViewerEvents.RegisterEventProc[ proc: FirstEditSendViewer, event: edit, filter: newV, before: TRUE]; newV.icon _ self.icon END; DestroySplits: ENTRY PROC[keepThisOne: Viewer] = BEGIN ENABLE UNWIND => NULL; InternalDestroySplits[keepThisOne]; END; InternalDestroySplits: INTERNAL PROC[keepThisOne: Viewer] = BEGIN next: Viewer _ keepThisOne.link; next2: Viewer; sI: SenderInfo; DO IF next = keepThisOne THEN EXIT; sI _ NARROW[ViewerOps.FetchProp[next, $SenderInfo]]; IF sI # NIL THEN { IF sI.destroyEvent # NIL THEN ViewerEvents.UnRegisterEventProc[sI.destroyEvent, destroy]; IF sI.closeEvent # NIL THEN ViewerEvents.UnRegisterEventProc[sI.closeEvent, close]; IF sI.editEvent # NIL THEN ViewerEvents.UnRegisterEventProc[sI.editEvent, edit]; sI.destroyEvent _ NIL; sI.closeEvent _ NIL; sI.editEvent _ NIL; }; next2 _ next.link; IF sI # NIL THEN ViewerOps.DestroyViewer[next]; -- DON'T FORK here next _ next2; ENDLOOP; -- make sure this sender is on the list of senders FOR sL: LIST OF SenderInfo _ senderList, sL.rest UNTIL sL=NIL DO IF sL.first.senderV = keepThisOne THEN RETURN; ENDLOOP; sI _ NARROW[ViewerOps.FetchProp[keepThisOne, $SenderInfo]]; IF sI = NIL THEN RETURN; senderList _ CONS[sI, senderList]; END; SenderPlacesProc: Menus.MenuProc = Menus.CopyEntry[ Menus.FindEntry[ TiogaMenuOps.tiogaMenu, "Places" ] ].proc; SenderLevelsProc: Menus.MenuProc = Menus.CopyEntry[ Menus.FindEntry[ TiogaMenuOps.tiogaMenu, "Levels" ] ].proc; SenderGetFormProc: ENTRY Menus.MenuProc = BEGIN ENABLE UNWIND => NULL; self: Viewer = NARROW[parent]; filename: ROPE _ NARROW[clientData]; fileStatus: FileStatus; exp, fName: ROPE; IF filename.Length[] = 0 THEN RETURN; [fileStatus, exp, fName] _ CheckForFile[filename]; IF fileStatus = local OR fileStatus = remote THEN { tc: ViewerTools.TiogaContents _ GetSendForm[fName]; senderInfo: SenderInfo _ NARROW[ViewerOps.FetchProp[self, $SenderInfo]]; InternalDisplayTioga[senderInfo, tc, TRUE]; ClearFileAssoc[self]; } ELSE {InternalReport["\n", TRUE]; InternalReport[exp]; InternalReport["\n"]}; END; SenderGetFileProc: Menus.MenuProc = BEGIN self: Viewer = NARROW[parent]; filename: ROPE _ ViewerTools.GetSelectionContents[]; IF filename.Length[] = 0 THEN RETURN; LoadFile[self, filename]; END; SenderFormsProc: Menus.MenuProc = BEGIN viewer: Viewer = NARROW[parent]; menu: Menus.Menu _ viewer.menu; found: BOOLEAN _ FALSE; firstForm: Menus.MenuEntry = Menus.GetLine[formsMenu, 0]; numLines: Menus.MenuLine = Menus.GetNumberOfLines[menu]; FOR i: Menus.MenuLine IN [1..numLines) DO -- see if already showing the submenu IF firstForm = NIL OR (Menus.GetLine[menu,i] # NIL AND Rope.Equal[Menus.GetLine[menu,i].name, firstForm.name]) THEN { -- yes, so remove it FOR j: Menus.MenuLine IN (i..numLines) DO Menus.SetLine[menu, j-1, Menus.GetLine[menu, j]]; ENDLOOP; menu.linesUsed _ menu.linesUsed-1; found _ TRUE; EXIT }; ENDLOOP; IF ~found THEN { Menus.SetLine[menu, numLines, Menus.GetLine[Menus.CopyMenu[formsMenu], 0]] }; ViewerBLT.ChangeNumberOfLines[viewer, menu.linesUsed]; IF ~found AND viewer.newVersion THEN SenderNewVersion[viewer]; END; SenderStoreProc: Menus.MenuProc = BEGIN self: Viewer _ NARROW[parent]; fileName: Rope.ROPE = ViewerTools.GetSelectionContents[]; status: FileStatus; exp: Rope.ROPE; [status, exp] _ CheckForFile[fileName]; SELECT status FROM local, localNotFound => { TiogaMenuOps.Store[self, fileName]; IF ~self.newVersion THEN UnsetNewVersion[self] }; ENDCASE => BadSaveOrStore[status, exp, fileName]; END; SenderSaveProc: Menus.MenuProc = BEGIN self: Viewer _ NARROW[parent]; status: FileStatus; exp: Rope.ROPE; IF self.file = NIL THEN {SenderReport["\nCan't save - no file name\n"]; RETURN}; [status, exp] _ CheckForFile[self.name]; SELECT status FROM local, localNotFound => { ViewerMenus.Save[ self ]; IF ~self.newVersion THEN UnsetNewVersion[self] }; ENDCASE => BadSaveOrStore[status, exp, self.name]; END; -- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * LoadFile: PROC[self: Viewer, filename: ROPE, noName: BOOL _ FALSE] = BEGIN status: FileStatus; s, fName, nameToUse: ROPE; [status, s, fName] _ CheckForFile[filename, FALSE]; IF status = localNotFound OR status = remoteNotFound THEN { [status, s, fName] _ CheckForFile[filename]; -- try .form extension nameToUse _ fName; } ELSE nameToUse _ filename; IF status = local OR status = remote THEN { self.file _ NIL; -- make sure that no one else thinks you're saving something TiogaMenuOps.Load[ self, nameToUse ]; ViewerTools.EnableUserEdits[ self ]; -- in case remote file specified IF noName THEN { self.name _ sendCaption; ViewerOps.PaintViewer[self, caption]; self.file _ NIL; -- don't want any file association }; GrabFocus[self]; UnsetNewVersion[self]; } ELSE ErrorReport[s]; END; CheckForFile: PROC[name: ROPE, addExt: BOOL _ TRUE] RETURNS[fileStatus: FileStatus, explanation, fName: ROPE] = BEGIN of: FS.OpenFile; cp: FS.ComponentPositions; explanation _ NIL; fName _ name; BEGIN ENABLE FS.Error => { IF error.group = user THEN { SELECT error.code FROM $illegalName => fileStatus _ illegalName; $unknownFile => fileStatus _ IF fileStatus = local THEN localNotFound ELSE remoteNotFound; ENDCASE => fileStatus _ otherError} ELSE fileStatus _ otherError; explanation _ error.explanation; CONTINUE }; cp _ FS.ExpandName[name, NIL].cp; IF explanation # NIL THEN RETURN; fName _ IF cp.ext.length = 0 AND addExt THEN name.Concat[".form"] ELSE name; IF cp.server.length = 0 THEN fileStatus _ local ELSE fileStatus _ remote; of _ FS.Open[fName]; IF of # NIL THEN FS.Close[of]; END; END; ErrorReport: ENTRY PROC[exp: ROPE] = { ENABLE UNWIND => NULL; InternalReport["\n", TRUE]; InternalReport[exp]; InternalReport["\n"]; }; BadSaveOrStore: PROC[status: FileStatus, exp, name: ROPE] = BEGIN SELECT status FROM remote, remoteNotFound => ErrorReport[Rope.Cat["Can't store on remote file: \"", name, "\""]]; illegalName, otherError => ErrorReport[exp]; ENDCASE => ERROR; END; -- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -- bug in SetTiogaContents necessitates this KillFeedbackSel: PROC[v: Viewer] = BEGIN OPEN TiogaOps; who: Viewer _ GetSelection[feedback].viewer; IF who = v THEN CancelSelection[feedback]; END; -- read the user profile line for message forms and add one menu entry for each SetFormsMenu: PROC = BEGIN OPEN Menus; forms: LIST OF ROPE = UserProfile.ListOfTokens[key: "WalnutSend.MsgForms", default: NIL]; formsMenu _ Menus.CreateMenu[]; FOR fl: LIST OF ROPE _ forms, fl.rest UNTIL fl = NIL DO form: ROPE _ fl.first; shortName, fullName: ROPE; cp: FS.ComponentPositions; [fullName, cp, ] _ FS.ExpandName[form, NIL]; -- if the file is a remote one, then strip off the leading server/directory info shortName _ fullName.Substr[cp.base.start, cp.base.length]; -- add ".form" if one does not exist IF cp.ext.length = 0 THEN form _ form.Concat[".form"]; Menus.AppendMenuEntry[formsMenu, CreateEntry[shortName, SenderGetFormProc, form]] ENDLOOP; END; -- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -- Start code: create menus BEGIN OPEN Menus; AppendMenuEntry[sendMenu, CreateEntry[name: "Send", proc: MessageSendProc, fork: FALSE]]; AppendMenuEntry[sendMenu, CreateEntry["Get", SenderGetFileProc]]; AppendMenuEntry[sendMenu, CreateEntry[name: "Store", proc: SenderStoreProc, guarded: TRUE]]; AppendMenuEntry[sendMenu, CreateEntry["Save", SenderSaveProc]]; AppendMenuEntry[sendMenu, CreateEntry["Forms", SenderFormsProc]]; AppendMenuEntry[sendMenu, CreateEntry["New", NewSenderProc]]; AppendMenuEntry[sendMenu, CreateEntry["Default", NewMsgFormProc]]; AppendMenuEntry[sendMenu, CreateEntry["PrevMsg", PrevMsgProc]]; AppendMenuEntry[sendMenu, CreateEntry["Split", SenderSplitProc]]; AppendMenuEntry[sendMenu, CreateEntry["Places", SenderPlacesProc]]; AppendMenuEntry[sendMenu, CreateEntry["Levels", SenderLevelsProc]]; AppendMenuEntry[sendingMenu, CreateEntry["StopSending!", AbortSendProc]]; AppendMenuEntry[confirmMenu, CreateEntry["Confirm", ConfirmSendProc]]; AppendMenuEntry[confirmMenu, CreateEntry["Deny", DenySendProc]]; AppendMenuEntry[replyToMenu, CreateEntry["Self", ReplyToSelfProc]]; AppendMenuEntry[replyToMenu, CreateEntry["All", ReplyToAllProc]]; AppendMenuEntry[replyToMenu, CreateEntry["Cancel", ReplyToCancelProc]]; END; ---------------------------- -- start code; make menu and register ourselves Commander.Register["WalnutSend", WalnutSendMail, "Mail sending tool"]; UserProfile.CallWhenProfileChanges[WalnutSendInit]; DoUserNameAndRegistry[]; BEGIN path: ROPE = "Walnut.icons"; senderIcon _ Icons.NewIconFromFile[path, 6 ! FS.Error => GOTO notFound]; edittingIcon _ Icons.NewIconFromFile[path, 9]; EXITS notFound => NULL; END; END. |WalnutSendControlImpl.mesa Copyright c 1984 by Xerox Corporation. All rights reserved. Willie-Sue, January 10, 1986 11:21:58 am PST Contents: Control module for WalnutSend Last Edited by: Willie-Sue, January 17, 1984 10:20:02 am PST Last Edited by: Donahue, October 7, 1983 9:42 am ViewerOps.PaintViewer[senderV, menu]; -- shouldn't be necessary ViewerOps.PaintViewer[senderV, menu]; -- shouldn't be necessary removes the named button from the sender menu, if such a button exists; no errors ViewerOps.PaintViewer[viewer, menu]; -- shouldn't be necessary should take senderInfo off of chain of senderInfo's, byt why bother? Κ+Ε˜šΟb™Jšœ Οmœ1™˜>—Jšœ Ÿœ,˜:šœ Ÿœ:˜KJšœV˜V—JšœŸœ ˜Jšœ Ÿœ ˜JšœŸœ˜Jšœ ŸœP˜aJšœ Ÿœ˜'Jšœ˜Jšœ˜Jšœ˜—J˜šœŸœŸ˜$šŸ˜JšœŸœŸœ˜5Jšœ˜Jšœ˜Jšœ/˜/Jšœ<˜Ÿ œ˜I—š ŸœŸœŸœ"ŸœŸœŸ˜BJ˜Jšœ˜Jš ŸœŸœŸœ ŸœŸœ˜$Jšœ>˜>šŸœŸœŸœ˜Jšœ>Ÿ œ˜IJšœ.˜.J˜—JšŸœ˜—JšŸœ˜—J˜Jš X˜XJš ˜J˜š‘œŸœ˜&šŸœŸœŸœŸœ˜JšœŸœ ˜ Jšœ˜—JšŸœ˜—J˜š‘ œŸœ˜#šŸœŸœŸœŸœ˜JšœŸœ ˜ JšœŸœ+˜JJšŸœ ŸœŸœ˜6Jšœ˜JšŸœŸœŸœ%Ÿœ˜JšœŸ˜šœ˜Jšœ9˜9Jšœ& ™?Jšœ˜———JšŸœ˜—J˜š‘ œ˜šŸ˜Jšœ.Ÿœ ŸœŸœ˜fJšœ˜—JšŸœ˜—J˜š‘ œŸ œ˜+šŸ˜JšœŸœ+˜JJšœ$Ÿœ˜*Jšœ˜—JšŸœ˜—J˜Jš X˜XJ˜š‘œ˜+šŸ˜JšœŸœ+˜JJš ŸœŸœŸœŸœŸœ˜'JšŸœŸœŸœŸœ˜?—š k˜kJš Ÿœ3ŸœŸœŸœŸœ˜LJšŸœŸœ'˜CJšŸœŸœ˜ —JšŸœ˜—J˜š‘œ˜(šŸœŸœ˜JšœŸœ+˜JJšœ˜JšœŸœŸœ˜ Jš ŸœŸœŸœŸœŸœ˜'Jš Ÿœ:ŸœŸœŸœŸœ˜SJšœŸœ˜Jšœ=˜=JšœŸœ˜JšŸœŸœ%˜BJšŸœŸœ˜—JšŸœ˜—J˜š‘œŸœŸœ4˜OšŸœŸœŸœŸœ˜JšœŸœ˜#—JšŸœ˜—J˜š‘œ˜.šŸœŸœ˜JšœŸœ+˜JJšœ ŸœŸœŸœ˜JšœŸœŸœ˜ Jš ŸœŸœŸœŸœŸœ˜'Jš Ÿœ<ŸœŸœŸœŸœ˜UJšœ Ÿœ˜JšœG˜GJšœŸœ˜JšŸœŸœ-˜JJšŸœŸœ˜—JšŸœ˜—J˜š ‘œŸœŸœ%ŸœŸœŸœ˜QšŸœŸœŸœŸœ˜Jšœ/˜/—JšŸœ˜—J˜š‘œ˜)šŸ˜JšœŸœ+˜JJš ŸœŸœŸœŸœŸœ˜'šŸœŸ˜šœ=Ÿœ˜CJšŸœŸœ˜ —Jšœ˜—Jšœ ŸœŸœŸœ ˜EJšŸœŸœ˜—JšŸœ˜—J˜š‘œ˜-šŸœŸœ˜JšœŸœ+˜JJš ŸœŸœŸœŸœŸœ˜'Jšœ ˜-JšŸœŸœ˜—JšŸœ˜—J˜š‘œŸœŸœ+˜FšŸœŸœŸœŸœ˜Jšœ ŸœŸœ˜JšŸœŸœŸœ ˜+šŸœŸ˜šœ?˜?JšŸœ*ŸœŸœ˜L—Jšœ˜—JšŸœŸœŸœ˜J˜šŸœŸœŸ˜%JšœC˜C—J˜šŸœŸœŸ˜#Jšœ?˜?—J˜šŸœŸœŸ˜"Jšœ=˜=—J˜šŸœŸœŸ˜"Jšœ=˜=—J˜šŸœŸœŸ˜#JšœG˜G—J˜JšœŸœ˜JšœŸœ˜JšœŸœ˜JšœŸœ˜JšœŸœ˜J˜JšœD™DJ™JšŸœŸœ$˜@—JšŸœ˜—J˜š‘œŸœŸœŸœ˜3šŸœŸœŸœŸœ˜JšœŸœ+˜JJšŸœŸœŸœŸœ˜ JšœŸœ˜—JšŸœ˜—J˜š‘œŸœŸœŸœ˜6šŸœŸœŸœŸœ˜JšœŸœ+˜JJšŸœŸœŸœŸœ˜ JšœŸœ˜—JšŸœ˜—J˜š‘ œŸœŸœŸœŸœ ŸœŸœ˜AšŸœŸœŸœŸœ˜Jšœ˜—JšŸœ˜—J˜š ‘œŸœŸœŸœ ŸœŸœ˜?šŸ˜šŸœ ŸœŸ˜šœ˜Jšœ$Ÿœ"˜KJšœ(Ÿœ˜;šœ-˜-JšœBŸœ˜H——Jšœ˜—šŸœŸ˜ šœ; ˜TJšŸœ ŸœŸœ˜1—Jšœ˜—Jšœ˜—JšŸœ˜—J˜š‘œ˜)šŸ˜JšŸœŸœŸœ8˜QJšœŸœ˜Jšœ Ÿœ˜Jšœ Ÿœ˜JšŸœŸœ˜—JšŸœ˜—J˜š ‘œŸœŸœŸœŸœŸœ˜6šŸœŸœŸœŸœ˜šŸœ ŸœŸ˜šœ˜Jšœ7˜7JšœŸœ˜Jšœ"˜"Jšœ Ÿœ˜—Jšœ˜—Jšœ˜—JšŸœ˜—J˜š ‘œŸœŸœŸœŸœŸœ˜8šŸœŸœŸœŸœ˜JšŸœŸœ Ÿœ˜'—JšŸœ˜—J˜š‘ œŸœ˜$šŸœŸœŸœŸœ˜JšœŸœ ˜ JšœŸœ+˜JJšœŸœ˜JšœŸœ˜ JšŸ œ˜—JšŸœ˜—J˜š‘œŸœ˜'šŸœŸœŸœŸœ˜JšœŸœ ˜ JšœŸœ+˜JJšœŸœ˜JšœŸœ˜ JšŸ œ˜—JšŸœ˜—J˜š‘ œŸœ˜%šŸœŸœŸœŸœ˜JšœŸœ ˜ JšœŸœ+˜JJšœ%˜%JšœŸœ˜—JšŸœ˜—J˜š‘œŸœ˜'šŸœŸœŸœŸœ˜JšœŸœ ˜ JšœŸœ+˜JJšœ"˜"JšœŸœ˜ JšŸ œ˜—JšŸœ˜—J˜š‘œŸœ˜&šŸœŸœŸœŸœ˜JšœŸœ ˜ JšœŸœ+˜JJšœ!˜!JšœŸœ˜ JšŸ œ˜—JšŸœ˜—J˜š‘œŸœ˜)šŸœŸœŸœŸœ˜JšœŸœ ˜ JšœŸœ+˜JJšœ$˜$JšœŸœ˜ JšŸ œ˜—JšŸœ˜—J˜Jš ˜š ‘œŸœŸœŸœŸœ˜SšŸœŸœŸœŸœ˜Jšœ%˜%JšœŸœŸœŸœ˜>JšŸœŸœŸœŸœ˜=JšœŸœ˜!JšŸœ Ÿœ˜*——J˜š ‘œŸœŸœŸœŸœŸœ˜LšŸœŸœŸœŸœ˜šŸœŸ˜šœ-˜-JšŸœŸœŸœ%˜FJšŸœŸœ%Ÿœ˜E—Jšœ˜—JšŸœ˜—JšŸœ˜—J˜š ‘ œŸœŸœŸœŸœŸœ˜HJš œŸœŸœŸœŸœ˜>—J˜š ‘œŸœŸœŸœŸœ˜FšŸ˜Jšœ%˜%Jšœ4˜4JšœŸœŸœŸœ˜>JšŸœŸœŸœŸœ˜=Jšœ!˜!JšœŸœ˜!JšŸœ˜—JšŸœ˜—J˜š‘ œŸœŸœ˜JšŸ˜Jšœ˜Jšœ˜—JšŸœ˜—J˜š‘œŸœ˜(šŸ˜JšœŸœ ˜Jšœ Ÿœ+˜:Jšœ#˜#JšŸœ ŸœŸœ˜*JšŸœŸœ*˜F—JšŸœ˜—J˜š‘œŸœ<Ÿœ˜QšŸœŸœ ˜JšœŸœ˜ JšœŸœ+˜JJšœŸœ˜Jšœ˜Jšœ˜JšœŸœ˜J˜JšœB˜BJšŸœ Ÿœ˜)J˜Jšœ˜Jšœ˜Jšœ˜Jšœ$˜$šŸœŸ˜šœ‘œŸœŸœ˜šŸœŸœŸœŸœ˜Jšœ$Ÿœ˜+—JšŸœ˜JšŸœŸœŸœŸœ˜NJšœ˜Jšœ˜—Jšœ˜——JšŸœ˜—J˜š‘œ˜!šŸ˜JšœŸœ ˜Jšœ ˜ Jšœ˜Jšœ˜—š ;˜;JšŸœŸœŸœŸœ˜BJ˜Jšœ Ÿœ)˜—JšŸœ˜—J˜š‘œ˜!šŸ˜JšœŸœ ˜JšœŸœ&˜9Jšœ˜Jšœ Ÿœ˜Jšœ'˜'šŸœŸ˜šœ˜šœ%˜%JšŸœŸœ˜1———JšŸœ*˜1—JšŸœ˜—J˜š‘œ˜ šŸ˜JšœŸœ ˜Jšœ˜Jšœ Ÿœ˜JšŸœ ŸœŸœ1Ÿœ˜PJ˜Jšœ(˜(šŸœŸ˜šœ˜JšœŸœŸœ˜M——JšŸœ+˜2—JšŸœ˜—J˜Jš H˜HJ˜š ‘œŸœŸœ ŸœŸœ˜DšŸ˜Jšœ˜JšœŸœ˜J˜Jšœ,Ÿœ˜3šŸœŸœŸœ˜;Jšœ. ˜DJšœ˜J˜JšŸœ˜—Jšœ˜šŸœŸœŸ˜)šœŸœ <˜OJšœ%˜%Jšœ&  ˜FšŸœŸ˜šœ˜Jšœ%˜%Jšœ Ÿœ "˜4—Jšœ˜—Jšœ˜Jšœ˜—Jšœ˜—JšŸœ˜—JšŸœ˜—J˜š ‘ œŸœŸœ ŸœŸœ˜3JšœŸœ-Ÿœ˜<šŸ˜JšœŸœ ˜JšœŸœ˜JšœŸœ˜Jšœ ˜ šŸœŸœŸœ ˜šœŸœŸ˜šœŸœ Ÿ˜Jšœ)˜)šœ˜Jšœ ŸœŸœŸœ˜J—JšŸœ˜#—JšŸœ˜Jšœ ˜ JšŸ˜—Jšœ˜—J˜JšœŸœŸœ˜!JšŸœŸœŸœŸœ˜!J˜Jš œŸœŸœŸœŸœ˜LJšŸœŸœŸœ˜IJšœŸœ ˜JšŸœŸœŸœŸœ ˜JšŸœ˜—JšŸœ˜—J˜š‘ œŸœŸœŸœ˜$šœŸœŸœŸœ˜JšœŸœ˜Jšœ˜Jšœ˜—Jšœ˜—J˜š‘œŸœ Ÿœ˜;šŸ˜šŸœŸ˜šœ˜JšœD˜D—Jšœ,˜,—JšŸœŸœ˜—JšŸœ˜J˜—Jš H˜HJš -˜-J˜š‘œŸœ ˜"šŸœŸœ ˜Jšœ,˜,JšŸœ Ÿœ˜*—JšŸœ˜—J˜Jš O˜OJ˜š‘ œŸœ˜šŸœŸœ˜Jš œŸœŸœŸœAŸœ˜YJšœ˜J˜š ŸœŸœŸœŸœŸœŸœŸ˜7JšœŸœ ˜JšœŸœ˜JšœŸœ˜J˜JšœŸœŸœ˜,—š P˜PJšœ;˜;—š $˜$JšŸœŸœ˜6J˜JšœQ˜Q—JšŸœ˜—JšŸœ˜—J˜Jš H˜HJš ˜J˜šŸ˜JšŸœ˜ J˜JšœSŸœ˜[JšœA˜AJšœWŸœ˜^Jšœ?˜?JšœA˜AJšœ=˜=JšœB˜BJšœ?˜?JšœA˜AJšœC˜CJšœC˜CJ˜JšœI˜IJ˜JšœF˜FJšœ@˜@J˜JšœC˜CJšœA˜AJšœG˜G—JšŸœ˜J˜Jš ˜Jš 0˜0J˜JšœF˜FJšœ3˜3˜J˜šŸ˜JšœŸœ˜Jšœ-Ÿœ Ÿœ ˜HJšœ.˜.šŸ˜Jšœ Ÿœ˜——JšŸœ˜—J˜JšŸœ˜—…—š½Ϋ