TopButton.mesa
Copyright Ó 1986, 1990, 1992 by Xerox Corporation. All rights reserved.
Bloomenthal, November 21, 1990 0:32 am PST
Brent Welch December 12, 1990 12:56 pm PST
Weiser, August 29, 1993 10:51 pm PDT
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];
Get prefix of current selection before the first CR
curSel ¬ Rope.Substr[base: curSel, start: 0, len: curSel.Index[s2: "\n"]];
The curSel is the fileName if curSel is longer than one character and contains no whitespace.
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];
The curSel is the spaceFileName if curSel is longer than one character.
spaceFileName ¬ IF (curSel.Length[] > 1) THEN curSel ELSE fileName;
shortSpaceFileName ¬ GetShortName[spaceFileName];
Add quotes for the quoted version.
quotedFileName ¬ Rope.Cat["\"", spaceFileName, "\""];
shortQuotedFileName ¬ Rope.Cat["\"", shortSpaceFileName, "\""];
IF Rope.SkipTo[def, 0, "$"] < Rope.Length[def] THEN {
It is likely that we have substitutions to do
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] = {
if old is not found in base, then value = base.
if allOccurrences THEN substitute for each occurrence of old, otherwise only for first.
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.