-- file: TOCSelection.Mesa -- last edited by Brotz, December 1, 1980 12:08 PM DIRECTORY dsD: FROM "DisplayDefs", Editor, inD: FROM "InteractorDefs", Inline, SystemDefs: FROM "SystemDefs", tsD: FROM "TOCSelectionDefs", vmD: FROM "VirtualMgrDefs"; TOCSelection: PROGRAM IMPORTS dsD, Editor, inD, Inline, SystemDefs EXPORTS tsD = PUBLIC BEGIN RunArrayBlk: TYPE = ARRAY [0 .. 0) OF RunRange; RunArray: TYPE = POINTER TO RunArrayBlk; RunRange: TYPE = RECORD [low, high: vmD.TOCIndex]; runArray: RunArray _ NIL; maxRuns: CARDINAL _ 0; runs: CARDINAL _ 0; runIncrement: CARDINAL = 10; AddRange: PROCEDURE [low, high: vmD.TOCIndex] = -- Includes the new range [low .. high) in the toc selection. BEGIN index: CARDINAL; IF runs = 0 THEN {runArray[0] _ [low, high]; runs _ 1} ELSE BEGIN index _ FindRunArrayIndex[low, high]; SELECT TRUE FROM (runArray[index].high < low) => {MakeRoomAt[index + 1]; runArray[index + 1] _ [low, high]}; (runArray[index].high = low) => BEGIN runArray[index].high _ high; IF index + 1 < runs AND runArray[index + 1].low = high THEN {runArray[index].high _ runArray[index + 1].high; ShiftDownTo[index + 1]}; END; (runArray[index].low = high) => BEGIN runArray[index].low _ low; IF index > 0 AND runArray[index - 1].high = low THEN {runArray[index - 1].high _ runArray[index].high; ShiftDownTo[index]}; END; (runArray[index].low > high) => {MakeRoomAt[index]; runArray[index] _ [low, high]}; ENDCASE => ERROR; END; END; -- of AddRange -- RemoveRange: PROCEDURE [low, high: vmD.TOCIndex] = -- Removes the range [low .. high) from the toc selection. BEGIN index: CARDINAL; runHigh, runLow: vmD.TOCIndex; IF runs = 0 THEN ERROR; [runLow, runHigh] _ runArray[index _ FindRunArrayIndex[low, high]]; SELECT TRUE FROM (runLow = low AND runHigh = high) => ShiftDownTo[index]; (runLow = low) => runArray[index].low _ high; (runHigh = high) => runArray[index].high _ low; (runLow < low AND runHigh > high) => {MakeRoomAt[index]; runArray[index].high _ low; runArray[index + 1].low _ high}; ENDCASE => ERROR; END; -- of RemoveRange -- FindRunArrayIndex: PROCEDURE [low, high: vmD.TOCIndex] RETURNS [index: CARDINAL] = -- If [low .. high) is contained in or adjacent to any run range in runArray, retruns the -- index of one such range in runArray. Otherwise, returns the index of a run range -- either immediately preceeding or immediately following [low .. high). If no runs exist, -- returns 0. BEGIN lowIndex, highIndex, halfwayIndex: CARDINAL; IF runs <= 1 THEN RETURN[0]; lowIndex _ 0; highIndex _ runs - 1; DO IF runArray[highIndex].low <= high THEN RETURN[highIndex]; IF runArray[lowIndex].high >= low THEN RETURN[lowIndex]; IF lowIndex + 1 = highIndex THEN RETURN[lowIndex]; halfwayIndex _ (lowIndex + highIndex) / 2; IF runArray[halfwayIndex].low > high THEN highIndex _ halfwayIndex ELSE lowIndex _ halfwayIndex; ENDLOOP; END; -- of FindRunArrayIndex -- MakeRoomAt: PROCEDURE [index: CARDINAL] = -- Moves entries in runArray[index .. runs) up by one index. BEGIN IF runs = maxRuns THEN EnlargeRunArray[]; IF index < runs THEN FOR i: CARDINAL DECREASING IN [index .. runs) DO runArray[i + 1] _ runArray[i]; ENDLOOP; runs _ runs + 1; END; -- of MakeRoomAt -- ShiftDownTo: PROCEDURE [index: CARDINAL] = -- Moves entries in runArray[index + 1 .. runs) down by one index. BEGIN runs _ runs - 1; IF index < runs THEN Inline.COPY[from: @runArray[index + 1], nwords: (runs - index) * SIZE[RunRange], to: @runArray[index]]; END; -- of ShiftDownTo -- EnlargeRunArray: PROCEDURE = -- Allocates a larger runArray and copies the contents of the old runArray into it. BEGIN newRunArray: RunArray; maxRuns _ maxRuns + runIncrement; newRunArray _ SystemDefs.AllocateHeapNode[SIZE[RunRange] * maxRuns]; Inline.COPY[from: runArray, nwords: runs * SIZE[RunRange], to: newRunArray]; SystemDefs.FreeHeapNode[runArray]; runArray _ newRunArray; END; -- of EnlargeRunArray -- ResetTOCSelection: PROCEDURE = -- Resets the entire TOCSelection to be empty. BEGIN IF maxRuns # runIncrement THEN BEGIN IF runArray # NIL THEN SystemDefs.FreeHeapNode[runArray]; maxRuns _ runIncrement; runArray _ SystemDefs.AllocateHeapNode[SIZE[RunRange] * maxRuns]; END; runs _ 0; END; -- of ResetTOCSelection -- SetTOCSelection: PROCEDURE [index: vmD.TOCIndex] = -- sets the TOC selection to the range [index..index+1). BEGIN ResetTOCSelection[]; runArray[0] _ [index, index + 1]; runs _ 1; END; -- of SetTOCSelection -- IsSelected: PROCEDURE [index: vmD.TOCIndex] RETURNS [BOOLEAN] = -- Returns TRUE iff index is in the current TOCSelection. BEGIN low, high: vmD.TOCIndex; IF runs = 0 THEN RETURN[FALSE]; [low, high] _ runArray[FindRunArrayIndex[index, index]]; RETURN[index IN [low .. high)]; END; -- of IsSelected -- TOCSelectionEmpty: PROCEDURE RETURNS [BOOLEAN] = -- Returns TRUE iff the current TOCSelection is empty. BEGIN RETURN[runs = 0]; END; -- of TOCSelectionEmpty -- FirstSelectedEntry: PROCEDURE RETURNS [vmD.TOCIndex] = -- Returns the lowest entry in the current TOCSelection. Returns 0 if the current -- TOCSelection is empty. BEGIN RETURN[IF runs = 0 THEN 0 ELSE runArray[0].low]; END; -- of FirstSelectedEntry -- LastSelectedEntry: PROCEDURE RETURNS [vmD.TOCIndex] = -- Returns the highest entry in the current TOCSelection. Returns 0 if the current -- TOCSelection is empty. BEGIN RETURN[IF runs = 0 THEN 0 ELSE runArray[runs - 1].high - 1]; END; -- of LastSelectedEntry -- NextSelectedEntry: PROCEDURE [index: vmD.TOCIndex] RETURNS [vmD.TOCIndex] = -- Returns the next higher entry after index in the current TOCSelection. Returns 0 if no -- higher selected entry exists. BEGIN runIndex: CARDINAL; low, high: vmD.TOCIndex; IF runs = 0 THEN RETURN[0]; runIndex _ FindRunArrayIndex[index, index]; [low, high] _ runArray[runIndex]; SELECT TRUE FROM (index < low) => RETURN[low]; (index IN [low .. high - 1)) => RETURN[index + 1]; (index >= high - 1 AND runIndex + 1 < runs) => RETURN[runArray[runIndex + 1].low]; ENDCASE => RETURN[0]; END; -- of NextSelectedEntry -- PrevSelectedEntry: PROCEDURE [index: vmD.TOCIndex] RETURNS [vmD.TOCIndex] = -- Returns the next lower entry before index in the current TOCSelection. Returns 0 if no -- lower selected entry exists. BEGIN runIndex: CARDINAL; low, high: vmD.TOCIndex; IF runs = 0 THEN RETURN[0]; runIndex _ FindRunArrayIndex[index, index]; [low, high] _ runArray[runIndex]; SELECT TRUE FROM (index >= high) => RETURN[high - 1]; (index IN (low .. high)) => RETURN[index - 1]; (index <= low AND runIndex > 0) => RETURN[runArray[runIndex - 1].high - 1]; ENDCASE => RETURN[0]; END; -- of PrevSelectedEntry -- ConsiderAll: PROCEDURE [tnp: inD.TOCTextNbrPtr] = BEGIN IF runs = 0 THEN RETURN; FOR i: CARDINAL IN [0 .. runs) DO inD.Consider[runArray[i].low, runArray[i].high -1, tnp]; ENDLOOP; END; -- of ConsiderAll -- DeconsiderAll: PROCEDURE [tnp: inD.TOCTextNbrPtr] = BEGIN IF runs = 0 THEN RETURN; FOR i: CARDINAL IN [0 .. runs) DO inD.Deconsider[runArray[i].low, runArray[i].high -1, tnp]; ENDLOOP; END; -- of DeconsiderAll -- TOCTextTracker: PROCEDURE [tnp: inD.TOCTextNbrPtr] = -- Sets cursor shape for TOC Text subneighborhood. Watches for button up and down, -- selects and restores selections in TOC according to the TOC selection protocol. BEGIN OPEN inD; trackerState: {neutral, startIsCocked, extending, adding, removing} _ neutral; extendState: {addLow, addHigh, takeLow, takeHigh}; x: ScreenXCoord; y: ScreenYCoord; xOffset, yOffset: INTEGER; low, high, start, lowerRunHigh, upperRunLow, thisEntry, floatC: vmD.TOCIndex; runIndex: CARDINAL; BailOut: PROCEDURE = BEGIN SELECT trackerState FROM startIsCocked => {Deconsider[start, start, tnp]; ConsiderAll[tnp]}; adding => Deconsider[start, start, tnp]; removing => Consider[start, start, tnp]; extending => SELECT extendState FROM addLow => IF floatC < low THEN Deconsider[floatC, low - 1, tnp]; addHigh => IF floatC > high THEN Deconsider[high, floatC - 1, tnp]; takeLow => IF floatC > low THEN Consider[low, floatC - 1, tnp]; takeHigh => IF floatC < high THEN Consider[floatC, high - 1, tnp]; ENDCASE => ERROR; ENDCASE; END; -- of BailOut -- dsD.ChangeCursor[lineArrow]; [ , xOffset, yOffset] _ dsD.GetCursor[]; DO x _ cursorX^ + xOffset; y _ cursorY^ + yOffset; IF ~(y IN [tnp.topY .. tnp.bottomY) AND x IN [lineBarLeftX .. leftMargin)) THEN -- cursor is out of line bar subneighborhood -- {BailOut[]; RETURN}; IF (thisEntry _ MapYToTOCIndex[y, tnp]) > 0 THEN SELECT trackerState FROM neutral => BEGIN start _ thisEntry; SELECT TRUE FROM MouseButton[left, down] => {trackerState _ startIsCocked; DeconsiderAll[tnp]; Consider[start, start, tnp]}; MouseButton[middle, down] => IF Editor.ShiftKey[up] AND ~IsSelected[thisEntry] THEN {trackerState _ adding; Consider[start, start, tnp]} ELSE IF Editor.ShiftKey[down] AND IsSelected[thisEntry] THEN {trackerState _ removing; Deconsider[start, start, tnp]}; (MouseButton[right, down] AND runs > 0) => BEGIN trackerState _ extending; runIndex _ FindRunArrayIndex[thisEntry, thisEntry]; [low, high] _ runArray[runIndex]; lowerRunHigh _ IF runIndex = 0 THEN 0 ELSE runArray[runIndex - 1].high; upperRunLow _ IF runIndex + 1 = runs THEN LAST[CARDINAL] ELSE runArray[runIndex + 1].low; SELECT thisEntry FROM <= low => {extendState _ addLow; floatC _ thisEntry; Consider[floatC, low - 1, tnp]}; < high => {extendState _ takeHigh; floatC _ thisEntry + 1; Deconsider[floatC, high - 1, tnp]}; ENDCASE => {extendState _ addHigh; floatC _ thisEntry+ 1; Consider[high, thisEntry, tnp]}; END; ENDCASE; END; startIsCocked => SELECT TRUE FROM (MouseButton[left, down] AND thisEntry # start) => {Deconsider[start, start, tnp]; start _ thisEntry; Consider[start, start, tnp]}; MouseButton[left, up] => {SetTOCSelection[start]; trackerState _ neutral; inD.UpdateTOCThumbLine[]}; ENDCASE; adding => SELECT TRUE FROM (MouseButton[middle, down] AND thisEntry # start) => {Deconsider[start, start, tnp]; IF ~IsSelected[thisEntry] THEN {start _ thisEntry; Consider[start, start, tnp]} ELSE trackerState _ neutral}; MouseButton[middle, up] => {AddRange[start, start + 1]; trackerState _ neutral; inD.UpdateTOCThumbLine[]}; ENDCASE; removing => SELECT TRUE FROM (MouseButton[middle, down] AND thisEntry # start) => {Consider[start, start, tnp]; IF IsSelected[thisEntry] THEN {start _ thisEntry; Deconsider[start, start, tnp]} ELSE trackerState _ neutral}; MouseButton[middle, up] => {RemoveRange[start, start + 1]; trackerState _ neutral; inD.UpdateTOCThumbLine[]}; ENDCASE; extending => IF MouseButton[right, down] THEN -- track -- SELECT extendState FROM addLow => BEGIN -- [floatC .. low) will be added. -- SELECT thisEntry FROM >= high, < lowerRunHigh => {Deconsider[floatC, low - 1, tnp]; trackerState _ neutral}; > low => {extendState _ takeLow; Deconsider[floatC, thisEntry - 1, tnp]}; > floatC => Deconsider[floatC, thisEntry - 1, tnp]; = floatC => NULL; ENDCASE => Consider[thisEntry, floatC, tnp]; floatC _ thisEntry; END; addHigh => BEGIN -- [high .. floatC) will be added. -- SELECT thisEntry FROM >= upperRunLow, < high => {Deconsider[high, floatC - 1, tnp]; trackerState _ neutral}; >= floatC => Consider[floatC, thisEntry, tnp]; = floatC - 1 => NULL; ENDCASE => Deconsider[thisEntry + 1, floatC -1, tnp]; floatC _ thisEntry + 1; END; takeLow => BEGIN -- [low .. floatC) will be removed. -- SELECT thisEntry FROM >= high, < lowerRunHigh => {Consider[low, floatC - 1, tnp]; trackerState _ neutral}; > floatC => Deconsider[floatC, thisEntry - 1, tnp]; = floatC => NULL; >= low => Consider[thisEntry, floatC - 1, tnp]; ENDCASE => {extendState _ addLow; Consider[thisEntry, floatC - 1, tnp]}; floatC _ thisEntry; END; takeHigh => BEGIN -- [floatC .. high) will be removed. -- SELECT thisEntry FROM >= high, < lowerRunHigh => {Consider[floatC, high - 1, tnp]; trackerState _ neutral}; >= floatC => Consider[floatC, thisEntry, tnp]; = floatC - 1 => NULL; >= low => Deconsider[thisEntry +1, floatC - 1, tnp]; ENDCASE => BEGIN extendState _ addLow; Consider[floatC, high - 1, tnp]; Consider[thisEntry, low - 1, tnp]; thisEntry _ thisEntry - 1; END; floatC _ thisEntry + 1; END; ENDCASE => ERROR ELSE -- right mouse button up -- BEGIN trackerState _ neutral; SELECT extendState FROM addLow => IF floatC < low THEN AddRange[floatC, low]; addHigh => IF floatC > high THEN AddRange[high, floatC]; takeLow => IF floatC > low THEN RemoveRange[low, floatC]; takeHigh => IF floatC < high THEN RemoveRange[floatC, high]; ENDCASE => ERROR; inD.UpdateTOCThumbLine[]; END; ENDCASE; IdleLoop[]; AcceptKeyboardInput[ ! UNWIND => BailOut[]]; ENDLOOP; END; -- of TOCTextTracker -- END. -- of TOCSelection --z20461(529)\f1