EditToolMiscImpl.mesa
Copyright (C) 1982, 1983, 1984, 1985, 1986 by Xerox Corporation. All rights reserved.
Doug Wyatt, January 9, 1985 4:22:22 pm PST
Michael Plass, May 9, 1986 1:54:42 pm PDT
Rick Beach, May 28, 1985 1:54:05 pm PDT
Russ Atkinson (RRA) June 18, 1985 4:58:04 pm PDT
DIRECTORY
Atom USING [GetPName, MakeAtom, PropList],
Buttons USING [ButtonProc],
EditToolBuilder USING [BuildButton, BuildDataFieldPair, BuildPair, BuildTriple, DataFieldButton, GetDataNode, HGap, ToMiddle, ToNext, VGap],
EditToolPrivate USING [ChangeState, CheckPSel, CycleTriple, DoButton, Info, mainToolInfo, NodeKind, Register, tSel],
Labels USING [Set],
MessageWindow USING [Append, Blink],
NodeProps USING [DoSpecs, GetProp, GetSpecs, MapProps],
NodePropsExtras USING [Is],
NodeStyleOps USING [StyleNameForNode],
Process USING [Detach],
Rope USING [Concat, Equal, Fetch, ROPE, Size, Substr],
RopeEdit USING [AlphaNumericChar, BlankChar],
RuntimeError USING [UNCAUGHT],
TEditDocument USING [Selection, SelectionPoint],
TEditInput USING [CloseEvent, CommandProc, CurrentEvent],
TEditInputOps USING [CheckReadonly, LoadAbbreviations, ReloadStyleName, SetStyleName, SetFormatName],
TEditLocks USING [Lock],
TEditOps USING [GetSelData, SetTextContents],
TEditRefresh USING [ScrollToEndOfSel],
TEditSelection USING [LockSel, MakeSelection, pSel, SelectionRoot, UnlockDocAndPSel, UnlockSel],
TextEdit USING [GetProp, GetRope, PutProp, Size, GetCharProp, PutCharProp, GetCharPropList],
TextFind USING [Finder, MalformedPattern, PatternErrorCode, Try],
TextNode USING [NodeItself, NewTextNode, Parent, Ref, RefTextNode, Root, StepBackward, StepForward],
TreeFind USING [CreateFromRope, Finder],
ViewerClasses USING [Viewer],
ViewerOps USING [EnumerateChildren, EnumerateViewers, PaintViewer];
EditToolMiscImpl: CEDAR PROGRAM
IMPORTS EditToolPrivate, EditToolBuilder, Atom, TEditOps, Labels, MessageWindow, NodeProps, NodePropsExtras, NodeStyleOps, Process, Rope, RopeEdit, RuntimeError, TextEdit, TextFind, TextNode, TEditSelection, TEditRefresh, TEditInput, TEditInputOps, TEditLocks, TreeFind, ViewerOps
EXPORTS EditToolPrivate
= BEGIN
ROPE: TYPE ~ Rope.ROPE;
BuildPropertyButtons
BuildPropertyButtons: PUBLIC PROC [info: EditToolPrivate.Info] = {
[, info.propNameArg] ← EditToolBuilder.BuildDataFieldPair[info.layout, "Property name:", PropNameArgButton, info, 1];
EditToolBuilder.VGap[info.layout];
[, info.propValueArg] ← EditToolBuilder.BuildDataFieldPair[info.layout, "Property value:", PropValueArgButton, info, 1];
EditToolBuilder.VGap[info.layout];
info.propNode ← forSelectedNodes;
[] ← EditToolBuilder.BuildButton[info.layout, "Get", GetPropButton, info];
[] ← EditToolBuilder.BuildButton[info.layout, "Set", SetPropButton, info];
[] ← EditToolBuilder.BuildButton[info.layout, "Remove", RemovePropButton, info];
[] ← EditToolBuilder.BuildButton[info.layout, "List", ListPropsButton, info];
EditToolBuilder.ToMiddle[info.layout];
[info.propNodeLabel, ] ← EditToolBuilder.BuildTriple[info.layout, PropNodeButton, ORD[info.propNode], rootNameRope, selectionNameRope, selectedCharsNameRope, info];
EditToolBuilder.ToNext[info.layout];
[] ← EditToolBuilder.BuildButton[info.layout, "Find", FindPropButton, info];
EditToolBuilder.HGap[info.layout];
[, info.propPatternArg] ← EditToolBuilder.BuildDataFieldPair[info.layout, "Value pattern:", PropPatternArgButton, info, 1];
};
BuildStyleButtons
BuildStyleButtons: PUBLIC PROC [info: EditToolPrivate.Info] = {
[, info.styleNameArg] ← EditToolBuilder.BuildDataFieldPair[info.layout, "Style name:", StyleNameArgButton, info, 1];
EditToolBuilder.VGap[info.layout];
info.styleNode ← forRoot;
[] ← EditToolBuilder.BuildButton[info.layout, "Get", GetStyleNameButton, info];
[] ← EditToolBuilder.BuildButton[info.layout, "Set", SetStyleNameButton, info];
[] ← EditToolBuilder.BuildButton[info.layout, "Clear", ClearStyleNameButton, info];
EditToolBuilder.ToMiddle[info.layout];
[info.styleNodeLabel, ] ← EditToolBuilder.BuildPair[info.layout, StyleNameNodeButton, info.styleNode=forSelectedNodes, selectionNameRope, rootNameRope, info];
EditToolBuilder.ToNext[info.layout];
[] ← EditToolBuilder.BuildButton[info.layout, "LoadStyleDefinition", LoadStyleButton, info];
[] ← EditToolBuilder.BuildButton[info.layout, "LoadAbbreviations", LoadAbbreviationsButton, info];
};
BuildFormatButtons
BuildFormatButtons: PUBLIC PROC [info: EditToolPrivate.Info] = {
[, info.formatNameArg] ← EditToolBuilder.BuildDataFieldPair[info.layout, "Format name:", FormatNameArgButton, info, 1];
EditToolBuilder.VGap[info.layout];
info.formatNode ← forSelectedNodes;
[] ← EditToolBuilder.BuildButton[info.layout, "Get", GetFormatNameButton, info];
[] ← EditToolBuilder.BuildButton[info.layout, "Set", SetFormatNameButton, info];
[] ← EditToolBuilder.BuildButton[info.layout, "Clear", ClearFormatNameButton, info];
EditToolBuilder.ToMiddle[info.layout];
[info.formatNodeLabel, ] ← EditToolBuilder.BuildPair[info.layout, FormatNameNodeButton, info.formatNode=forSelectedNodes, selectionNameRope, rootNameRope, info];
};
GetNameArg
BadName: ERROR = CODE;
GetNameArg: PROC [arg: ViewerClasses.Viewer] RETURNS [name: ROPE] = {
Bad: PROC = {
MessageWindow.Append["Enter name consisting of one or more alphanumeric characters.", TRUE];
MessageWindow.Blink[];
ERROR BadName
};
start, end, size: INT ← 0;
rope: ROPE ← TextEdit.GetRope[EditToolBuilder.GetDataNode[arg]];
end ← size ← Rope.Size[rope];
FOR i:INT IN [0..size) DO -- skip to first nonblank
IF NOT RopeEdit.BlankChar[Rope.Fetch[rope, i]] THEN { start ← i; EXIT };
ENDLOOP;
FOR i:INT IN (start..size) DO -- count the AlphaNumericChar's
IF NOT RopeEdit.AlphaNumericChar[Rope.Fetch[rope, i]] THEN { end ← i; EXIT };
ENDLOOP;
IF end=start THEN Bad[];
name ← Rope.Substr[rope, start, end-start];
FOR i:INT IN (end..size) DO -- check that rest is blanks
IF NOT RopeEdit.BlankChar[Rope.Fetch[rope, i]] THEN Bad[];
ENDLOOP;
};
ForEachNode: PROC [nodeKind: EditToolPrivate.NodeKind, pSel: TEditDocument.Selection,
proc: PROC [node: TextNode.Ref, start: INT, size: INT]] = {
SELECT nodeKind FROM
forRoot => proc[TextNode.Root[pSel.start.pos.node], 0, 0];
forSelectedNodes, forSelectedChars => {
FOR node: TextNode.Ref ← pSel.start.pos.node, TextNode.StepForward[node] DO
start: INT ← 0;
end: INT ← TextEdit.Size[node];
IF node = pSel.start.pos.node AND pSel.start.pos.where # TextNode.NodeItself THEN {
start ← pSel.start.pos.where;
};
IF node = pSel.end.pos.node AND pSel.end.pos.where # TextNode.NodeItself THEN {
end ← MIN[end, pSel.end.pos.where+1];
};
proc[node: node, start: start, size: end-start];
IF node = pSel.end.pos.node THEN EXIT;
ENDLOOP;
};
ENDCASE => ERROR
};
SetNodeName: PROC [info: EditToolPrivate.Info, name: ROPE, style: BOOLEAN,
pSel: TEditDocument.Selection] = {
Set: PROC [node: TextNode.Ref, start: INT, size: INT] = {
IF style THEN TEditInputOps.SetStyleName[name, node]
ELSE TEditInputOps.SetFormatName[name, node]
};
ForEachNode[IF style THEN info.styleNode ELSE info.formatNode, pSel, Set]
};
StyleNameArg
styleNameArgAtom: LIST OF REF =
EditToolPrivate.Register[$StyleName, StyleNameArgOp];
clearStyleNameArgAtom: LIST OF REF =
EditToolPrivate.Register[$ClearStyleName, ClearStyleNameArgOp];
StyleNameArgButton: Buttons.ButtonProc = {
EditToolPrivate.DoButton[styleNameArgAtom, clearStyleNameArgAtom, mouseButton=red]
};
StyleNameArgOp: TEditInput.CommandProc = {
EditToolBuilder.DataFieldButton[EditToolPrivate.mainToolInfo.styleNameArg, FALSE]
};
ClearStyleNameArgOp: TEditInput.CommandProc = {
EditToolBuilder.DataFieldButton[EditToolPrivate.mainToolInfo.styleNameArg, TRUE]
};
FormatNameArg
formatNameArgAtom: LIST OF REF =
EditToolPrivate.Register[$FormatName, FormatNameArgOp];
typeNameArgAtom: LIST OF REF =
EditToolPrivate.Register[$TypeName, FormatNameArgOp];
clearFormatNameArgAtom: LIST OF REF =
EditToolPrivate.Register[$ClearFormatName, ClearFormatNameArgOp];
clearTypeNameArgAtom: LIST OF REF =
EditToolPrivate.Register[$ClearTypeName, ClearFormatNameArgOp];
FormatNameArgButton: Buttons.ButtonProc = {
EditToolPrivate.DoButton[formatNameArgAtom, clearFormatNameArgAtom, mouseButton=red]
};
FormatNameArgOp: TEditInput.CommandProc = {
EditToolBuilder.DataFieldButton[EditToolPrivate.mainToolInfo.formatNameArg, FALSE]
};
ClearFormatNameArgOp: TEditInput.CommandProc = {
EditToolBuilder.DataFieldButton[EditToolPrivate.mainToolInfo.formatNameArg, TRUE]
};
GetStyleName
getStyleNameAtom: LIST OF REF =
EditToolPrivate.Register[$GetNodeStyleName, GetStyleNameOp];
GetStyleNameButton: Buttons.ButtonProc = {
EditToolPrivate.DoButton[getStyleNameAtom]
};
GetStyleNameOp: TEditInput.CommandProc = {
GetStyleNameCom[EditToolPrivate.mainToolInfo];
};
GetStyleNameCom: PROC [info: EditToolPrivate.Info] = {
GetNameCom[info, TRUE];
};
getFormatNameAtom: LIST OF REF =
EditToolPrivate.Register[$GetNodeFormatName, GetFormatNameOp];
getTypeNameAtom: LIST OF REF =
EditToolPrivate.Register[$GetNodeTypeName, GetFormatNameOp];
GetFormatNameButton: Buttons.ButtonProc = {
EditToolPrivate.DoButton[getFormatNameAtom]
};
GetFormatNameOp: TEditInput.CommandProc = {
GetFormatNameCom[EditToolPrivate.mainToolInfo];
};
GetFormatNameCom: PROC [info: EditToolPrivate.Info] = {
GetNameCom[info, FALSE];
};
NotUniform: SIGNAL = CODE;
GetNameCom: PROC [info: EditToolPrivate.Info, style: BOOLEAN] = {
pSel: TEditDocument.Selection = TEditOps.GetSelData[];
nodeKind: EditToolPrivate.NodeKind ← IF style THEN info.styleNode ELSE info.formatNode;
name: ATOMNIL;
arg: ViewerClasses.Viewer ← IF style THEN info.styleNameArg ELSE info.formatNameArg;
Get: PROC [node: TextNode.Ref, start, size: INT] = {
n: ATOMIF style THEN NodeStyleOps.StyleNameForNode[node]
ELSE node.formatName;
IF name = NIL THEN name ← n
ELSE IF name # n THEN {
MessageWindow.Append[IF style
THEN "Nodes don't have uniform style. Using first."
ELSE "Nodes don't have uniform format. Using first.",
TRUE];
MessageWindow.Blink[];
SIGNAL NotUniform
}
};
IF NOT EditToolPrivate.CheckPSel[pSel] THEN RETURN;
ForEachNode[nodeKind, pSel, Get ! NotUniform => CONTINUE];
TEditOps.SetTextContents[arg, IF name=NIL THEN NIL ELSE Atom.GetPName[name]];
};
SetStyleName
setStyleNameAtom: LIST OF REF =
EditToolPrivate.Register[$SetNodeStyleName, SetStyleNameOp];
SetStyleNameButton: Buttons.ButtonProc = {
EditToolPrivate.DoButton[setStyleNameAtom]
};
SetStyleNameOp: TEditInput.CommandProc = {
SetStyleNameCom[EditToolPrivate.mainToolInfo];
};
SetStyleNameCom: PROC [info: EditToolPrivate.Info] = {
SetNameCom[info, TRUE];
};
setFormatNameAtom: LIST OF REF =
EditToolPrivate.Register[$SetNodeFormatName, SetFormatNameOp];
setTypeNameAtom: LIST OF REF =
EditToolPrivate.Register[$SetNodeTypeName, SetFormatNameOp];
SetFormatNameButton: Buttons.ButtonProc = {
EditToolPrivate.DoButton[setFormatNameAtom]
};
SetFormatNameOp: TEditInput.CommandProc = {
SetFormatNameCom[EditToolPrivate.mainToolInfo];
};
SetFormatNameCom: PROC [info: EditToolPrivate.Info] = {
SetNameCom[info, FALSE];
};
SetNameCom: PROC [info: EditToolPrivate.Info, style: BOOLEAN] = {
pSel: TEditDocument.Selection;
arg: ViewerClasses.Viewer ← IF style THEN info.styleNameArg ELSE info.formatNameArg;
name: ROPE ← GetNameArg[arg ! BadName => GOTO Bad];
TEditSelection.LockSel[primary, "SetNameCom"];
pSel ← TEditOps.GetSelData[];
IF NOT EditToolPrivate.CheckPSel[pSel] OR NOT TEditInputOps.CheckReadonly[pSel] THEN {
TEditSelection.UnlockSel[primary];
RETURN;
};
SetNodeName[info, name, style, pSel];
TEditSelection.UnlockSel[primary];
EXITS Bad => RETURN
};
ClearFormat
clearFormatNameAtom: LIST OF REF =
EditToolPrivate.Register[$ClearNodeFormatName, ClearFormatNameOp];
clearTypeNameAtom: LIST OF REF =
EditToolPrivate.Register[$ClearNodeTypeName, ClearFormatNameOp];
ClearFormatNameButton: Buttons.ButtonProc = {
EditToolPrivate.DoButton[clearFormatNameAtom]
};
ClearFormatNameOp: TEditInput.CommandProc = {
ClearFormatNameCom[EditToolPrivate.mainToolInfo];
};
ClearFormatNameCom: PROC [info: EditToolPrivate.Info] = {
pSel: TEditDocument.Selection = TEditOps.GetSelData[];
IF NOT EditToolPrivate.CheckPSel[pSel] THEN RETURN;
IF NOT TEditInputOps.CheckReadonly[pSel] THEN RETURN;
SetNodeName[info, NIL, FALSE, pSel]
};
clearStyleNameAtom: LIST OF REF =
EditToolPrivate.Register[$ClearNodeStyleName, ClearStyleNameOp];
ClearStyleNameButton: Buttons.ButtonProc = {
EditToolPrivate.DoButton[clearStyleNameAtom]
};
ClearStyleNameOp: TEditInput.CommandProc = {
ClearStyleNameCom[EditToolPrivate.mainToolInfo];
};
ClearStyleNameCom: PROC [info: EditToolPrivate.Info] = {
pSel: TEditDocument.Selection = TEditOps.GetSelData[];
IF NOT EditToolPrivate.CheckPSel[pSel] THEN RETURN;
IF NOT TEditInputOps.CheckReadonly[pSel] THEN RETURN;
SetNodeName[info, NIL, TRUE, pSel]
};
LoadStyle
loadStyleAtom: LIST OF REF =
EditToolPrivate.Register[$LoadStyleDefinition, LoadStyleOp];
LoadStyleButton: Buttons.ButtonProc = {
EditToolPrivate.DoButton[loadStyleAtom]
};
LoadStyleOp: TEditInput.CommandProc = {
LoadStyle[EditToolPrivate.mainToolInfo];
};
LoadStyle: PROC [info: EditToolPrivate.Info] = {
repaint: PROC [v: ViewerClasses.Viewer] RETURNS [BOOLTRUE] = {
SELECT v.class.flavor FROM
$Text => TRUSTED {Process.Detach[FORK ViewerOps.PaintViewer[v, all]]};
$Container => ViewerOps.EnumerateChildren[v, repaint];
ENDCASE;
RETURN [TRUE]
};
name: ROPE ← GetNameArg[info.styleNameArg ! BadName => GOTO Bad];
TEditInputOps.ReloadStyleName[name];
ViewerOps.EnumerateViewers[repaint];
EXITS Bad => RETURN
};
LoadAbbreviations
loadAbbreviationsAtom: LIST OF REF =
EditToolPrivate.Register[$LoadAbbreviations, LoadAbbreviationsOp];
LoadAbbreviationsButton: Buttons.ButtonProc = {
EditToolPrivate.DoButton[loadAbbreviationsAtom]
};
LoadAbbreviationsOp: TEditInput.CommandProc = {
LoadAbbreviations[EditToolPrivate.mainToolInfo];
};
LoadAbbreviations: PROC [info: EditToolPrivate.Info] = {
name: ROPE ← GetNameArg[info.styleNameArg ! BadName => GOTO Bad];
TEditInputOps.LoadAbbreviations[name];
EXITS Bad => RETURN
};
Format/Style name ranges
rootNameRope: ROPE = "For root node";
selectionNameRope: ROPE = "For selected nodes";
selectedCharsNameRope: ROPE = "For selected characters";
rootFormatNameAtom: LIST OF REF =
EditToolPrivate.Register[$FormatNameForRoot, RootFormatNameOp];
rootTypeNameAtom: LIST OF REF =
EditToolPrivate.Register[$TypeNameForRoot, RootFormatNameOp];
selFormatNameAtom: LIST OF REF =
EditToolPrivate.Register[$FormatNameForSelection, SelectionFormatNameOp];
selTypeNameAtom: LIST OF REF =
EditToolPrivate.Register[$TypeNameForSelection, SelectionFormatNameOp];
FormatNameNodeButton: Buttons.ButtonProc = {
EditToolPrivate.ChangeState[EditToolPrivate.mainToolInfo.formatNode=forSelectedNodes, selFormatNameAtom, rootFormatNameAtom]
};
RootFormatNameOp: TEditInput.CommandProc = {
RootFormatName[EditToolPrivate.mainToolInfo];
};
RootFormatName: PROC [info: EditToolPrivate.Info] = {
info.formatNode ← forRoot;
Labels.Set[info.formatNodeLabel, rootNameRope]
};
SelectionFormatNameOp: TEditInput.CommandProc = {
SelectionFormatName[EditToolPrivate.mainToolInfo];
};
SelectionFormatName: PROC [info: EditToolPrivate.Info] = {
info.formatNode ← forSelectedNodes;
Labels.Set[info.formatNodeLabel, selectionNameRope]
};
rootStyleNameAtom: LIST OF REF =
EditToolPrivate.Register[$StyleNameForRoot, RootStyleNameOp];
selStyleNameAtom: LIST OF REF =
EditToolPrivate.Register[$StyleNameForSelection, SelectionStyleNameOp];
StyleNameNodeButton: Buttons.ButtonProc = {
EditToolPrivate.ChangeState[EditToolPrivate.mainToolInfo.styleNode=forSelectedNodes, selStyleNameAtom, rootStyleNameAtom]
};
RootStyleNameOp: TEditInput.CommandProc = {
RootStyleName[EditToolPrivate.mainToolInfo];
};
RootStyleName: PROC [info: EditToolPrivate.Info] = {
info.styleNode ← forRoot;
Labels.Set[info.styleNodeLabel, rootNameRope]
};
SelectionStyleNameOp: TEditInput.CommandProc = {
SelectionStyleName[EditToolPrivate.mainToolInfo];
};
SelectionStyleName: PROC [info: EditToolPrivate.Info] = {
info.styleNode ← forSelectedNodes;
Labels.Set[info.styleNodeLabel, selectionNameRope]
};
PropName
propNameArgAtom: LIST OF REF =
EditToolPrivate.Register[$PropName, PropNameArgOp];
clearPropNameArgAtom: LIST OF REF =
EditToolPrivate.Register[$ClearPropName, ClearPropNameArgOp];
PropNameArgButton: Buttons.ButtonProc = {
EditToolPrivate.DoButton[propNameArgAtom, clearPropNameArgAtom, mouseButton=red]
};
PropNameArgOp: TEditInput.CommandProc = {
PropNameArg[EditToolPrivate.mainToolInfo];
};
PropNameArg: PROC [info: EditToolPrivate.Info] = {
EditToolBuilder.DataFieldButton[info.propNameArg, FALSE];
};
ClearPropNameArgOp: TEditInput.CommandProc = {
ClearPropNameArg[EditToolPrivate.mainToolInfo];
};
ClearPropNameArg: PROC [info: EditToolPrivate.Info] = {
EditToolBuilder.DataFieldButton[info.propNameArg, TRUE];
};
PropValue
propValueArgAtom: LIST OF REF =
EditToolPrivate.Register[$PropValue, PropValueArgOp];
clearPropValueArgAtom: LIST OF REF =
EditToolPrivate.Register[$ClearPropValue, ClearPropValueArgOp];
PropValueArgButton: Buttons.ButtonProc = {
EditToolPrivate.DoButton[propValueArgAtom, clearPropValueArgAtom, mouseButton=red]
};
PropValueArgOp: TEditInput.CommandProc = {
PropValueArg[EditToolPrivate.mainToolInfo];
};
PropValueArg: PROC [info: EditToolPrivate.Info] = {
EditToolBuilder.DataFieldButton[info.propValueArg, FALSE]
};
ClearPropValueArgOp: TEditInput.CommandProc = {
ClearPropValueArg[EditToolPrivate.mainToolInfo]
};
ClearPropValueArg: PROC [info: EditToolPrivate.Info] = {
EditToolBuilder.DataFieldButton[info.propValueArg, TRUE]
};
PropPatternArg
propPatternArgAtom: LIST OF REF =
EditToolPrivate.Register[$PropPattern, PropPatternArgOp];
clearPropPatternArgAtom: LIST OF REF =
EditToolPrivate.Register[$ClearPropPattern, ClearPropPatternArgOp];
PropPatternArgButton: Buttons.ButtonProc = {
EditToolPrivate.DoButton[propPatternArgAtom, clearPropPatternArgAtom, mouseButton=red]
};
PropPatternArgOp: TEditInput.CommandProc = {
PropPatternArg[EditToolPrivate.mainToolInfo]
};
PropPatternArg: PROC [info: EditToolPrivate.Info] = {
EditToolBuilder.DataFieldButton[info.propPatternArg, FALSE]
};
ClearPropPatternArgOp: TEditInput.CommandProc = {
ClearPropPatternArg[EditToolPrivate.mainToolInfo]
};
ClearPropPatternArg: PROC [info: EditToolPrivate.Info] = {
EditToolBuilder.DataFieldButton[info.propPatternArg, TRUE]
};
PropNode
rootPropAtom: LIST OF REF =
EditToolPrivate.Register[$PropForRoot, RootPropOp];
selPropAtom: LIST OF REF =
EditToolPrivate.Register[$PropForSelection, SelectionPropOp];
charPropAtom: LIST OF REF =
EditToolPrivate.Register[$PropForSelectedCharacter, CharacterPropOp];
PropNodeButton: Buttons.ButtonProc = {
EditToolPrivate.CycleTriple[ORD[EditToolPrivate.mainToolInfo.propNode], rootPropAtom, selPropAtom, charPropAtom]
};
RootPropOp: TEditInput.CommandProc = {
RootProp[EditToolPrivate.mainToolInfo];
};
RootProp: PROC [info: EditToolPrivate.Info] = {
info.propNode ← forRoot;
Labels.Set[info.propNodeLabel, rootNameRope]
};
SelectionPropOp: TEditInput.CommandProc = {
SelectionProp[EditToolPrivate.mainToolInfo];
};
SelectionProp: PROC [info: EditToolPrivate.Info] = {
info.propNode ← forSelectedNodes;
Labels.Set[info.propNodeLabel, selectionNameRope]
};
CharacterPropOp: TEditInput.CommandProc = {
SelectedCharsProp[EditToolPrivate.mainToolInfo];
};
SelectedCharsProp: PROC [info: EditToolPrivate.Info] = {
info.propNode ← forSelectedChars;
Labels.Set[info.propNodeLabel, selectedCharsNameRope]
};
GetProp
getPropAtom: LIST OF REF =
EditToolPrivate.Register[$GetNodeProp, GetPropOp];
GetPropButton: Buttons.ButtonProc = {
EditToolPrivate.DoButton[getPropAtom]
};
GetPropOp: TEditInput.CommandProc = {
GetPropCom[EditToolPrivate.mainToolInfo];
};
GetPropCom: PROC [info: EditToolPrivate.Info] = {
pSel: TEditDocument.Selection;
root: TextNode.Ref;
name: ROPE;
key: ATOM;
valueRope: ROPE;
haveFirst: BOOLEANFALSE;
SaveOrCheck: PROC [value: REF] ~ {
vrope: ROPE ← NodeProps.GetSpecs[key, value];
IF haveFirst THEN {
IF NOT Rope.Equal[valueRope, vrope] THEN {
MessageWindow.Append[IF info.propNode = forSelectedChars THEN "Characters" ELSE "Nodes", TRUE];
MessageWindow.Append[" don't have uniform property values. Using first.", FALSE];
MessageWindow.Blink[];
SIGNAL NotUniform;
};
}
ELSE {
valueRope ← vrope;
haveFirst ← TRUE;
};
};
Get: PROC [node: TextNode.Ref, start, size: INT] = {
IF info.propNode = forSelectedChars THEN {
IF node.hascharprops THEN FOR i: INT IN [start..start+size) DO
SaveOrCheck[TextEdit.GetCharProp[node, i, key]];
ENDLOOP;
}
ELSE SaveOrCheck[TextEdit.GetProp[node, key]];
};
name ← GetNameArg[info.propNameArg ! BadName => GOTO Bad];
key ← Atom.MakeAtom[name];
TEditSelection.LockSel[primary, "GetPropCom"];
pSel ← TEditOps.GetSelData[];
IF NOT EditToolPrivate.CheckPSel[pSel] THEN {
TEditSelection.UnlockSel[primary];
RETURN;
};
root ← TEditSelection.SelectionRoot[pSel];
[] ← TEditLocks.Lock[root, "GetPropCom", read];
ForEachNode[info.propNode, pSel, Get ! NotUniform => CONTINUE];
TEditSelection.UnlockDocAndPSel[root];
TEditOps.SetTextContents[info.propValueArg, valueRope];
EXITS Bad => RETURN
};
SetProp
setPropAtom: LIST OF REF =
EditToolPrivate.Register[$SetNodeProp, SetPropOp];
SetPropButton: Buttons.ButtonProc = {
EditToolPrivate.DoButton[setPropAtom]
};
SetPropOp: TEditInput.CommandProc = {
SetPropCom[EditToolPrivate.mainToolInfo];
};
SetPropCom: PROC [info: EditToolPrivate.Info] = {
pSel: TEditDocument.Selection;
name: ROPE;
key: ATOM;
root: TextNode.Ref;
valueRope: ROPE ← TextEdit.GetRope[EditToolBuilder.GetDataNode[info.propValueArg]];
Put: PROC [node: TextNode.Ref, start, size: INT] = {
value: REF ~ NodeProps.DoSpecs[key, valueRope];
IF info.propNode = forSelectedChars THEN
TextEdit.PutCharProp[node: node, index: start, name: key, value: value, nChars: size, event: TEditInput.CurrentEvent[], root: root]
ELSE {
IF NodePropsExtras.Is[key, $ClientOnly] THEN {
MessageWindow.Append["Can't change ", TRUE];
MessageWindow.Append[Atom.GetPName[key], FALSE];
MessageWindow.Append[" node property with the EditTool.", FALSE];
IF key = $CharProps THEN {
MessageWindow.Append[" Set character properties with \"For selected characters\" option.", FALSE];
};
MessageWindow.Blink[];
}
ELSE {TextEdit.PutProp[node, key, value, TEditInput.CurrentEvent[], root]}
};
};
name ← GetNameArg[info.propNameArg ! BadName => GOTO Bad];
key ← Atom.MakeAtom[name];
TEditSelection.LockSel[primary, "SetPropCom"];
pSel ← TEditOps.GetSelData[];
IF NOT EditToolPrivate.CheckPSel[pSel] OR NOT TEditInputOps.CheckReadonly[pSel] THEN {
TEditSelection.UnlockSel[primary];
RETURN;
};
root ← TEditSelection.SelectionRoot[pSel];
[] ← TEditLocks.Lock[root, "SetPropCom"];
ForEachNode[info.propNode, pSel, Put !
RuntimeError.UNCAUGHT => {
MessageWindow.Append["Uncaught error in EditToolMiscImpl.SetPropCom", TRUE];
MessageWindow.Blink[];
CONTINUE;
};
];
TEditSelection.UnlockDocAndPSel[root];
EXITS Bad => RETURN
};
RemoveProp
removePropAtom: LIST OF REF =
EditToolPrivate.Register[$RemoveNodeProp, RemovePropOp];
RemovePropButton: Buttons.ButtonProc = {
EditToolPrivate.DoButton[removePropAtom]
};
RemovePropOp: TEditInput.CommandProc = {
RemovePropCom[EditToolPrivate.mainToolInfo];
};
RemovePropCom: PROC [info: EditToolPrivate.Info] = {
pSel: TEditDocument.Selection;
root: TextNode.Ref;
key: ATOM;
Rem: PROC [node: TextNode.Ref, start, size: INT] = {
IF info.propNode = forSelectedChars THEN
TextEdit.PutCharProp[node: node, index: start, name: key, value: NIL, nChars: size, event: TEditInput.CurrentEvent[], root: root]
ELSE
TextEdit.PutProp[node, key, NIL, TEditInput.CurrentEvent[], root]
};
key ← Atom.MakeAtom[GetNameArg[info.propNameArg ! BadName => GOTO Bad]];
TEditSelection.LockSel[primary, "RemovePropCom"];
pSel ← TEditOps.GetSelData[];
IF NOT EditToolPrivate.CheckPSel[pSel] OR NOT TEditInputOps.CheckReadonly[pSel] THEN {
TEditSelection.UnlockSel[primary];
RETURN;
};
root ← TEditSelection.SelectionRoot[pSel];
[] ← TEditLocks.Lock[root, "RemovePropCom"];
ForEachNode[info.propNode, pSel, Rem];
TEditSelection.UnlockDocAndPSel[root];
EXITS Bad => RETURN
};
ListProps
listPropsAtom: LIST OF REF =
EditToolPrivate.Register[$ListNodeProps, ListPropsOp];
ListPropsButton: Buttons.ButtonProc = {
EditToolPrivate.DoButton[listPropsAtom]
};
ListPropsOp: TEditInput.CommandProc = {
ListPropsCom[EditToolPrivate.mainToolInfo];
};
ListPropsCom: PROC [info: EditToolPrivate.Info] = {
pSel: TEditDocument.Selection;
valueRope: ROPE;
root: TextNode.Ref;
list: LIST OF ATOMNIL;
listProp: PROC [name: ATOM, value: REF] RETURNS[BOOLEAN] = {
IF value#NIL THEN {list ← CONS[name, list]};
RETURN [FALSE];
};
List: PROC[node: TextNode.Ref, start, size: INT] = {
IF info.propNode = forSelectedChars THEN {
IF node.hascharprops THEN FOR i: INT IN [start..start+size) DO
FOR p: Atom.PropList ← TextEdit.GetCharPropList[node, i], p.rest UNTIL p=NIL DO
[] ← listProp[NARROW[p.first.key], p.first.val];
ENDLOOP;
ENDLOOP;
}
ELSE [] ← NodeProps.MapProps[node, listProp];
};
TEditSelection.LockSel[primary, "ListPropsCom"];
pSel ← TEditOps.GetSelData[];
IF NOT EditToolPrivate.CheckPSel[pSel] THEN {
TEditSelection.UnlockSel[primary];
RETURN;
};
root ← TEditSelection.SelectionRoot[pSel];
[] ← TEditLocks.Lock[root, "ListPropsCom", read];
ForEachNode[info.propNode, pSel, List];
TEditSelection.UnlockDocAndPSel[root];
FOR lst: LIST OF ATOM ← list, lst.rest UNTIL lst=NIL DO
valueRope ← Rope.Concat[valueRope, Atom.GetPName[lst.first]];
IF lst.rest # NIL THEN valueRope ← Rope.Concat[valueRope, " "];
ENDLOOP;
TEditOps.SetTextContents[info.propNameArg, valueRope]
};
FindProp
findPropAtom: LIST OF REF =
EditToolPrivate.Register[$FindNodeProp, FindPropOp];
findBackPropAtom: LIST OF REF =
EditToolPrivate.Register[$BackFindNodeProp, FindBackPropOp];
FindPropButton: Buttons.ButtonProc = {
EditToolPrivate.DoButton[findPropAtom, findBackPropAtom, mouseButton=red]
};
FindPropOp: TEditInput.CommandProc = {
FindPropCom[EditToolPrivate.mainToolInfo, TRUE];
};
FindBackPropOp: TEditInput.CommandProc = {
FindPropCom[EditToolPrivate.mainToolInfo, FALSE];
};
FindPropCom: PROC [info: EditToolPrivate.Info, forward: BOOLEAN] = {
pSel: TEditDocument.Selection;
root: TextNode.Ref ← NIL;
node: TextNode.Ref;
{
startNode: TextNode.Ref;
startCharFound: INT ← 0;
endCharFound: INT ← 0;
pattern: ROPE;
tester: TextNode.RefTextNode ← TextNode.NewTextNode[];
name: ATOM; -- property name we're looking for
finder: TreeFind.Finder; -- pattern for property value
Step: PROC [n: TextNode.Ref] RETURNS [TextNode.Ref] = {
RETURN [IF forward THEN TextNode.StepForward[n] ELSE TextNode.StepBackward[n]]
};
name ← Atom.MakeAtom[GetNameArg[info.propNameArg ! BadName => GOTO Bad]];
pattern ← TextEdit.GetRope[EditToolBuilder.GetDataNode[info.propPatternArg]];
IF Rope.Size[pattern] # 0 THEN -- create a finder for the pattern
finder ← TreeFind.CreateFromRope[pattern !
TextFind.MalformedPattern => { ReportPatternError[ec]; GOTO Bad }];
TEditSelection.LockSel[primary, "FindPropCom"];
pSel ← TEditOps.GetSelData[];
IF NOT EditToolPrivate.CheckPSel[pSel] THEN {
MessageWindow.Append["Select node.", TRUE];
MessageWindow.Blink[];
TEditSelection.UnlockSel[primary];
RETURN;
};
root ← TEditSelection.SelectionRoot[pSel];
[] ← TEditLocks.Lock[root, "FindPropCom", read];
node ← NIL; -- start for search
startNode ← IF forward THEN pSel.end.pos.node ELSE pSel.start.pos.node;
IF finder#NIL
THEN FOR n: TextNode.Ref ← startNode, Step[n] UNTIL n = NIL DO
IF info.propNode = forSelectedChars THEN {
selectionPoint: TEditDocument.SelectionPoint ~ IF forward THEN pSel.end ELSE pSel.start;
SearchCharProps: PROC RETURNS [found: BOOLEANFALSE] ~ {
prev: REFNIL;
foundRope: ROPENIL;
CharAction: PROC [i: INT] ~ {
value: REF ← TextEdit.GetCharProp[n, i, name];
IF value # prev AND value # NIL THEN {
tester.rope ← NodeProps.GetSpecs[name, value];
IF TextFind.Try[finder, tester].found THEN {
startCharFound ← i;
endCharFound ← i+1;
foundRope ← tester.rope;
prev ← value;
found ← TRUE;
};
prev ← value;
};
};
CheckChar: PROC [i: INT] RETURNS [stop: BOOLEAN] ~ {
value: REF ← TextEdit.GetCharProp[n, i, name];
IF value # prev OR NOT Rope.Equal[foundRope, NodeProps.GetSpecs[name, value]] THEN RETURN [TRUE];
startCharFound ← MIN[startCharFound, i];
endCharFound ← MAX[endCharFound, i+1];
prev ← value;
RETURN [FALSE];
};
IF forward THEN {
FOR i: INT IN [startChar..endChar) UNTIL found DO CharAction[i] ENDLOOP;
IF found THEN FOR i: INT IN [endCharFound..endChar) UNTIL CheckChar[i].stop DO ENDLOOP;
}
ELSE {
FOR i: INT DECREASING IN [startChar..endChar) UNTIL found DO CharAction[i] ENDLOOP;
IF found THEN FOR i: INT DECREASING IN [startChar..startCharFound) UNTIL CheckChar[i].stop DO ENDLOOP;
}
};
startChar: INT ← 0;
endChar: INT ← TextEdit.Size[n];
IF n = startNode AND selectionPoint.pos.where = TextNode.NodeItself THEN LOOP
ELSE IF n = startNode THEN {
IF forward THEN startChar ← MAX[MIN[selectionPoint.pos.where+1, endChar], startChar]
ELSE endChar ← MIN[MAX[selectionPoint.pos.where, startChar], endChar]
};
IF n.hascharprops AND SearchCharProps[].found THEN {node ← n; EXIT};
}
ELSE IF n # startNode THEN {
value: REF ← NodeProps.GetProp[n, name];
IF value # NIL THEN { -- see if matches pattern
tester.rope ← NodeProps.GetSpecs[name, value];
IF TextFind.Try[finder, tester].found THEN {node ← n; EXIT};
};
};
ENDLOOP;
IF node=NIL OR TextNode.Parent[node]=NIL THEN {
MessageWindow.Append["Failed to find property matching given pattern.", TRUE];
MessageWindow.Blink[];
TEditSelection.UnlockDocAndPSel[root];
RETURN;
};
IF info.propNode = forSelectedChars THEN {
EditToolPrivate.tSel.start.pos ← [node, startCharFound];
EditToolPrivate.tSel.end.pos ← [node, endCharFound-1];
EditToolPrivate.tSel.granularity ← char;
}
ELSE {
EditToolPrivate.tSel.start.pos ← [node, 0];
EditToolPrivate.tSel.end.pos ← [node, MAX[TextEdit.Size[node], 1]-1];
EditToolPrivate.tSel.granularity ← node;
};
EditToolPrivate.tSel.viewer ← pSel.viewer;
EditToolPrivate.tSel.data ← pSel.data;
EditToolPrivate.tSel.insertion ← after;
TEditSelection.MakeSelection[new: EditToolPrivate.tSel];
TEditInput.CloseEvent[];
TEditRefresh.ScrollToEndOfSel[EditToolPrivate.tSel.viewer, FALSE];
TEditSelection.UnlockDocAndPSel[root];
GetPropCom[info];
EXITS Bad => { RETURN; }
}
};
ReportPatternError: PUBLIC PROC [ec: TextFind.PatternErrorCode] = {
msg: ROPE = SELECT ec FROM
toobig => "Pattern too long",
endquote => "Pattern ends with quote (')",
endtilda => "Pattern ends with tilde (~)",
boundary => "Pattern has | inside rather than at beginning or end",
missingNameEnd => "Pattern has < without matching >",
unmatchedNameEnd => "Pattern has > without previous <",
ENDCASE => "Pattern error (consult CedarSupport^)";
MessageWindow.Append[msg, TRUE];
MessageWindow.Blink[]
};
END.