TextFrames.Mod.txt 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856
  1. MODULE TextFrames; (*JG 8.10.90 / NW 10.5.2013 / 11.2.2017*)
  2. IMPORT Modules, Input, Display, Viewers, Fonts, Texts, Oberon, MenuViewers;
  3. CONST replace* = 0; insert* = 1; delete* = 2; unmark* = 3; (*message id*)
  4. BS = 8X; TAB = 9X; CR = 0DX; DEL = 7FX;
  5. TYPE Line = POINTER TO LineDesc;
  6. LineDesc = RECORD
  7. len: LONGINT;
  8. wid: INTEGER;
  9. eot: BOOLEAN;
  10. next: Line
  11. END;
  12. Location* = RECORD
  13. org*, pos*: LONGINT;
  14. dx*, x*, y*: INTEGER;
  15. lin: Line
  16. END;
  17. Frame* = POINTER TO FrameDesc;
  18. FrameDesc* = RECORD
  19. (Display.FrameDesc)
  20. text*: Texts.Text;
  21. org*: LONGINT;
  22. col*: INTEGER;
  23. lsp*: INTEGER;
  24. left*, right*, top*, bot*: INTEGER;
  25. markH*: INTEGER;
  26. time*: LONGINT;
  27. hasCar*, hasSel*, hasMark: BOOLEAN;
  28. carloc*: Location;
  29. selbeg*, selend*: Location;
  30. trailer: Line
  31. END;
  32. UpdateMsg* = RECORD (Display.FrameMsg)
  33. id*: INTEGER;
  34. text*: Texts.Text;
  35. beg*, end*: LONGINT
  36. END;
  37. CopyOverMsg = RECORD (Display.FrameMsg)
  38. text: Texts.Text;
  39. beg, end: LONGINT
  40. END;
  41. VAR TBuf*, DelBuf: Texts.Buffer;
  42. menuH*, barW*, left*, right*, top*, bot*, lsp*: INTEGER; (*standard sizes*)
  43. asr, dsr, selH, markW, eolW: INTEGER;
  44. nextCh: CHAR;
  45. ScrollMarker: Oberon.Marker;
  46. W, KW: Texts.Writer; (*keyboard writer*)
  47. PROCEDURE Min (i, j: INTEGER): INTEGER;
  48. BEGIN IF i < j THEN j := i END ;
  49. RETURN j
  50. END Min;
  51. (*------------------display support------------------------*)
  52. PROCEDURE ReplConst (col: INTEGER; F: Frame; X, Y, W, H: INTEGER; mode: INTEGER);
  53. BEGIN
  54. IF X + W <= F.X + F.W THEN Display.ReplConst(col, X, Y, W, H, mode)
  55. ELSIF X < F.X + F.W THEN Display.ReplConst(col, X, Y, F.X + F.W - X, H, mode)
  56. END
  57. END ReplConst;
  58. PROCEDURE FlipSM(X, Y: INTEGER);
  59. VAR DW, DH, CL: INTEGER;
  60. BEGIN DW := Display.Width; DH := Display.Height; CL := DW;
  61. IF X < CL THEN
  62. IF X < 3 THEN X := 3 ELSIF X > DW - 4 THEN X := DW - 4 END
  63. ELSE
  64. IF X < CL + 3 THEN X := CL + 4 ELSIF X > CL + DW - 4 THEN X := CL + DW - 4 END
  65. END ;
  66. IF Y < 6 THEN Y := 6 ELSIF Y > DH - 6 THEN Y := DH - 6 END;
  67. Display.CopyPattern(Display.white, Display.updown, X-4, Y-4, Display.invert)
  68. END FlipSM;
  69. PROCEDURE UpdateMark (F: Frame); (*in scroll bar*)
  70. VAR oldH: INTEGER;
  71. BEGIN oldH := F.markH; F.markH := F.org * F.H DIV (F.text.len + 1);
  72. IF F.hasMark & (F.left >= barW) & (F.markH # oldH) THEN
  73. Display.ReplConst(Display.white, F.X + 1, F.Y + F.H - 1 - oldH, markW, 1, Display.invert);
  74. Display.ReplConst(Display.white, F.X + 1, F.Y + F.H - 1 - F.markH, markW, 1, Display.invert)
  75. END
  76. END UpdateMark;
  77. PROCEDURE SetChangeMark (F: Frame; on: BOOLEAN); (*in corner*)
  78. BEGIN
  79. IF F.H > menuH THEN
  80. IF on THEN Display.CopyPattern(Display.white, Display.block, F.X+F.W-12, F.Y+F.H-12, Display.paint)
  81. ELSE Display.ReplConst(F.col, F.X+F.W-12, F.Y+F.H-12, 8, 8, Display.replace)
  82. END
  83. END
  84. END SetChangeMark;
  85. PROCEDURE Width (VAR R: Texts.Reader; len: LONGINT): INTEGER;
  86. VAR patadr, pos: LONGINT; ox, dx, x, y, w, h: INTEGER;
  87. BEGIN pos := 0; ox := 0;
  88. WHILE pos < len DO
  89. Fonts.GetPat(R.fnt, nextCh, dx, x, y, w, h, patadr);
  90. ox := ox + dx; INC(pos); Texts.Read(R, nextCh)
  91. END;
  92. RETURN ox
  93. END Width;
  94. PROCEDURE DisplayLine (F: Frame; L: Line;
  95. VAR R: Texts.Reader; X, Y: INTEGER; len: LONGINT);
  96. VAR patadr, NX, dx, x, y, w, h: INTEGER;
  97. BEGIN NX := F.X + F.W;
  98. WHILE (nextCh # CR) & (R.fnt # NIL) DO
  99. Fonts.GetPat(R.fnt, nextCh, dx, x, y, w, h, patadr);
  100. IF (X + x + w <= NX) & (h # 0) THEN
  101. Display.CopyPattern(R.col, patadr, X + x, Y + y, Display.invert)
  102. END;
  103. X := X + dx; INC(len); Texts.Read(R, nextCh)
  104. END;
  105. L.len := len + 1; L.wid := X + eolW - (F.X + F.left);
  106. L.eot := R.fnt = NIL; Texts.Read(R, nextCh)
  107. END DisplayLine;
  108. PROCEDURE Validate (T: Texts.Text; VAR pos: LONGINT);
  109. VAR R: Texts.Reader;
  110. BEGIN
  111. IF pos > T.len THEN pos := T.len
  112. ELSIF pos > 0 THEN
  113. DEC(pos); Texts.OpenReader(R, T, pos);
  114. REPEAT Texts.Read(R, nextCh); INC(pos) UNTIL R.eot OR (nextCh = CR)
  115. ELSE pos := 0
  116. END
  117. END Validate;
  118. PROCEDURE Mark* (F: Frame; on: BOOLEAN);
  119. BEGIN
  120. IF (F.H > 0) & (F.left >= barW) & ((F.hasMark & ~on) OR (~F.hasMark & on)) THEN
  121. Display.ReplConst(Display.white, F.X + 1, F.Y + F.H - 1 - F.markH, markW, 1, Display.invert)
  122. END;
  123. F.hasMark := on
  124. END Mark;
  125. PROCEDURE Restore* (F: Frame);
  126. VAR R: Texts.Reader; L, l: Line; curY, botY: INTEGER;
  127. BEGIN Display.ReplConst(F.col, F.X, F.Y, F.W, F.H, Display.replace);
  128. IF F.left >= barW THEN
  129. Display.ReplConst(Display.white, F.X + barW - 1, F.Y, 1, F.H, Display.invert)
  130. END;
  131. Validate(F.text, F.org);
  132. botY := F.Y + F.bot + dsr;
  133. Texts.OpenReader(R, F.text, F.org); Texts.Read(R, nextCh);
  134. L := F.trailer; curY := F.Y + F.H - F.top - asr;
  135. WHILE ~L.eot & (curY >= botY) DO
  136. NEW(l);
  137. DisplayLine(F, l, R, F.X + F.left, curY, 0);
  138. L.next := l; L := l; curY := curY - lsp
  139. END;
  140. L.next := F.trailer;
  141. F.markH := F.org * F.H DIV (F.text.len + 1)
  142. END Restore;
  143. PROCEDURE Suspend* (F: Frame);
  144. BEGIN F.trailer.next := F.trailer
  145. END Suspend;
  146. PROCEDURE Extend* (F: Frame; newY: INTEGER);
  147. VAR R: Texts.Reader; L, l: Line;
  148. org: LONGINT; curY, botY: INTEGER;
  149. BEGIN Display.ReplConst(F.col, F.X, newY, F.W, F.Y - newY, Display.replace);
  150. IF F.left >= barW THEN
  151. Display.ReplConst(Display.white, F.X + barW - 1, newY, 1, F.Y - newY, Display.invert)
  152. END;
  153. botY := F.Y + F.bot + dsr; F.H := F.H + F.Y - newY; F.Y := newY;
  154. IF F.trailer.next = F.trailer THEN Validate(F.text, F.org) END;
  155. L := F.trailer; org := F.org; curY := F.Y + F.H - F.top - asr;
  156. WHILE (L.next # F.trailer) & (curY >= botY) DO
  157. L := L.next; org := org + L.len; curY := curY - lsp
  158. END;
  159. botY := F.Y + F.bot + dsr;
  160. Texts.OpenReader(R, F.text, org); Texts.Read(R, nextCh);
  161. WHILE ~L.eot & (curY >= botY) DO
  162. NEW(l);
  163. DisplayLine(F, l, R, F.X + F.left, curY, 0);
  164. L.next := l; L := l; curY := curY - lsp
  165. END;
  166. L.next := F.trailer;
  167. F.markH := F.org * F.H DIV (F.text.len + 1)
  168. END Extend;
  169. PROCEDURE Reduce* (F: Frame; newY: INTEGER);
  170. VAR L: Line; curY, botY: INTEGER;
  171. BEGIN F.H := F.H + F.Y - newY; F.Y := newY;
  172. botY := F.Y + F.bot + dsr;
  173. L := F.trailer; curY := F.Y + F.H - F.top - asr;
  174. WHILE (L.next # F.trailer) & (curY >= botY) DO
  175. L := L.next; curY := curY - lsp
  176. END;
  177. L.next := F.trailer;
  178. IF curY + asr > F.Y THEN
  179. Display.ReplConst(F.col, F.X + F.left, F.Y, F.W - F.left, curY + asr - F.Y, Display.replace)
  180. END;
  181. F.markH := F.org * F.H DIV (F.text.len + 1); Mark(F, TRUE)
  182. END Reduce;
  183. PROCEDURE Show* (F: Frame; pos: LONGINT);
  184. VAR R: Texts.Reader; L, L0: Line;
  185. org: LONGINT; curY, botY, Y0: INTEGER;
  186. BEGIN
  187. IF F.trailer.next # F.trailer THEN
  188. Validate(F.text, pos);
  189. IF pos < F.org THEN Mark(F, FALSE);
  190. Display.ReplConst(F.col, F.X + F.left, F.Y, F.W - F.left, F.H, Display.replace);
  191. botY := F.Y; F.Y := F.Y + F.H; F.H := 0;
  192. F.org := pos; F.trailer.next := F.trailer; Extend(F, botY); Mark(F, TRUE)
  193. ELSIF pos > F.org THEN
  194. org := F.org; L := F.trailer.next; curY := F.Y + F.H - F.top - asr;
  195. WHILE (L.next # F.trailer) & (org # pos) DO
  196. org := org + L.len; L := L.next; curY := curY - lsp;
  197. END;
  198. IF org = pos THEN
  199. F.org := org; F.trailer.next := L; Y0 := curY;
  200. WHILE L.next # F.trailer DO (*!*)
  201. org := org + L.len; L := L.next; curY := curY - lsp
  202. END;
  203. Display.CopyBlock (F.X + F.left, curY - dsr, F.W - F.left, Y0 + asr - (curY - dsr),
  204. F.X + F.left, curY - dsr + F.Y + F.H - F.top - asr - Y0, 0);
  205. curY := curY + F.Y + F.H - F.top - asr - Y0;
  206. Display.ReplConst(F.col, F.X + F.left, F.Y, F.W - F.left, curY - dsr - F.Y, Display.replace);
  207. botY := F.Y + F.bot + dsr;
  208. org := org + L.len; curY := curY - lsp;
  209. Texts.OpenReader(R, F.text, org); Texts.Read(R, nextCh);
  210. WHILE ~L.eot & (curY >= botY) DO
  211. NEW(L0); DisplayLine(F, L0, R, F.X + F.left, curY, 0);
  212. L.next := L0; L := L0; curY := curY - lsp
  213. END;
  214. L.next := F.trailer; UpdateMark(F)
  215. ELSE Mark(F, FALSE);
  216. Display.ReplConst(F.col, F.X + F.left, F.Y, F.W - F.left, F.H, Display.replace);
  217. botY := F.Y; F.Y := F.Y + F.H; F.H := 0;
  218. F.org := pos; F.trailer.next := F.trailer; Extend(F, botY);
  219. Mark(F, TRUE)
  220. END
  221. END
  222. END ;
  223. SetChangeMark(F, F.text.changed)
  224. END Show;
  225. PROCEDURE LocateLine (F: Frame; y: INTEGER; VAR loc: Location);
  226. VAR L: Line; org: LONGINT; cury: INTEGER;
  227. BEGIN org := F.org; L := F.trailer.next; cury := F.H - F.top - asr;
  228. WHILE (L.next # F.trailer) & (cury > y + dsr) DO
  229. org := org + L.len; L := L.next; cury := cury - lsp
  230. END;
  231. loc.org := org; loc.lin := L; loc.y := cury
  232. END LocateLine;
  233. PROCEDURE LocateString (F: Frame; x, y: INTEGER; VAR loc: Location);
  234. VAR R: Texts.Reader;
  235. patadr, bpos, pos, lim: LONGINT;
  236. bx, ex, ox, dx, u, v, w, h: INTEGER;
  237. BEGIN LocateLine(F, y, loc);
  238. lim := loc.org + loc.lin.len - 1;
  239. bpos := loc.org; bx := F.left;
  240. pos := loc.org; ox := F.left;
  241. Texts.OpenReader(R, F.text, loc.org); Texts.Read(R, nextCh);
  242. REPEAT
  243. WHILE (pos # lim) & (nextCh > " ") DO (*scan string*)
  244. Fonts.GetPat(R.fnt, nextCh, dx, u, v, w, h, patadr);
  245. INC(pos); ox := ox + dx; Texts.Read(R, nextCh)
  246. END;
  247. ex := ox;
  248. WHILE (pos # lim) & (nextCh <= " ") DO (*scan gap*)
  249. Fonts.GetPat(R.fnt, nextCh, dx, u, v, w, h, patadr);
  250. INC(pos); ox := ox + dx; Texts.Read(R, nextCh)
  251. END;
  252. IF (pos # lim) & (ox <= x) THEN
  253. Fonts.GetPat(R.fnt, nextCh, dx, u, v, w, h, patadr);
  254. bpos := pos; bx := ox;
  255. INC(pos); ox := ox + dx; Texts.Read(R, nextCh)
  256. ELSE pos := lim
  257. END
  258. UNTIL pos = lim;
  259. loc.pos := bpos; loc.dx := ex - bx; loc.x := bx
  260. END LocateString;
  261. PROCEDURE LocateChar (F: Frame; x, y: INTEGER; VAR loc: Location);
  262. VAR R: Texts.Reader;
  263. patadr, pos, lim: LONGINT;
  264. ox, dx, u, v, w, h: INTEGER;
  265. BEGIN LocateLine(F, y, loc);
  266. lim := loc.org + loc.lin.len - 1;
  267. pos := loc.org; ox := F.left; dx := eolW;
  268. Texts.OpenReader(R, F.text, loc.org);
  269. WHILE pos # lim DO
  270. Texts.Read(R, nextCh);
  271. Fonts.GetPat(R.fnt, nextCh, dx, u, v, w, h, patadr);
  272. IF ox + dx <= x THEN
  273. INC(pos); ox := ox + dx;
  274. IF pos = lim THEN dx := eolW END
  275. ELSE lim := pos
  276. END
  277. END ;
  278. loc.pos := pos; loc.dx := dx; loc.x := ox
  279. END LocateChar;
  280. PROCEDURE LocatePos (F: Frame; pos: LONGINT; VAR loc: Location);
  281. VAR T: Texts.Text; R: Texts.Reader; L: Line;
  282. org: LONGINT; cury: INTEGER;
  283. BEGIN T := F.text;
  284. org := F.org; L := F.trailer.next; cury := F.H - F.top - asr;
  285. IF pos < org THEN pos := org END;
  286. WHILE (L.next # F.trailer) & (pos >= org + L.len) DO
  287. org := org + L.len; L := L.next; cury := cury - lsp
  288. END;
  289. IF pos >= org + L.len THEN pos := org + L.len - 1 END;
  290. Texts.OpenReader(R, T, org); Texts.Read(R, nextCh);
  291. loc.org := org; loc.pos := pos; loc.lin := L;
  292. loc.x := F.left + Width(R, pos - org); loc.y := cury
  293. END LocatePos;
  294. PROCEDURE Pos* (F: Frame; X, Y: INTEGER): LONGINT;
  295. VAR loc: Location;
  296. BEGIN LocateChar(F, X - F.X, Y - F.Y, loc); RETURN loc.pos
  297. END Pos;
  298. PROCEDURE FlipCaret (F: Frame);
  299. BEGIN
  300. IF (F.carloc.x < F.W) & (F.carloc.y >= 10) & (F.carloc.x + 12 < F.W) THEN
  301. Display.CopyPattern(Display.white, Display.hook, F.X + F.carloc.x, F.Y + F.carloc.y - 10, Display.invert)
  302. END
  303. END FlipCaret;
  304. PROCEDURE SetCaret* (F: Frame; pos: LONGINT);
  305. BEGIN LocatePos(F, pos, F.carloc); FlipCaret(F); F.hasCar := TRUE
  306. END SetCaret;
  307. PROCEDURE TrackCaret* (F: Frame; X, Y: INTEGER; VAR keysum: SET);
  308. VAR loc: Location; keys: SET;
  309. BEGIN
  310. IF F.trailer.next # F.trailer THEN
  311. LocateChar(F, X - F.X, Y - F.Y, F.carloc);
  312. FlipCaret(F); keysum := {};
  313. REPEAT Input.Mouse(keys, X, Y); keysum := keysum + keys;
  314. Oberon.DrawMouseArrow(X, Y); LocateChar(F, X - F.X, Y - F.Y, loc);
  315. IF loc.pos # F.carloc.pos THEN FlipCaret(F); F.carloc := loc; FlipCaret(F) END
  316. UNTIL keys = {};
  317. F.hasCar := TRUE
  318. END
  319. END TrackCaret;
  320. PROCEDURE RemoveCaret* (F: Frame);
  321. BEGIN IF F.hasCar THEN FlipCaret(F); F.hasCar := FALSE END
  322. END RemoveCaret;
  323. PROCEDURE FlipSelection (F: Frame; VAR beg, end: Location);
  324. VAR L: Line; Y: INTEGER;
  325. BEGIN L := beg.lin; Y := F.Y + beg.y - 2;
  326. IF L = end.lin THEN ReplConst(Display.white, F, F.X + beg.x, Y, end.x - beg.x, selH, Display.invert)
  327. ELSE
  328. ReplConst(Display.white, F, F.X + beg.x, Y, F.left + L.wid - beg.x, selH, Display.invert);
  329. L := L.next; Y := Y - lsp;
  330. WHILE L # end.lin DO
  331. ReplConst(Display.white, F, F.X + F.left, Y, L.wid, selH, Display.invert);
  332. L := L.next; Y := Y - lsp
  333. END;
  334. ReplConst(Display.white, F, F.X + F.left, Y, end.x - F.left, selH, Display.invert)
  335. END
  336. END FlipSelection;
  337. PROCEDURE SetSelection* (F: Frame; beg, end: LONGINT);
  338. BEGIN
  339. IF F.hasSel THEN FlipSelection(F, F.selbeg, F.selend) END;
  340. LocatePos(F, beg, F.selbeg); LocatePos(F, end, F.selend);
  341. IF F.selbeg.pos < F.selend.pos THEN
  342. FlipSelection(F, F.selbeg, F.selend); F.time := Oberon.Time(); F.hasSel := TRUE
  343. END
  344. END SetSelection;
  345. PROCEDURE TrackSelection* (F: Frame; X, Y: INTEGER; VAR keysum: SET);
  346. VAR loc: Location; keys: SET;
  347. BEGIN
  348. IF F.trailer.next # F.trailer THEN
  349. IF F.hasSel THEN FlipSelection(F, F.selbeg, F.selend) END;
  350. LocateChar(F, X - F.X, Y - F.Y, loc);
  351. IF F.hasSel & (loc.pos = F.selbeg.pos) & (F.selend.pos = F.selbeg.pos + 1) THEN
  352. LocateChar(F, F.left, Y - F.Y, F.selbeg)
  353. ELSE F.selbeg := loc
  354. END;
  355. INC(loc.pos); loc.x := loc.x + loc.dx; F.selend := loc;
  356. FlipSelection(F, F.selbeg, F.selend); keysum := {};
  357. REPEAT
  358. Input.Mouse(keys, X, Y);
  359. keysum := keysum + keys;
  360. Oberon.DrawMouseArrow(X, Y);
  361. LocateChar(F, X - F.X, Y - F.Y, loc);
  362. IF loc.pos < F.selbeg.pos THEN loc := F.selbeg END;
  363. INC(loc.pos); loc.x := loc.x + loc.dx;
  364. IF loc.pos < F.selend.pos THEN FlipSelection(F, loc, F.selend); F.selend := loc
  365. ELSIF loc.pos > F.selend.pos THEN FlipSelection(F, F.selend, loc); F.selend := loc
  366. END
  367. UNTIL keys = {};
  368. F.time := Oberon.Time(); F.hasSel := TRUE
  369. END
  370. END TrackSelection;
  371. PROCEDURE RemoveSelection* (F: Frame);
  372. BEGIN IF F.hasSel THEN FlipSelection(F, F.selbeg, F.selend); F.hasSel := FALSE END
  373. END RemoveSelection;
  374. PROCEDURE TrackLine* (F: Frame; X, Y: INTEGER; VAR org: LONGINT; VAR keysum: SET);
  375. VAR old, new: Location; keys: SET;
  376. BEGIN
  377. IF F.trailer.next # F.trailer THEN
  378. LocateLine(F, Y - F.Y, old);
  379. ReplConst(Display.white, F, F.X + F.left, F.Y + old.y - dsr, old.lin.wid, 2, Display.invert);
  380. keysum := {};
  381. REPEAT Input.Mouse(keys, X, Y);
  382. keysum := keysum + keys;
  383. Oberon.DrawMouse(ScrollMarker, X, Y);
  384. LocateLine(F, Y - F.Y, new);
  385. IF new.org # old.org THEN
  386. ReplConst(Display.white, F, F.X + F.left, F.Y + old.y - dsr, old.lin.wid, 2, Display.invert);
  387. ReplConst(Display.white, F, F.X + F.left, F.Y + new.y - dsr, new.lin.wid, 2, Display.invert);
  388. old := new
  389. END
  390. UNTIL keys = {};
  391. ReplConst(Display.white, F, F.X + F.left, F.Y + new.y - dsr, new.lin.wid, 2, Display.invert);
  392. org := new.org
  393. ELSE org := 0 (*<----*)
  394. END
  395. END TrackLine;
  396. PROCEDURE TrackWord* (F: Frame; X, Y: INTEGER; VAR pos: LONGINT; VAR keysum: SET);
  397. VAR old, new: Location; keys: SET;
  398. BEGIN
  399. IF F.trailer.next # F.trailer THEN
  400. LocateString(F, X - F.X, Y - F.Y, old);
  401. ReplConst(Display.white, F, F.X + old.x, F.Y + old.y - dsr, old.dx, 2, Display.invert);
  402. keysum := {};
  403. REPEAT
  404. Input.Mouse(keys, X, Y); keysum := keysum + keys;
  405. Oberon.DrawMouseArrow(X, Y);
  406. LocateString(F, X - F.X, Y - F.Y, new);
  407. IF new.pos # old.pos THEN
  408. ReplConst(Display.white, F, F.X + old.x, F.Y + old.y - dsr, old.dx, 2, Display.invert);
  409. ReplConst(Display.white, F, F.X + new.x, F.Y + new.y - dsr, new.dx, 2, Display.invert);
  410. old := new
  411. END
  412. UNTIL keys = {};
  413. ReplConst(Display.white, F, F.X + new.x, F.Y + new.y - dsr, new.dx, 2, Display.invert);
  414. pos := new.pos
  415. ELSE pos := 0 (*<----*)
  416. END
  417. END TrackWord;
  418. PROCEDURE Replace* (F: Frame; beg, end: LONGINT);
  419. VAR R: Texts.Reader; L: Line;
  420. org, len: LONGINT; curY, wid: INTEGER;
  421. BEGIN
  422. IF end > F.org THEN
  423. IF beg < F.org THEN beg := F.org END;
  424. org := F.org; L := F.trailer.next; curY := F.Y + F.H - F.top - asr;
  425. WHILE (L # F.trailer) & (org + L.len <= beg) DO
  426. org := org + L.len; L := L.next; curY := curY - lsp
  427. END;
  428. IF L # F.trailer THEN
  429. Texts.OpenReader(R, F.text, org); Texts.Read(R, nextCh);
  430. len := beg - org; wid := Width(R, len);
  431. ReplConst(F.col, F, F.X + F.left + wid, curY - dsr, L.wid - wid, lsp, Display.replace);
  432. DisplayLine(F, L, R, F.X + F.left + wid, curY, len);
  433. org := org + L.len; L := L.next; curY := curY - lsp;
  434. WHILE (L # F.trailer) & (org <= end) DO
  435. Display.ReplConst(F.col, F.X + F.left, curY - dsr, F.W - F.left, lsp, Display.replace);
  436. DisplayLine(F, L, R, F.X + F.left, curY, 0);
  437. org := org + L.len; L := L.next; curY := curY - lsp
  438. END
  439. END
  440. END;
  441. UpdateMark(F)
  442. END Replace;
  443. PROCEDURE Insert* (F: Frame; beg, end: LONGINT);
  444. VAR R: Texts.Reader; L, L0, l: Line;
  445. org, len: LONGINT; curY, botY, Y0, Y1, Y2, dY, wid: INTEGER;
  446. BEGIN
  447. IF beg < F.org THEN F.org := F.org + (end - beg)
  448. ELSE
  449. org := F.org; L := F.trailer.next; curY := F.Y + F.H - F.top - asr;
  450. WHILE (L # F.trailer) & (org + L.len <= beg) DO
  451. org := org + L.len; L := L.next; curY := curY - lsp
  452. END;
  453. IF L # F.trailer THEN
  454. botY := F.Y + F.bot + dsr;
  455. Texts.OpenReader(R, F.text, org); Texts.Read(R, nextCh);
  456. len := beg - org; wid := Width(R, len);
  457. ReplConst (F.col, F, F.X + F.left + wid, curY - dsr, L.wid - wid, lsp, Display.replace);
  458. DisplayLine(F, L, R, F.X + F.left + wid, curY, len);
  459. org := org + L.len; curY := curY - lsp;
  460. Y0 := curY; L0 := L.next;
  461. WHILE (org <= end) & (curY >= botY) DO
  462. NEW(l);
  463. Display.ReplConst(F.col, F.X + F.left, curY - dsr, F.W - F.left, lsp, Display.replace);
  464. DisplayLine(F, l, R, F.X + F.left, curY, 0);
  465. L.next := l; L := l;
  466. org := org + L.len; curY := curY - lsp
  467. END;
  468. IF L0 # L.next THEN Y1 := curY;
  469. L.next := L0;
  470. WHILE (L.next # F.trailer) & (curY >= botY) DO
  471. L := L.next; curY := curY - lsp
  472. END;
  473. L.next := F.trailer;
  474. dY := Y0 - Y1;
  475. IF Y1 > curY + dY THEN
  476. Display.CopyBlock(F.X + F.left, curY + dY + lsp - dsr, F.W - F.left, Y1 - curY - dY,
  477. F.X + F.left, curY + lsp - dsr, 0);
  478. Y2 := Y1 - dY
  479. ELSE Y2 := curY
  480. END;
  481. curY := Y1; L := L0;
  482. WHILE curY # Y2 DO
  483. Display.ReplConst(F.col, F.X + F.left, curY - dsr, F.W - F.left, lsp, Display.replace);
  484. DisplayLine(F, L, R, F.X + F.left, curY, 0);
  485. L := L.next; curY := curY - lsp
  486. END
  487. END
  488. END
  489. END;
  490. UpdateMark(F)
  491. END Insert;
  492. PROCEDURE Delete* (F: Frame; beg, end: LONGINT);
  493. VAR R: Texts.Reader; L, L0, l: Line;
  494. org, org0, len: LONGINT; curY, botY, Y0, Y1, wid: INTEGER;
  495. BEGIN
  496. IF end <= F.org THEN F.org := F.org - (end - beg)
  497. ELSE
  498. IF beg < F.org THEN
  499. F.trailer.next.len := F.trailer.next.len + (F.org - beg);
  500. F.org := beg
  501. END;
  502. org := F.org; L := F.trailer.next; curY := F.Y + F.H - F.top - asr;
  503. WHILE (L # F.trailer) & (org + L.len <= beg) DO
  504. org := org + L.len; L := L.next; curY := curY - lsp
  505. END;
  506. IF L # F.trailer THEN
  507. botY := F.Y + F.bot + dsr;
  508. org0 := org; L0 := L; Y0 := curY;
  509. WHILE (L # F.trailer) & (org <= end) DO
  510. org := org + L.len; L := L.next; curY := curY - lsp
  511. END;
  512. Y1 := curY;
  513. Texts.OpenReader(R, F.text, org0); Texts.Read(R, nextCh);
  514. len := beg - org0; wid := Width(R, len);
  515. ReplConst (F.col, F, F.X + F.left + wid, Y0 - dsr, L0.wid - wid, lsp, Display.replace);
  516. DisplayLine(F, L0, R, F.X + F.left + wid, Y0, len);
  517. Y0 := Y0 - lsp;
  518. IF L # L0.next THEN
  519. L0.next := L;
  520. L := L0; org := org0 + L0.len;
  521. WHILE L.next # F.trailer DO
  522. L := L.next; org := org + L.len; curY := curY - lsp
  523. END;
  524. Display.CopyBlock(F.X + F.left, curY + lsp - dsr, F.W - F.left, Y1 - curY,
  525. F.X + F.left, curY + lsp - dsr + (Y0 - Y1), 0);
  526. curY := curY + (Y0 - Y1);
  527. Display.ReplConst (F.col, F.X + F.left, F.Y, F.W - F.left, curY + lsp - (F.Y + dsr), Display.replace);
  528. Texts.OpenReader(R, F.text, org); Texts.Read(R, nextCh);
  529. WHILE ~L.eot & (curY >= botY) DO
  530. NEW(l);
  531. DisplayLine(F, l, R, F.X + F.left, curY, 0);
  532. L.next := l; L := l; curY := curY - lsp
  533. END;
  534. L.next := F.trailer
  535. END
  536. END
  537. END;
  538. UpdateMark(F)
  539. END Delete;
  540. PROCEDURE Recall*(VAR B: Texts.Buffer);
  541. BEGIN B := TBuf; NEW(TBuf); Texts.OpenBuf(TBuf)
  542. END Recall;
  543. (*------------------message handling------------------------*)
  544. PROCEDURE RemoveMarks (F: Frame);
  545. BEGIN RemoveCaret(F); RemoveSelection(F)
  546. END RemoveMarks;
  547. PROCEDURE NotifyDisplay* (T: Texts.Text; op: INTEGER; beg, end: LONGINT);
  548. VAR M: UpdateMsg;
  549. BEGIN M.id := op; M.text := T; M.beg := beg; M.end := end; Viewers.Broadcast(M)
  550. END NotifyDisplay;
  551. PROCEDURE Call* (F: Frame; pos: LONGINT; new: BOOLEAN);
  552. VAR S: Texts.Scanner; res: INTEGER;
  553. BEGIN
  554. Texts.OpenScanner(S, F.text, pos); Texts.Scan(S);
  555. IF (S.class = Texts.Name) & (S.line = 0) THEN
  556. Oberon.SetPar(F, F.text, pos + S.len); Oberon.Call(S.s, res);
  557. IF res > 0 THEN
  558. Texts.WriteString(W, "Call error: "); Texts.WriteString(W, Modules.importing);
  559. IF res = 1 THEN Texts.WriteString(W, " module not found")
  560. ELSIF res = 2 THEN Texts.WriteString(W, " bad version")
  561. ELSIF res = 3 THEN Texts.WriteString(W, " imports ");
  562. Texts.WriteString(W, Modules.imported); Texts.WriteString(W, " with bad key");
  563. ELSIF res = 4 THEN Texts.WriteString(W, " corrupted obj file")
  564. ELSIF res = 5 THEN Texts.WriteString(W, " command not found")
  565. ELSIF res = 7 THEN Texts.WriteString(W, " insufficient space")
  566. END;
  567. Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf)
  568. END
  569. END
  570. END Call;
  571. PROCEDURE Write* (F: Frame; ch: CHAR; fnt: Fonts.Font; col, voff: INTEGER);
  572. VAR buf: Texts.Buffer;
  573. BEGIN (*F.hasCar*)
  574. IF ch = BS THEN (*backspace*)
  575. IF F.carloc.pos > F.org THEN
  576. Texts.Delete(F.text, F.carloc.pos - 1, F.carloc.pos, DelBuf); SetCaret(F, F.carloc.pos - 1)
  577. END
  578. ELSIF ch = 3X THEN (* ctrl-c copy*)
  579. IF F.hasSel THEN
  580. NEW(TBuf); Texts.OpenBuf(TBuf); Texts.Save(F.text, F.selbeg.pos, F.selend.pos, TBuf)
  581. END
  582. ELSIF ch = 16X THEN (*ctrl-v paste*)
  583. NEW(buf); Texts.OpenBuf(buf); Texts.Copy(TBuf, buf); Texts.Insert(F.text, F.carloc.pos, buf);
  584. SetCaret(F, F.carloc.pos + TBuf.len)
  585. ELSIF ch = 18X THEN (*ctrl-x, cut*)
  586. IF F.hasSel THEN
  587. NEW(TBuf); Texts.OpenBuf(TBuf); Texts.Delete(F.text, F.selbeg.pos, F.selend.pos, TBuf)
  588. END
  589. ELSIF (20X <= ch) & (ch <= DEL) OR (ch = CR) OR (ch = TAB) THEN
  590. KW.fnt := fnt; KW.col := col; KW.voff := voff; Texts.Write(KW, ch);
  591. Texts.Insert(F.text, F.carloc.pos, KW.buf);
  592. SetCaret(F, F.carloc.pos + 1)
  593. END
  594. END Write;
  595. PROCEDURE Defocus* (F: Frame);
  596. BEGIN RemoveCaret(F)
  597. END Defocus;
  598. PROCEDURE Neutralize* (F: Frame);
  599. BEGIN RemoveMarks(F)
  600. END Neutralize;
  601. PROCEDURE Modify* (F: Frame; id, dY, Y, H: INTEGER);
  602. BEGIN
  603. Mark(F, FALSE); RemoveMarks(F); SetChangeMark(F, FALSE);
  604. IF id = MenuViewers.extend THEN
  605. IF dY > 0 THEN Display.CopyBlock(F.X, F.Y, F.W, F.H, F.X, F.Y + dY, 0); F.Y := F.Y + dY END;
  606. Extend(F, Y)
  607. ELSIF id = MenuViewers.reduce THEN
  608. Reduce(F, Y + dY);
  609. IF dY > 0 THEN Display.CopyBlock(F.X, F.Y, F.W, F.H, F.X, Y, 0); F.Y := Y END
  610. END;
  611. IF F.H > 0 THEN Mark(F, TRUE); SetChangeMark(F, F.text.changed) END
  612. END Modify;
  613. PROCEDURE Open* (F: Frame; H: Display.Handler; T: Texts.Text; org: LONGINT;
  614. col, left, right, top, bot, lsp: INTEGER);
  615. VAR L: Line;
  616. BEGIN NEW(L);
  617. L.len := 0; L.wid := 0; L.eot := FALSE; L.next := L;
  618. F.handle := H; F.text := T; F.org := org; F.trailer := L;
  619. F.left := left; F.right := right; F.top := top; F.bot := bot;
  620. F.lsp := lsp; F.col := col; F.hasMark := FALSE; F.hasCar := FALSE; F.hasSel := FALSE
  621. END Open;
  622. PROCEDURE Copy* (F: Frame; VAR F1: Frame);
  623. BEGIN NEW(F1);
  624. Open(F1, F.handle, F.text, F.org, F.col, F.left, F.right, F.top, F.bot, F.lsp)
  625. END Copy;
  626. PROCEDURE CopyOver(F: Frame; text: Texts.Text; beg, end: LONGINT);
  627. VAR buf: Texts.Buffer;
  628. BEGIN
  629. IF F.hasCar THEN
  630. NEW(buf); Texts.OpenBuf(buf);
  631. Texts.Save(text, beg, end, buf); Texts.Insert(F.text, F.carloc.pos, buf);
  632. SetCaret(F, F.carloc.pos + (end - beg))
  633. END
  634. END CopyOver;
  635. PROCEDURE GetSelection* (F: Frame; VAR text: Texts.Text; VAR beg, end, time: LONGINT);
  636. BEGIN
  637. IF F.hasSel THEN
  638. IF F.text = text THEN
  639. IF F.selbeg.pos < beg THEN beg := F.selbeg.pos END ; (*leftmost*)
  640. IF F.time > time THEN end := F.selend.pos; time := F.time END ; (*last selected*)
  641. ELSIF F.time > time THEN
  642. text := F.text; beg := F.selbeg.pos; end := F.selend.pos; time := F.time
  643. END
  644. END
  645. END GetSelection;
  646. PROCEDURE Update* (F: Frame; VAR M: UpdateMsg);
  647. BEGIN (*F.text = M.text*) SetChangeMark(F, FALSE);
  648. RemoveMarks(F); Oberon.RemoveMarks(F.X, F.Y, F.W, F.H);
  649. IF M.id = replace THEN Replace(F, M.beg, M.end)
  650. ELSIF M.id = insert THEN Insert(F, M.beg, M.end)
  651. ELSIF M.id = delete THEN Delete(F, M.beg, M.end)
  652. END ;
  653. SetChangeMark(F, F.text.changed)
  654. END Update;
  655. PROCEDURE Edit* (F: Frame; X, Y: INTEGER; Keys: SET);
  656. VAR M: CopyOverMsg;
  657. text: Texts.Text;
  658. buf: Texts.Buffer;
  659. v: Viewers.Viewer;
  660. beg, end, time, pos: LONGINT;
  661. keysum: SET;
  662. fnt: Fonts.Font;
  663. col, voff: INTEGER;
  664. BEGIN
  665. IF X < F.X + Min(F.left, barW) THEN (*scroll bar*)
  666. Oberon.DrawMouse(ScrollMarker, X, Y); keysum := Keys;
  667. IF Keys = {2} THEN (*ML, scroll up*)
  668. TrackLine(F, X, Y, pos, keysum);
  669. IF (pos >= 0) & (keysum = {2}) THEN
  670. SetChangeMark(F, FALSE);
  671. RemoveMarks(F); Oberon.RemoveMarks(F.X, F.Y, F.W, F.H);
  672. Show(F, pos)
  673. END
  674. ELSIF Keys = {1} THEN (*MM*) keysum := Keys;
  675. REPEAT Input.Mouse(Keys, X, Y); keysum := keysum + Keys;
  676. Oberon.DrawMouse(ScrollMarker, X, Y)
  677. UNTIL Keys = {};
  678. IF keysum # {0, 1, 2} THEN
  679. IF 0 IN keysum THEN pos := 0
  680. ELSIF 2 IN keysum THEN pos := F.text.len - 100
  681. ELSE pos := (F.Y + F.H - Y) * (F.text.len) DIV F.H
  682. END ;
  683. SetChangeMark(F, FALSE);
  684. RemoveMarks(F); Oberon.RemoveMarks(F.X, F.Y, F.W, F.H);
  685. Show(F, pos)
  686. END
  687. ELSIF Keys = {0} THEN (*MR, scroll down*)
  688. TrackLine(F, X, Y, pos, keysum);
  689. IF keysum = {0} THEN
  690. SetChangeMark(F, FALSE);
  691. RemoveMarks(F); Oberon.RemoveMarks(F.X, F.Y, F.W, F.H);
  692. Show(F, F.org*2 - pos - 100)
  693. END
  694. END
  695. ELSE (*text area*)
  696. Oberon.DrawMouseArrow(X, Y);
  697. IF 0 IN Keys THEN (*MR: select*)
  698. TrackSelection(F, X, Y, keysum);
  699. IF F.hasSel THEN
  700. IF keysum = {0, 2} THEN (*MR, ML: delete text*)
  701. Oberon.GetSelection(text, beg, end, time);
  702. Texts.Delete(text, beg, end, TBuf);
  703. Oberon.PassFocus(Viewers.This(F.X, F.Y)); SetCaret(F, beg)
  704. ELSIF keysum = {0, 1} THEN (*MR, MM: copy to caret*)
  705. Oberon.GetSelection(text, beg, end, time);
  706. M.text := text; M.beg := beg; M.end := end;
  707. Oberon.FocusViewer.handle(Oberon.FocusViewer, M)
  708. END
  709. END
  710. ELSIF 1 IN Keys THEN (*MM: call*)
  711. TrackWord(F, X, Y, pos, keysum);
  712. IF (pos >= 0) & ~(0 IN keysum) THEN Call(F, pos, 2 IN keysum) END
  713. ELSIF 2 IN Keys THEN (*ML: set caret*)
  714. Oberon.PassFocus(Viewers.This(F.X, F.Y));
  715. TrackCaret(F, X, Y, keysum);
  716. IF keysum = {2, 1} THEN (*ML, MM: copy from selection to caret*)
  717. Oberon.GetSelection(text, beg, end, time);
  718. IF time >= 0 THEN
  719. NEW(TBuf); Texts.OpenBuf(TBuf);
  720. Texts.Save(text, beg, end, TBuf); Texts.Insert(F.text, F.carloc.pos, TBuf);
  721. SetSelection(F, F.carloc.pos, F.carloc.pos + (end - beg));
  722. SetCaret(F, F.carloc.pos + (end - beg))
  723. ELSIF TBuf # NIL THEN
  724. NEW(buf); Texts.OpenBuf(buf);
  725. Texts.Copy(TBuf, buf); Texts.Insert(F.text, F.carloc.pos, buf);
  726. SetCaret(F, F.carloc.pos + buf.len)
  727. END
  728. ELSIF keysum = {2, 0} THEN (*ML, MR: copy looks*)
  729. Oberon.GetSelection(text, beg, end, time);
  730. IF time >= 0 THEN
  731. Texts.Attributes(F.text, F.carloc.pos, fnt, col, voff);
  732. IF fnt # NIL THEN Texts.ChangeLooks(text, beg, end, {0,1,2}, fnt, col, voff) END
  733. END
  734. END
  735. END
  736. END
  737. END Edit;
  738. PROCEDURE Handle* (F: Display.Frame; VAR M: Display.FrameMsg);
  739. VAR F1: Frame; buf: Texts.Buffer;
  740. BEGIN
  741. CASE F OF Frame:
  742. CASE M OF
  743. Oberon.InputMsg:
  744. IF M.id = Oberon.track THEN Edit(F, M.X, M.Y, M.keys)
  745. ELSIF M.id = Oberon.consume THEN
  746. IF F.hasCar THEN Write(F, M.ch, M.fnt, M.col, M.voff) END
  747. END |
  748. Oberon.ControlMsg:
  749. IF M.id = Oberon.defocus THEN Defocus(F)
  750. ELSIF M.id = Oberon.neutralize THEN Neutralize(F)
  751. END |
  752. Oberon.SelectionMsg:
  753. GetSelection(F, M.text, M.beg, M.end, M.time) |
  754. Oberon.CopyMsg: Copy(F, F1); M.F := F1 |
  755. MenuViewers.ModifyMsg: Modify(F, M.id, M.dY, M.Y, M.H) |
  756. CopyOverMsg: CopyOver(F, M.text, M.beg, M.end) |
  757. UpdateMsg: IF F.text = M.text THEN Update(F, M) END
  758. END
  759. END
  760. END Handle;
  761. (*creation*)
  762. PROCEDURE Menu (name, commands: ARRAY OF CHAR): Texts.Text;
  763. VAR T: Texts.Text;
  764. BEGIN NEW(T); T.notify := NotifyDisplay; Texts.Open(T, "");
  765. Texts.WriteString(W, name); Texts.WriteString(W, " | "); Texts.WriteString(W, commands);
  766. Texts.Append(T, W.buf); RETURN T
  767. END Menu;
  768. PROCEDURE Text* (name: ARRAY OF CHAR): Texts.Text;
  769. VAR T: Texts.Text;
  770. BEGIN NEW(T); T.notify := NotifyDisplay; Texts.Open(T, name); RETURN T
  771. END Text;
  772. PROCEDURE NewMenu* (name, commands: ARRAY OF CHAR): Frame;
  773. VAR F: Frame; T: Texts.Text;
  774. BEGIN NEW(F); T := Menu(name, commands);
  775. Open(F, Handle, T, 0, Display.white, left DIV 4, 0, 0, 0, lsp); RETURN F
  776. END NewMenu;
  777. PROCEDURE NewText* (text: Texts.Text; pos: LONGINT): Frame;
  778. VAR F: Frame;
  779. BEGIN NEW(F);
  780. Open(F, Handle, text, pos, Display.black, left, right, top, bot, lsp); RETURN F
  781. END NewText;
  782. BEGIN NEW(TBuf); NEW(DelBuf);
  783. Texts.OpenBuf(TBuf); Texts.OpenBuf(DelBuf);
  784. lsp := Fonts.Default.height; menuH := lsp + 2; barW := menuH;
  785. left := barW + lsp DIV 2;
  786. right := lsp DIV 2;
  787. top := lsp DIV 2; bot := lsp DIV 2;
  788. asr := Fonts.Default.maxY;
  789. dsr := -Fonts.Default.minY;
  790. selH := lsp; markW := lsp DIV 2;
  791. eolW := lsp DIV 2;
  792. ScrollMarker.Fade := FlipSM; ScrollMarker.Draw := FlipSM;
  793. Texts.OpenWriter(W); Texts.OpenWriter(KW)
  794. END TextFrames.