<> <> <> <> <> <> <> DIRECTORY ACFind USING [ActionProc, Create, Find, Ref], Basics USING [CompareCard], Commander USING [CommandProc, Register], CommandTool USING [ParseToList], Convert USING [IntFromRope, RopeFromInt], FS USING [Error], MBQueue USING [Create, Queue], MessageWindow USING [Append, Blink], Process USING [Detach], Rope USING [Cat, Equal, Fetch, Length, Match, ROPE], SymTab USING [Create, EachPairAction, Fetch, Insert, Pairs, Ref], UserCredentials USING [Get], UserProfile USING [Boolean, ListOfTokens], ViewerTools USING [TiogaContents], WalnutOps USING [AddMsg, CreateMsgSet, GetDisplayProps, GetMsg, MsgSetNames, MsgSetsInfo, MsgsInSetEnumeration, MoveMsg], WalnutSortDB, WalnutWindow USING [QueueCall, SelectMsgSetsFromMSNames], WalnutWindowInternal USING [Report], WalnutRegistry USING [EventProc, MsgProc, ProcSet, Register], WalnutSortMail; WalnutSortMailImpl: CEDAR MONITOR IMPORTS ACFind, Basics, Commander, CommandTool, Convert, FS, MBQueue, MessageWindow, Process, Rope, SymTab, UserCredentials, UserProfile, WalnutOps, WalnutRegistry, WalnutSortDB, WalnutWindow, WalnutWindowInternal EXPORTS WalnutSortMail = BEGIN ROPE: TYPE ~ Rope.ROPE; TriggerSet: TYPE ~ WalnutSortDB.TriggerSet; Trigger: TYPE ~ WalnutSortDB.Trigger; Error: PUBLIC ERROR [info: ROPE] = CODE; EachMsg: WalnutRegistry.MsgProc ~ { <> IF sortEnabled AND event=firstRead THEN SortMsg[IF UserProfile.Boolean[key: "WalnutSort.SelectOnTOC"] THEN WalnutOps.GetDisplayProps[msg: msgName].TOCentry ELSE WalnutOps.GetMsg[msgName].contents.contents]; }; SortMsg: PUBLIC PROC [msg: ROPE] ~ { EachKeyword: ACFind.ActionProc ~ { trig: REF Trigger _ NARROW[SymTab.Fetch[ts.mapping, keyFound].val]; SELECT Basics.CompareCard[trig.priority, highestPriorityFound] FROM less => RETURN; greater => { highestPriorityFound _ trig.priority; msNamesToActivate _ SymTab.Create[]; }; ENDCASE => NULL; FOR each: LIST OF ROPE _ trig.msNames, each.rest UNTIL each=NIL DO [] _ SymTab.Insert[msNamesToActivate, each.first, NIL]; ENDLOOP; }; EachMSName: SymTab.EachPairAction = { <<[key: SymTab.Key, val: SymTab.Val] RETURNS [quit: BOOL]>> msNamesToActivateList _ CONS[key, msNamesToActivateList]; RETURN[FALSE]; }; highestPriorityFound: CARDINAL _ 0; msNamesToActivate: SymTab.Ref; msNamesToActivateList: LIST OF ROPE _ NIL; finder: ACFind.Ref _ NIL; ts: TriggerSet _ GetTriggerSet[]; <> finder _ NARROW[ts.clientData]; IF ACFind.Find[finder, msg, EachKeyword] THEN [] _ SymTab.Pairs[msNamesToActivate, EachMSName] ELSE <> msNamesToActivateList _ UserProfile.ListOfTokens["WalnutSort.DefaultSelections"]; IF msNamesToActivateList # NIL THEN [] _ WalnutWindow.SelectMsgSetsFromMSNames[msNamesToActivateList]; }; ReSort: PUBLIC PROC [pattern, fromMsgSets: LIST OF ROPE, newMsgSet: ROPE, removeFromOld, checkOnlyTitle: BOOLEAN _ FALSE] RETURNS [msgsMoved: LIST OF ROPE _ NIL] ~ { <> patternRE: ACFind.Ref ~ ACFind.Create[keys: pattern, caseSensitive: FALSE]; <> msgSets: LIST OF ROPE _ NIL; count: INT _ 0; CMSProc: PROC[] RETURNS[doReset: BOOL] = { doReset _ TRUE; WalnutOps.CreateMsgSet[newMsgSet, WalnutOps.MsgSetsInfo[].version]; <> FOR mss: LIST OF ROPE _ WalnutOps.MsgSetNames[].mL, mss.rest UNTIL mss=NIL DO <> <<[position: INT, keyFound: ROPE] RETURNS [quit: BOOL _ FALSE]>> <> <> <<};>> <> <> FOR each: LIST OF ROPE _ fromMsgSets, each.rest UNTIL each=NIL DO IF Rope.Match[pattern: each.first, object: mss.first, case: FALSE] THEN { msgSets _ CONS[mss.first, msgSets]; EXIT; }; ENDLOOP; ENDLOOP; <> WalnutWindowInternal.Report["Messages moved:"]; FOR mss: LIST OF ROPE _ msgSets, mss.rest UNTIL mss=NIL DO ms: ROPE ~ mss.first; FOR msgs: LIST OF ROPE _ WalnutOps.MsgsInSetEnumeration[ms].mL, msgs.rest UNTIL msgs=NIL DO FindKeyInText: ACFind.ActionProc = { <<[position: INT, keyFound: ROPE] RETURNS [quit: BOOL _ FALSE]>> RETURN [TRUE] }; herald, text: ROPE; herald _ text _ WalnutOps.GetDisplayProps[msgs.first].TOCentry; IF ~checkOnlyTitle THEN { text _ WalnutOps.GetMsg[msgs.first].contents.contents; }; IF ACFind.Find[self: patternRE, text: text, action: FindKeyInText] THEN { count _ count+1; msgsMoved _ CONS[msgs.first, msgsMoved]; WalnutWindowInternal.Report[" ", herald]; IF removeFromOld THEN [] _ WalnutOps.MoveMsg[msgs.first, [ms], [newMsgSet]] ELSE [] _ WalnutOps.AddMsg[msgs.first, [ms], [newMsgSet]]; }; ENDLOOP; ENDLOOP; WalnutWindowInternal.Report[Convert.RopeFromInt[from: count], " messages moved."]; }; [] _ WalnutWindow.QueueCall[CMSProc] }; WatchWalnut: WalnutRegistry.EventProc = { <<[event: WalnutRegistry.Event, clientData: REF ANY]>> SELECT event FROM stopped => WalnutSortDisable[]; started => IF Rope.Equal[user, UserCredentials.Get[].name, FALSE] THEN WalnutSortEnable[]; ENDCASE => NULL; }; user: ROPE _ NIL; sortEnabled: BOOLEAN _ FALSE; queue: MBQueue.Queue ~ MBQueue.Create[]; procSet: WalnutRegistry.ProcSet ~ [ eventProc: WatchWalnut, msgProc: EachMsg ]; WalnutSortEnable: PUBLIC ENTRY PROC ~ { ENABLE UNWIND => NULL; <> IF NOT sortEnabled THEN { currentUser: ROPE _ UserCredentials.Get[].name; IF NOT Rope.Equal[currentUser, user, FALSE] THEN { user _ currentUser; WalnutSortDB.DeclareSegment[user]; }; sortEnabled _ TRUE; TRUSTED {Process.Detach[FORK MakeWalnutSortGetTriggerSet[]];} }; }; WalnutSortDisable: PUBLIC ENTRY PROC ~ { ENABLE UNWIND => NULL; <> WalnutSortDB.Close[]; sortEnabled _ FALSE; }; LoadDefinitions: Commander.CommandProc ~ { tokens: LIST OF ROPE ~ CommandTool.ParseToList[cmd].list; usageMessage: ROPE ~ "Usage: WSLoad .\n"; SELECT TRUE FROM tokens=NIL => msg _ usageMessage; ENDCASE => WalnutSortDB.LoadFromFile[tokens.first ! FS.Error => {msg _ error.explanation; GOTO Complain}; ANY => {msg _ "Failed.\n"; GOTO Complain} ]; EXITS Complain => {RETURN [$Failure, msg]}; }; DumpDefinitions: Commander.CommandProc ~ { tokens: LIST OF ROPE ~ CommandTool.ParseToList[cmd].list; usageMessage: ROPE ~ "Usage: WSDump .\n"; SELECT TRUE FROM tokens=NIL => msg _ usageMessage; ENDCASE => WalnutSortDB.DumpToFile[tokens.first ! FS.Error => {msg _ error.explanation; GOTO Complain}; ANY => {msg _ "Failed.\n"; GOTO Complain} ]; EXITS Complain => {RETURN [$Failure, msg]}; }; MakeFinder: PROC [mapping: SymTab.Ref] RETURNS [finder: ACFind.Ref] ~ { BuildKeyList: SymTab.EachPairAction = { <<[key: SymTab.Key, val: SymTab.Val] RETURNS [quit: BOOL]>> keyList _ CONS[key, keyList]; RETURN[FALSE]; }; keyList: LIST OF ROPE _ NIL; [] _ SymTab.Pairs[x: mapping, action: BuildKeyList]; finder _ ACFind.Create[keyList]; }; GetTriggerSet: PROC RETURNS [ts: TriggerSet] ~ { ts _ WalnutSortDB.GetTriggerSetFromDB[]; IF ts.clientData = NIL THEN ts.clientData _ MakeFinder[ts.mapping]; }; MakeWalnutSortGetTriggerSet: PROC ~ { [] _ GetTriggerSet[ ! Error => { MessageWindow.Append[info, TRUE]; MessageWindow.Blink[]; CONTINUE }]; }; WalnutSort: Commander.CommandProc ~ { tokens: LIST OF ROPE ~ CommandTool.ParseToList[cmd].list; usageMessage: ROPE ~ "Usage: WalnutSort on|off.\n"; SELECT TRUE FROM tokens=NIL OR Rope.Equal[tokens.first, "on", FALSE] => { WalnutSortEnable[]; msg _ "Sort enabled.\n"; }; Rope.Equal[tokens.first, "off", FALSE] => {WalnutSortDisable[]; msg _ "Sort disabled.\n"}; ENDCASE => msg _ usageMessage; }; resortMsg: ROPE ~ "WSResort [-[m][t]] _ IN ."; ResortCmd: Commander.CommandProc ~ { arg: LIST OF ROPE _ CommandTool.ParseToList[cmd].list; pattern: LIST OF ROPE _ NIL; to: ROPE; deleteFromOld, checkOnlyTitle: BOOL _ FALSE; IF arg = NIL THEN GOTO NoPattern; IF Rope.Fetch[arg.first]='- THEN { FOR k: NAT IN [0.. NAT[Rope.Length[arg.first]]) DO SELECT Rope.Fetch[arg.first, k] FROM 'm => deleteFromOld _ TRUE; 't => checkOnlyTitle _ TRUE; ENDCASE; ENDLOOP; arg _ arg.rest; }; IF arg = NIL OR arg.rest = NIL OR NOT Rope.Equal[arg.rest.first, "_"] THEN GOTO NoPattern; to _ arg.first; arg _ arg.rest.rest; IF Rope.Equal[s1: arg.first, s2: "*"] THEN { pattern _ LIST[""]; arg _ arg.rest; } ELSE UNTIL arg=NIL OR Rope.Equal[s1: arg.first, s2: "IN"] DO pattern _ CONS[arg.first, pattern]; arg _ arg.rest; ENDLOOP; IF arg = NIL OR arg.rest = NIL OR NOT Rope.Equal[s1: arg.first, s2: "IN"] THEN GOTO NoPattern; <> [] _ ReSort[pattern, arg.rest --from--, to, deleteFromOld, checkOnlyTitle]; EXITS NoPattern => RETURN [msg: resortMsg]; }; SetTrigger: Commander.CommandProc ~ { tokens: LIST OF ROPE _ CommandTool.ParseToList[cmd].list; priority: INT _ 10; usageMessage: ROPE ~ "Usage: WSSetTrigger [-priority] keyword messageSetName.\n"; SELECT TRUE FROM tokens=NIL => RETURN [msg: usageMessage]; Rope.Fetch[tokens.first]='- => { priority _ -Convert.IntFromRope[tokens.first]; tokens _ tokens.rest; }; ENDCASE; IF tokens=NIL OR tokens.rest=NIL THEN RETURN [msg: usageMessage]; WalnutSortDB.DeleteTrigger[tokens.rest.first, tokens.first]; WalnutSortDB.AddTrigger[tokens.rest.first, tokens.first, priority]; }; DeleteTrigger: Commander.CommandProc ~ { tokens: LIST OF ROPE ~ CommandTool.ParseToList[cmd].list; usageMessage: ROPE ~ "Usage: WSDeleteTrigger keyword messageSetName.\n"; IF tokens=NIL OR tokens.rest=NIL THEN RETURN [msg: usageMessage]; WalnutSortDB.DeleteTrigger[tokens.rest.first, tokens.first]; }; ListTriggers: Commander.CommandProc ~ { WalnutSortDB.DumpToStream[cmd.out]; }; Init: PROC ~ { OPEN Commander; Register["WalnutSort", WalnutSort, "WalnutSort on|off --Enable/disable auto message set button selection"]; Register["WSDump", DumpDefinitions, "Dump the Walnut Sort Definitions (WSDump filename)."]; Register["WSLoad", LoadDefinitions, "Load the Walnut Sort Definitions (WSLoad filename)."]; Register["WSSetTrigger", SetTrigger, "Create a keyword trigger (WSSetTrigger [-priority] keyword messageSetName)."]; Register["WSDeleteTrigger", DeleteTrigger, "Remove a keyword trigger (WSDeleteTrigger keyword messageSetName)."]; Register["WSListTriggers", ListTriggers, "List current triggers."]; Register["WSResort", ResortCmd, Rope.Cat["Resort Walnut messages into another message set. (", resortMsg, ")."]]; [] _ WalnutRegistry.Register[procSet: procSet, queue: queue]; }; Init[]; END. <> <> <> <> <> <<>>