<<>> <> <> <> <> <> <<>> DIRECTORY Buttons, Commander, CommanderOps, Convert, Rope, IO, Menus, SimpleFeedback, TiogaOps, ViewerClasses, ViewerTools; TopButton: CEDAR PROGRAM IMPORTS Buttons, Commander, CommanderOps, Convert, IO, Rope, SimpleFeedback, TiogaOps, ViewerTools ~ BEGIN ROPE: TYPE ~ Rope.ROPE; ButtonImplRef: TYPE = REF ButtonImplObject; ButtonImplObject: TYPE = RECORD [ name: ROPE, def: ROPE, button: Buttons.Button ¬ NIL ]; DoButtonProc: TYPE = PROC [br: ButtonImplRef]; topButtonList: LIST OF ButtonImplRef ¬ NIL; TopButton: Commander.CommandProc ~ { s: IO.STREAM = IO.RIS[cmd.commandLine]; br: ButtonImplRef; button: Buttons.Button; name: ROPE = (IO.GetCedarTokenRope[s]).token; cmdline: ROPE = IO.GetRope[s]; br ¬ NEW[ButtonImplObject ¬ [name: name, def: cmdline]]; button _ Buttons.Create[info: [name: name], proc: TopButtonImpl, clientData: br, paint: FALSE]; Buttons.SetDisplayStyle[button, $BlackOnWhite]; br.button _ button; topButtonList ¬ CONS[br, topButtonList]; }; SimpleTopButtonImpl: Buttons.ButtonProc ~ { msg: ROPE = CommanderOps.DoCommandRope[commandLine: NARROW[clientData], parent: NIL].out; SimpleFeedback.Append[$TopButton, oneLiner, $Feedback, msg]; }; FindTopbuttons: PROC [pattern: ROPE, doEachButton: DoButtonProc] ~ { FOR brList: LIST OF ButtonImplRef ¬ topButtonList, brList.rest UNTIL brList=NIL DO IF Rope.Match[pattern, brList.first.name, TRUE] THEN doEachButton[brList.first]; ENDLOOP; }; DestroyButtonImpl: Commander.CommandProc ~ { s: IO.STREAM = IO.RIS[cmd.commandLine]; pattern: ROPE = (IO.GetCedarTokenRope[s]).token; didDestroy: BOOL ¬ FALSE; doButton: DoButtonProc = {Buttons.Destroy[br.button]; didDestroy ¬ TRUE;}; FindTopbuttons[pattern, doButton]; IF NOT didDestroy THEN { msg: ROPE ¬ IO.PutFR1["could not find button matching `%g'.", IO.rope[pattern]]; SimpleFeedback.Append[$TopButton, oneLiner, $Feedback, msg]; }; }; ReplaceButtonImpl: Commander.CommandProc ~ { s: IO.STREAM = IO.RIS[cmd.commandLine]; name: ROPE = (IO.GetCedarTokenRope[s]).token; cmdline: ROPE = IO.GetRope[s]; didReplace: BOOL ¬ FALSE; doButton: DoButtonProc = { didReplace ¬ TRUE; br.def ¬ cmdline; }; FindTopbuttons[name, doButton]; IF NOT didReplace THEN { msg: ROPE ¬ IO.PutFR1["could not find button matching `%g'.", IO.rope[name]]; SimpleFeedback.Append[$TopButton, oneLiner, $Feedback, msg]; }; }; TopButtonImpl: Menus.ClickProc = { <<[parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]>> WITH clientData SELECT FROM br: ButtonImplRef => { def: ROPE = br.def; msg: ROPE = CommanderOps.DoCommandRope[commandLine: ButtonCommandToRope[parent, def, mouseButton, shift, control], parent: NIL].out; SimpleFeedback.Append[$TopButton, oneLiner, $Feedback, msg]; }; ENDCASE; }; ButtonCommandToRope: PROC [parent: ViewerClasses.Viewer, cmdIn: ROPE ¬ NIL, mouseButton: ViewerClasses.MouseButton ¬ red, shift, control: BOOL ¬ FALSE] RETURNS [def: ROPE] = { <<[mimics ViewerClasses.ClickProc, but with different return value]>> curSel, escaped, spaced: ROPE; viewer: ViewerClasses.Viewer ¬ NIL; start: TiogaOps.Location; viewerName: ROPE ¬ NIL; fileName: ROPE ¬ NIL; shortFileName: ROPE ¬ NIL; baseFileName: ROPE ¬ NIL; spaceFileName: ROPE ¬ NIL; shortSpaceFileName: ROPE ¬ NIL; quotedFileName: ROPE ¬ NIL; shortQuotedFileName: ROPE ¬ NIL; index: INT ¬ -1; controlRope: ROPE ¬ IF control THEN "control" ELSE "noControl"; shiftRope: ROPE ¬ IF shift THEN "shift" ELSE "noShift"; buttonRope: ROPE; SELECT mouseButton FROM red => buttonRope ¬ "left"; yellow => buttonRope ¬ "middle"; blue => buttonRope ¬ "right"; ENDCASE => ERROR; IF cmdIn = NIL THEN RETURN; def ¬ cmdIn; [viewer: viewer, start: start] ¬ TiogaOps.GetSelection[primary]; curSel ¬ ViewerTools.GetSelectionContents[]; IF viewer # NIL AND NOT viewer.destroyed AND NOT viewer.newFile THEN { root: TiogaOps.Ref ¬ TiogaOps.Root[start.node]; offset: INT ¬ TiogaOps.LocOffset[loc1: [root, 0], loc2: start, skipCommentNodes: TRUE]; index ¬ offset; viewerName ¬ viewer.file; IF viewerName = NIL THEN viewerName ¬ viewer.name; fileName ¬ viewer.file; fileName ¬ Rope.Substr[fileName, 0, Rope.SkipTo[fileName, 0, "!"]]; }; escaped ¬ Convert.RopeFromRope[from: curSel, quote: FALSE]; spaced ¬ RopeSubst[old: "\n", new: " ", base: curSel]; <> curSel ¬ Rope.Substr[base: curSel, start: 0, len: curSel.Index[s2: "\n"]]; <> fileName ¬ IF (Rope.SkipTo[s: curSel, pos: 0, skip: " \t"] = curSel.Length[]) AND (curSel.Length[] > 1) THEN curSel ELSE fileName; shortFileName ¬ GetShortName[fileName]; baseFileName ¬ GetBaseName[shortFileName]; <> spaceFileName ¬ IF (curSel.Length[] > 1) THEN curSel ELSE fileName; shortSpaceFileName ¬ GetShortName[spaceFileName]; <> quotedFileName ¬ Rope.Cat["\"", spaceFileName, "\""]; shortQuotedFileName ¬ Rope.Cat["\"", shortSpaceFileName, "\""]; IF Rope.SkipTo[def, 0, "$"] < Rope.Length[def] THEN { <> def ¬ RopeSubst[old: "$CurrentSelection$", new: curSel, base: def, case: TRUE]; def ¬ RopeSubst[old: "$CurrentEscapedSelection$", new: escaped, base: def, case: TRUE]; def ¬ RopeSubst[old: "$CurrentSpacedSelection$", new: spaced, base: def, case: TRUE]; def ¬ RopeSubst[old: "$FileNameSelection$", new: fileName, base: def, case: TRUE]; def ¬ RopeSubst[old: "$ShortFileNameSelection$", new: shortFileName, base: def, case: TRUE]; def ¬ RopeSubst[old: "$BaseFileNameSelection$", new: baseFileName, base: def, case: TRUE]; def ¬ RopeSubst[old: "$SpaceFileNameSelection$", new: spaceFileName, base: def, case: TRUE]; def ¬ RopeSubst[old: "$ShortSpaceFileNameSelection$", new: shortSpaceFileName, base: def, case: TRUE]; def ¬ RopeSubst[old: "$QuotedFileNameSelection$", new: quotedFileName, base: def, case: TRUE]; def ¬ RopeSubst[old: "$ShortQuotedFileNameSelection$", new: shortQuotedFileName, base: def, case: TRUE]; def ¬ RopeSubst[old: "$SelectedViewerName$", new: viewerName, base: def, case: TRUE]; def ¬ RopeSubst[old: "$ViewerPosition$", new: Convert.RopeFromInt[index, 10, FALSE], base: def, case: TRUE]; def ¬ RopeSubst[old: "$MouseButton$", new: buttonRope, base: def, case: TRUE]; def ¬ RopeSubst[old: "$ControlKey$", new: controlRope, base: def, case: TRUE]; def ¬ RopeSubst[old: "$ShiftKey$", new: shiftRope, base: def, case: TRUE]; }; RETURN [def]; }; RopeSubst: PROC [old, new, base: ROPE, case: BOOL ¬ FALSE, allOccurrences: BOOL ¬ TRUE] RETURNS [ROPE] = { <> <> lenOld: INT = old.Length[]; lenNew: INT = new.Length[]; i: INT ¬ 0; WHILE (i ¬ Rope.Find[s1: base, s2: old, case: case, pos1: i]) # -1 DO base ¬ Rope.Replace[base: base, start: i, len: lenOld, with: new]; IF ~allOccurrences THEN EXIT; i ¬ i + lenNew; ENDLOOP; RETURN[base]; }; GetShortName: PROC [path: ROPE] RETURNS [ROPE] = { len: INT ¬ Rope.Length[path]; bang: INT ¬ len; pos: INT ¬ len; WHILE pos # 0 DO np: INT ¬ pos - 1; c: CHAR ¬ Rope.Fetch[path, np]; SELECT c FROM '! => bang ¬ np; '>, '], '/ => RETURN [Rope.Substr[path, pos, bang-pos]]; ENDCASE; pos ¬ np; ENDLOOP; RETURN [Rope.Substr[path, 0, bang]]; }; GetBaseName: PROC [shortName: ROPE] RETURNS [ROPE] = { dotPos: INT ¬ Rope.Find[shortName, "."]; IF dotPos<0 THEN RETURN [shortName] ELSE RETURN [Rope.Substr[shortName, 0, dotPos]] }; Commander.Register["DestroyTopButton", DestroyButtonImpl, "Destroy all top buttons whose name match the argument (*'s permitted, case-sensitive)"]; Commander.Register["ReplaceTopButton", ReplaceButtonImpl, "Replace the command strings of those top buttons whose name match the argument"]; Commander.Register["TopButton", TopButton, "Install a new top-of-screen button. Prototype: TopButton buttonname command substitutions are: $CurrentSelection$ => replaced by the current selection up to but not including the first carriage return $CurrentEscapedSelection$ => replaced by a rope literal (minus the surrounding quotes) for the entire current selection $CurrentSpacedSelection$ => replaced by the whole current selection, with spaces substituted for newlines $FileNameSelection$ => replaced by the current selection if it appears to be a file name, otherwise replaced by the name of the selected viewer $ShortFileNameSelection$ => same as $FileNameSelection$ except that version number and directory are omitted $BaseFileNameSelection$ => same as $ShortFileNameSelection$ except that extensions are ommited $SpaceFileNameSelection$ => replaced by the current selection if it appears to be a file name (may include white space), otherwise replaced by the name of the selected viewer $ShortSpaceFileNameSelection$ => same as $SpaceFileNameSelection$ except that version number and directory are omitted $QuotedFileNameSelection$ => replaced by the current selection if it appears to be a file name (may include white space), otherwise replaced by the name of the selected viewer. The results will have double quotes around it. $ShortQuotedFileNameSelection$ => same as $QuotedFileNameSelection$ except that version number and directory are omitted. $SelectedViewerName$ => replaced by the name of the selected viewer $ViewerPosition$ => replaced by the position of the current selection in a viewer $MouseButton$ => left|middle|right $ShiftKey$ => shift|noShift $ControlKey$ control|noControl "]; END.