DIRECTORY Rope USING [ROPE, Concat, Substr], SoundList USING [ExtractSoundList, ReplaceSoundList, SoundChars], TEditDocument USING [SelectionId], TextEdit USING [Size], TiogaButtons USING [TextNodeRef], TiogaOps USING [GetSelection, Location, GetRope, SelectPoint, CancelSelection], TiogaOpsDefs USING [Ref, WhichSelection, SelectionGrain], IO USING [PutFR, rope], MessageWindow USING [Append, Blink], ViewerClasses USING [Viewer], ViewerOps USING [FetchProp, DestroyViewer], VoiceAging USING [EditAges], VoiceInText USING [thrushHandle, DebugRope], VoiceMarkers USING [ExtractCharMarks, EditCharMarks, ExtractTextMarks, EditTextMarks], VoicePlayBack USING [RedrawViewer], VoiceViewers USING [VoiceViewerInfo, SoundList, soundRopeCharLength, Selection, SelectionRec, SoundInterval, SoundIntervalRec, GetVoiceLock], VoiceRope USING [Replace, VoiceRopeInterval], VoiceEditOps; VoiceEditOpsImpl: CEDAR PROGRAM IMPORTS IO, MessageWindow, Rope, SoundList, TextEdit, TiogaButtons, TiogaOps, ViewerOps, VoiceAging, VoiceInText, VoiceMarkers, VoicePlayBack, VoiceRope, VoiceViewers EXPORTS VoiceEditOps = BEGIN BadVoiceViewerDisplay: ERROR [viewer: ViewerClasses.Viewer]; Mumble: PUBLIC PROC [opAttempted: Rope.ROPE, viewerTypes: Rope.ROPE] = { MessageWindow.Append[IO.PutFR["%g is not a valid operation between %g viewers", IO.rope[opAttempted], IO.rope[viewerTypes]], TRUE]; MessageWindow.Blink[] }; DescribeSelection: PUBLIC PROC [which: TiogaOpsDefs.WhichSelection, forceDelete: BOOLEAN, returnSoundInterval: BOOLEAN, viewerAlreadyLocked: ViewerClasses.Viewer _ NIL] RETURNS [failed: BOOLEAN _ FALSE, selection: VoiceViewers.Selection, soundInterval: VoiceViewers.SoundInterval _ NIL] = { selStart, selEnd: TiogaOps.Location; caretBefore, pendingDelete: BOOL; level: TiogaOpsDefs.SelectionGrain; selection _ NEW[VoiceViewers.SelectionRec]; [viewer: selection.viewer, start: selStart, end: selEnd, level: level, caretBefore: caretBefore, pendingDelete: pendingDelete] _ TiogaOps.GetSelection[which]; selection.voiceViewerInfo _ NARROW[ViewerOps.FetchProp[selection.viewer, $voiceViewerInfo], VoiceViewers.VoiceViewerInfo]; IF ~( viewerAlreadyLocked = selection.viewer OR VoiceViewers.GetVoiceLock[selection.voiceViewerInfo] ) THEN {failed _ TRUE; RETURN}; IF selStart.node # selEnd.node THEN ERROR BadVoiceViewerDisplay[selection.viewer]; selection.displayNode _ selStart.node; selection.ropeInterval _ [selection.voiceViewerInfo.ropeInterval.ropeID, selStart.where*VoiceViewers.soundRopeCharLength, (selEnd.where+(IF level=point THEN 0 ELSE 1))*VoiceViewers.soundRopeCharLength -selStart.where*VoiceViewers.soundRopeCharLength]; IF selection.ropeInterval.start + selection.ropeInterval.length > selection.voiceViewerInfo.ropeInterval.length THEN ERROR BadVoiceViewerDisplay[selection.viewer]; IF returnSoundInterval THEN { soundInterval _ NEW [VoiceViewers.SoundIntervalRec _ [ropeInterval: selection.ropeInterval]]; SoundList.ExtractSoundList[selection.voiceViewerInfo, soundInterval]; VoiceMarkers.ExtractCharMarks[selection.voiceViewerInfo, soundInterval]; VoiceMarkers.ExtractTextMarks[selection.voiceViewerInfo, soundInterval] -- the width/displayChars fields in this will not necessarily be valid at the end of the list }; IF ~(pendingDelete OR forceDelete) THEN { IF ~caretBefore THEN selection.ropeInterval.start _ selection.ropeInterval.start + selection.ropeInterval.length; selection.ropeInterval.length _ 0 } }; ReplaceSelectionWithSavedInterval: PUBLIC PROC [selection: VoiceViewers.Selection, soundInterval: VoiceViewers.SoundInterval, leaveSelected: BOOLEAN] RETURNS [viewerDeleted: BOOLEAN, insertChars, positionAfterInsert: INT] = { newViewerContents: Rope.ROPE; displayCutPoint, deleteChars: INT; IF selection.ropeInterval.length = 0 AND soundInterval = NIL THEN RETURN; SoundList.ReplaceSoundList[voiceViewerInfo: selection.voiceViewerInfo, cutStart: selection.ropeInterval.start, cutLength: selection.ropeInterval.length, replacement: IF soundInterval = NIL THEN NIL ELSE soundInterval.soundList]; displayCutPoint _ selection.ropeInterval.start/VoiceViewers.soundRopeCharLength; deleteChars _ selection.ropeInterval.length/VoiceViewers.soundRopeCharLength; insertChars _ IF soundInterval = NIL THEN 0 ELSE soundInterval.ropeInterval.length/VoiceViewers.soundRopeCharLength; positionAfterInsert _ displayCutPoint + insertChars + 1; VoiceMarkers.EditCharMarks[selection.voiceViewerInfo, displayCutPoint, deleteChars, insertChars, soundInterval]; VoiceMarkers.EditTextMarks[selection.voiceViewerInfo, displayCutPoint, deleteChars, insertChars, soundInterval]; VoiceAging.EditAges[selection.voiceViewerInfo, displayCutPoint, deleteChars, insertChars]; [newViewerContents, selection.voiceViewerInfo.remnant] _ SoundList.SoundChars[selection.voiceViewerInfo, displayCutPoint]; newViewerContents _ Rope.Concat[(TiogaOps.GetRope[selection.displayNode]).Substr[0, displayCutPoint], newViewerContents]; selection.displayNode _ VoicePlayBack.RedrawViewer[selection.viewer, newViewerContents, displayCutPoint, deleteChars, insertChars, selection.voiceViewerInfo.remnant, TRUE, IF leaveSelected THEN primaryOnInsert ELSE deSelected]; viewerDeleted _ TextEdit.Size[TiogaButtons.TextNodeRef[selection.displayNode]] = 0; IF viewerDeleted THEN ViewerOps.DestroyViewer[selection.viewer] ELSE { selection.voiceViewerInfo.ropeInterval _ VoiceRope.Replace[handle: VoiceInText.thrushHandle, vr: NEW [VoiceRope.VoiceRopeInterval _ selection.voiceViewerInfo.ropeInterval], start: selection.ropeInterval.start, len: selection.ropeInterval.length, with: IF soundInterval = NIL THEN NIL ELSE NEW [VoiceRope.VoiceRopeInterval _ soundInterval.ropeInterval]]^ } }; Delete: PUBLIC PROC = { charAfterDelete: INT; viewerDeleted: BOOLEAN; selectionLocked: BOOLEAN; selection: VoiceViewers.Selection; [selection: selection, failed: selectionLocked] _ DescribeSelection[which: primary, forceDelete: TRUE, returnSoundInterval: FALSE]; IF selectionLocked THEN RETURN; selection.voiceViewerInfo.edited _ TRUE; [positionAfterInsert: charAfterDelete, viewerDeleted: viewerDeleted] _ ReplaceSelectionWithSavedInterval[selection: selection, soundInterval: NIL, leaveSelected: TRUE]; IF ~viewerDeleted THEN { TiogaOps.SelectPoint[viewer: selection.viewer, caret: [selection.displayNode, MIN [charAfterDelete, TextEdit.Size[TiogaButtons.TextNodeRef[selection.displayNode]]-1]], which: primary]; selection.voiceViewerInfo.editInProgress _ FALSE } }; Copy: PUBLIC PROC [target: TEditDocument.SelectionId _ primary] = { sourceSelection, destSelection: VoiceViewers.Selection; sourceInterval: VoiceViewers.SoundInterval; selectionLocked: BOOLEAN; destMovesForward: INT _ 0; [selection: sourceSelection, soundInterval: sourceInterval, failed: selectionLocked] _ DescribeSelection[which: (IF target=primary THEN secondary ELSE primary), forceDelete: FALSE, returnSoundInterval: TRUE]; IF ~selectionLocked THEN { [selection: destSelection, failed: selectionLocked] _ DescribeSelection[which: (IF target=primary THEN primary ELSE secondary), forceDelete: FALSE, returnSoundInterval: FALSE, viewerAlreadyLocked: sourceSelection.viewer]; IF selectionLocked THEN sourceSelection.voiceViewerInfo.editInProgress _ FALSE }; TiogaOps.CancelSelection[primary]; TiogaOps.CancelSelection[secondary]; IF selectionLocked THEN RETURN; destSelection.voiceViewerInfo.edited _ TRUE; IF sourceSelection.ropeInterval.length # 0 THEN { sourceSelection.voiceViewerInfo.edited _ TRUE; IF sourceSelection.viewer = destSelection.viewer -- special case of same viewer THEN { IF (sourceSelection.ropeInterval.start< destSelection.ropeInterval.start+destSelection.ropeInterval.length) = (destSelection.ropeInterval.start< sourceSelection.ropeInterval.start+sourceSelection.ropeInterval.length) THEN { firstStart: INT _ destSelection.ropeInterval.start; firstLength: INT _ MAX [sourceSelection.ropeInterval.start -destSelection.ropeInterval.start, 0]; secondStart: INT _ sourceSelection.ropeInterval.start + sourceSelection.ropeInterval.length; secondLength: INT _ MAX [(destSelection.ropeInterval.start+destSelection.ropeInterval.length) - (sourceSelection.ropeInterval.start+sourceSelection.ropeInterval.length), 0]; sourceSelection.ropeInterval.start _ firstStart; sourceSelection.ropeInterval.length _ firstLength; destSelection.ropeInterval.start _ secondStart; destSelection.ropeInterval.length _ secondLength; sourceInterval _ NIL }; IF sourceSelection.ropeInterval.start