Links.txt 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  1. MODULE StdLinks;
  2. (* THIS IS TEXT COPY OF BlackBox 1.6-rc6 Std/Mod/Links.odc *)
  3. IMPORT Kernel, Services,
  4. Stores, Ports, Fonts, Models, Views, Controllers, Properties, Dialog, Containers,
  5. TextModels, TextMappers, TextViews, TextControllers, TextSetters, TextRulers,
  6. Strings, StdCmds;
  7. CONST
  8. kind* = 0; cmd* = 1; close* = 2; (* constants for Prop.valid *)
  9. always* = 0; ifShiftDown* = 1; never* = 2; (* constants for close attrubute *)
  10. minLinkVersion = 0; maxLinkVersion = 1;
  11. minTargVersion = 0; maxTargVersion = 0;
  12. TYPE
  13. Directory* = POINTER TO ABSTRACT RECORD END;
  14. Link* = POINTER TO RECORD (Views.View)
  15. leftSide-: BOOLEAN;
  16. cmd: POINTER TO ARRAY OF CHAR;
  17. close: INTEGER
  18. END;
  19. Target* = POINTER TO RECORD (Views.View)
  20. leftSide-: BOOLEAN;
  21. ident: POINTER TO ARRAY OF CHAR
  22. END;
  23. Prop* = POINTER TO RECORD (Properties.Property)
  24. cmd*: POINTER TO ARRAY OF CHAR;
  25. link-: BOOLEAN;
  26. close*: INTEGER
  27. END;
  28. ChangeAttrOp = POINTER TO RECORD (Stores.Operation)
  29. v: Views.View;
  30. cmd: POINTER TO ARRAY OF CHAR;
  31. close: INTEGER;
  32. valid: SET
  33. END;
  34. StdDirectory = POINTER TO RECORD (Directory) END;
  35. TrapCleaner = POINTER TO RECORD (Kernel.TrapCleaner) END;
  36. VAR
  37. dir-, stdDir-: Directory;
  38. par-: Link;
  39. iconFont: Fonts.Typeface;
  40. linkLeft, linkRight, targetLeft, targetRight: ARRAY 8 OF SHORTCHAR;
  41. coloredBackg: BOOLEAN;
  42. cleaner: TrapCleaner;
  43. dialog*: RECORD
  44. cmd*: ARRAY 512 OF CHAR;
  45. type-: ARRAY 32 OF CHAR;
  46. close*: Dialog.List;
  47. known, valid: SET;
  48. END;
  49. fingerprint: INTEGER;
  50. (** Cleaner **)
  51. PROCEDURE (c: TrapCleaner) Cleanup;
  52. BEGIN
  53. par := NIL
  54. END Cleanup;
  55. (** Properties **)
  56. PROCEDURE (p: Prop) IntersectWith* (q: Properties.Property; OUT equal: BOOLEAN);
  57. VAR valid: SET;
  58. BEGIN
  59. WITH q: Prop DO
  60. valid := p.valid * q.valid; equal := TRUE;
  61. IF (cmd IN valid) & (p.cmd^ # q.cmd^) THEN EXCL(valid, cmd) END;
  62. IF (kind IN valid) & (p.link # q.link) THEN EXCL(valid, kind) END;
  63. IF (close IN valid) & (p.close # q.close) THEN EXCL (valid, close) END;
  64. IF p.valid # valid THEN p.valid := valid; equal := FALSE END
  65. END
  66. END IntersectWith;
  67. PROCEDURE (op: ChangeAttrOp) Do;
  68. VAR v: Views.View; s: POINTER TO ARRAY OF CHAR; c: INTEGER;
  69. BEGIN
  70. v := op.v;
  71. WITH
  72. | v: Link DO
  73. IF cmd IN op.valid THEN s := op.cmd; op.cmd := v.cmd; v.cmd := s END;
  74. IF close IN op.valid THEN c := op.close; op.close := v.close; v.close := c END
  75. | v: Target DO
  76. IF cmd IN op.valid THEN s := op.cmd; op.cmd := v.ident; v.ident := s END
  77. END
  78. END Do;
  79. PROCEDURE DoChangeAttrOp (v: Views.View; s: POINTER TO ARRAY OF CHAR; c: INTEGER; valid: SET);
  80. VAR op: ChangeAttrOp;
  81. BEGIN
  82. NEW(op); op.v := v; op.valid := valid;
  83. IF close IN valid THEN
  84. op.close := c END;
  85. IF cmd IN valid THEN NEW(op.cmd, LEN(s)+1); op.cmd^ := s$ END;
  86. Views.Do(v, "#Std:LinkChange", op)
  87. END DoChangeAttrOp;
  88. PROCEDURE SetProp(v: Views.View; msg: Properties.SetMsg);
  89. VAR p: Properties.Property;
  90. BEGIN
  91. p := msg.prop;
  92. WHILE p # NIL DO
  93. WITH p: Prop DO
  94. IF (cmd IN p.valid) OR (close IN p.valid) THEN DoChangeAttrOp(v, p.cmd, p.close, p.valid) END
  95. ELSE
  96. END;
  97. p := p.next
  98. END
  99. END SetProp;
  100. PROCEDURE PollProp(v: Views.View; VAR msg: Properties.PollMsg);
  101. VAR p: Prop;
  102. BEGIN
  103. NEW(p);
  104. WITH v: Link DO
  105. p.known := {kind, cmd, close};
  106. p.link := TRUE;
  107. p.cmd := v.cmd;
  108. p.close := v.close
  109. | v: Target DO
  110. p.known := {kind, cmd};
  111. p.link := FALSE;
  112. p.cmd := v.ident
  113. ELSE
  114. END;
  115. p.valid := p.known;
  116. Properties.Insert(msg.prop, p)
  117. END PollProp;
  118. PROCEDURE InitDialog*;
  119. VAR p: Properties.Property;
  120. BEGIN
  121. dialog.cmd := ""; dialog.type := ""; dialog.close.index := -1;
  122. dialog.known := {}; dialog.valid := {};
  123. Properties.CollectProp(p);
  124. WHILE p # NIL DO
  125. WITH p: Prop DO
  126. dialog.valid := p.valid; dialog.known := p.known;
  127. IF cmd IN p.valid THEN
  128. dialog.cmd := p.cmd$
  129. END;
  130. IF kind IN p.valid THEN
  131. IF p.link THEN Dialog.MapString("#Std:Link", dialog.type)
  132. ELSE Dialog.MapString("#Std:Target", dialog.type)
  133. END
  134. END;
  135. IF close IN p.valid THEN
  136. dialog.close.index := p.close
  137. END
  138. ELSE
  139. END;
  140. p := p.next
  141. END;
  142. Dialog.Update(dialog)
  143. END InitDialog;
  144. PROCEDURE Set*;
  145. VAR p: Prop;
  146. BEGIN
  147. NEW(p);
  148. p.valid := dialog.valid;
  149. IF cmd IN p.valid THEN
  150. NEW(p.cmd, LEN(dialog.cmd) + 1);
  151. p.cmd^ := dialog.cmd$
  152. END;
  153. p.close := dialog.close.index;
  154. Properties.EmitProp(NIL, p);
  155. fingerprint := 0 (* force actualization of fields *)
  156. END Set;
  157. PROCEDURE CmdGuard* (VAR par: Dialog.Par);
  158. VAR c: Containers.Controller; v: Views.View; fp: INTEGER;
  159. BEGIN
  160. IF ~(cmd IN dialog.known) THEN par.disabled := TRUE
  161. ELSIF ~(cmd IN dialog.valid) THEN par.undef := TRUE
  162. END;
  163. Controllers.SetCurrentPath(Controllers.targetPath);
  164. fp := 0;
  165. c := Containers.Focus();
  166. IF c # NIL THEN
  167. c.GetFirstView(Containers.selection, v);
  168. WHILE v # NIL DO fp := fp + Services.AdrOf(v); c.GetNextView(TRUE, v) END
  169. END;
  170. IF fp # fingerprint THEN fingerprint := fp; InitDialog END;
  171. Controllers.ResetCurrentPath()
  172. END CmdGuard;
  173. PROCEDURE CloseGuard* (VAR par: Dialog.Par);
  174. BEGIN
  175. IF ~(close IN dialog.known) THEN par.disabled := TRUE
  176. ELSIF ~(close IN dialog.valid) THEN par.undef := TRUE
  177. END;
  178. END CloseGuard;
  179. PROCEDURE Notifier* (idx, op, from, to: INTEGER);
  180. BEGIN
  181. IF op = Dialog.changed THEN INCL(dialog.valid, idx) END
  182. END Notifier;
  183. PROCEDURE (d: Directory) NewLink* (IN cmd: ARRAY OF CHAR): Link, NEW, ABSTRACT;
  184. PROCEDURE (d: Directory) NewTarget* (IN ident: ARRAY OF CHAR): Target, NEW, ABSTRACT;
  185. PROCEDURE InFrame (f: Views.Frame; x, y: INTEGER): BOOLEAN;
  186. BEGIN
  187. RETURN (f.l <= x) & (x < f.r) & (f.t <= y) & (y < f.b)
  188. END InFrame;
  189. PROCEDURE Mark (f: Views.Frame; show: BOOLEAN);
  190. BEGIN
  191. f.MarkRect(f.l, f.t, f.r, f.b, Ports.fill, Ports.hilite, show)
  192. END Mark;
  193. PROCEDURE ThisPos (v: TextViews.View; f: Views.Frame; x, y: INTEGER): INTEGER;
  194. (* "corrected" v.ThisPos: does not adjust position when crossing 50% boundary of characters *)
  195. VAR loc: TextViews.Location; pos: INTEGER;
  196. BEGIN
  197. pos := v.ThisPos(f, x, y); v.GetThisLocation(f, pos, loc);
  198. IF (loc.y <= y) & (y < loc.y + loc.asc + loc.dsc) & (x < loc.x) THEN DEC(pos) END;
  199. RETURN pos
  200. END ThisPos;
  201. PROCEDURE GetLinkPair (this: Link; VAR l, r: Link);
  202. (* POST: BalancedPair(l, r) & (l # r) & (l = this OR r = this) OR (l = r = NIL) *)
  203. VAR t: TextModels.Model; rd: TextModels.Reader; v: Views.View; level: INTEGER;
  204. BEGIN
  205. l := NIL; r := NIL; level := 1;
  206. IF (this.context # NIL) & (this.context IS TextModels.Context) THEN
  207. t := this.context(TextModels.Context).ThisModel();
  208. rd := t.NewReader(NIL);
  209. IF this.leftSide THEN
  210. rd.SetPos(this.context(TextModels.Context).Pos() + 1);
  211. REPEAT
  212. rd.ReadView(v);
  213. IF (v # NIL) & (v IS Link) THEN
  214. IF v(Link).leftSide THEN INC(level) ELSE DEC(level) END
  215. END
  216. UNTIL (v = NIL) OR (level = 0);
  217. IF v # NIL THEN l := this; r := v(Link) END
  218. ELSE
  219. rd.SetPos(this.context(TextModels.Context).Pos());
  220. REPEAT
  221. rd.ReadPrevView(v);
  222. IF (v # NIL) & (v IS Link) THEN
  223. IF v(Link).leftSide THEN DEC(level) ELSE INC(level) END
  224. END
  225. UNTIL (v = NIL) OR (level = 0);
  226. IF v # NIL THEN l := v(Link); r := this END
  227. END
  228. END
  229. END GetLinkPair;
  230. PROCEDURE GetTargetPair (this: Target; VAR l, r: Target);
  231. (* POST: BalancedPair(l, r) & (l # r) & (l = this OR r = this) OR (l = r = NIL) *)
  232. VAR t: TextModels.Model; rd: TextModels.Reader; v: Views.View; level: INTEGER;
  233. BEGIN
  234. l := NIL; r := NIL; level := 1;
  235. IF (this.context # NIL) & (this.context IS TextModels.Context) THEN
  236. t := this.context(TextModels.Context).ThisModel();
  237. rd := t.NewReader(NIL);
  238. IF this.leftSide THEN
  239. rd.SetPos(this.context(TextModels.Context).Pos() + 1);
  240. REPEAT
  241. rd.ReadView(v);
  242. IF (v # NIL) & (v IS Target) THEN
  243. IF v(Target).leftSide THEN INC(level) ELSE DEC(level) END
  244. END
  245. UNTIL (v = NIL) OR (level = 0);
  246. IF v # NIL THEN l := this; r := v(Target) END
  247. ELSE
  248. rd.SetPos(this.context(TextModels.Context).Pos());
  249. REPEAT
  250. rd.ReadPrevView(v);
  251. IF (v # NIL) & (v IS Target) THEN
  252. IF v(Target).leftSide THEN DEC(level) ELSE INC(level) END
  253. END
  254. UNTIL (v = NIL) OR (level = 0);
  255. IF v # NIL THEN l := v(Target); r := this END
  256. END
  257. END
  258. END GetTargetPair;
  259. PROCEDURE GetRange (l, r: Link; VAR beg, end: INTEGER);
  260. BEGIN
  261. beg := l.context(TextModels.Context).Pos();
  262. end := r.context(TextModels.Context).Pos() + 1
  263. END GetRange;
  264. PROCEDURE MarkRange (v: TextViews.View; f: Views.Frame; beg, end: INTEGER; show: BOOLEAN);
  265. VAR b, e: TextViews.Location; r, t: INTEGER;
  266. BEGIN
  267. ASSERT(beg < end, 20);
  268. v.GetThisLocation(f, beg, b); v.GetThisLocation(f, end, e);
  269. IF (b.pos < e.pos) OR (b.pos = e.pos) & (b.x < e.x) THEN
  270. IF b.start # e.start THEN
  271. r := f.r; t := b.y + b.asc + b.dsc;
  272. f.MarkRect(b.x, b.y, r, t, Ports.fill, Ports.hilite, show);
  273. IF t < e.y THEN f.MarkRect(0, t, r, e.y, Ports.fill, Ports.hilite, show) END;
  274. b.x := f.l; b.y := e.y
  275. END;
  276. f.MarkRect(b.x, b.y, e.x, e.y + e.asc + e.dsc, Ports.fill, Ports.hilite, show)
  277. END
  278. END MarkRange;
  279. PROCEDURE Reveal (left, right: Views.View; str: ARRAY OF CHAR; opname: Stores.OpName);
  280. VAR con: TextModels.Context; t: TextModels.Model; pos: INTEGER;
  281. w: TextMappers.Formatter; op: Stores.Operation;
  282. BEGIN
  283. con := left.context(TextModels.Context);
  284. t := con.ThisModel(); pos := con.Pos();
  285. w.ConnectTo(t); w.SetPos(pos);
  286. IF con.Attr() # NIL THEN w.rider.SetAttr(con.Attr()) END;
  287. Models.BeginScript(t, opname, op);
  288. t.Delete(pos, pos + 1);
  289. w.WriteChar("<");
  290. IF str # "" THEN w.WriteString(str) END;
  291. w.WriteChar(">");
  292. con := right.context(TextModels.Context);
  293. pos := con.Pos();
  294. w.SetPos(pos);
  295. IF con.Attr() # NIL THEN w.rider.SetAttr(con.Attr()) END;
  296. t.Delete(pos, pos + 1);
  297. w.WriteString("<>");
  298. Models.EndScript(t, op)
  299. END Reveal;
  300. PROCEDURE RevealCmd (v: Link);
  301. VAR left, right: Link;
  302. BEGIN GetLinkPair(v, left, right);
  303. IF left # NIL THEN
  304. IF v.cmd # NIL THEN Reveal(left, right, v.cmd^, "#StdLinks:Reveal Link Command")
  305. ELSE Reveal(left, right, "", "#StdLinks:Reveal Link Command")
  306. END
  307. END
  308. END RevealCmd;
  309. PROCEDURE RevealTarget (targ: Target);
  310. VAR left, right: Target;
  311. BEGIN GetTargetPair(targ, left, right);
  312. IF left # NIL THEN
  313. IF left.ident # NIL THEN Reveal(left, right, left.ident^, "#SdtLinks:Reveal Target Ident")
  314. ELSE Reveal(left, right, "", "#SdtLinks:Reveal Target Ident")
  315. END
  316. END
  317. END RevealTarget;
  318. PROCEDURE CallCmd (v: Link; close: BOOLEAN);
  319. VAR res: INTEGER;
  320. BEGIN
  321. Kernel.PushTrapCleaner(cleaner);
  322. par := v;
  323. IF v.cmd^ # "" THEN
  324. IF close & (v.close = ifShiftDown) OR (v.close = always) THEN
  325. StdCmds.CloseDialog
  326. END;
  327. Dialog.Call(v.cmd^, "#StdLinks:Link Call Failed", res)
  328. END;
  329. par := NIL;
  330. Kernel.PopTrapCleaner(cleaner)
  331. END CallCmd;
  332. PROCEDURE TrackSingle (f: Views.Frame; VAR in: BOOLEAN);
  333. VAR x, y: INTEGER; modifiers: SET; in0, isDown: BOOLEAN;
  334. BEGIN
  335. in := FALSE;
  336. REPEAT
  337. f.Input(x, y, modifiers, isDown);
  338. in0 := in; in := InFrame(f, x, y);
  339. IF in # in0 THEN Mark(f, in) END
  340. UNTIL ~isDown;
  341. IF in THEN Mark(f, FALSE) END
  342. END TrackSingle;
  343. PROCEDURE TrackRange (v: TextViews.View; f: Views.Frame; l, r: Link; x, y: INTEGER;
  344. VAR in: BOOLEAN);
  345. VAR pos, beg, end: INTEGER; modifiers: SET; in0, isDown: BOOLEAN;
  346. BEGIN
  347. in := FALSE;
  348. GetRange(l, r, beg, end); pos := ThisPos(v, f, x, y);
  349. IF (beg <= pos) & (pos < end) THEN
  350. REPEAT
  351. f.Input(x, y, modifiers, isDown); pos := ThisPos(v, f, x, y);
  352. in0 := in; in := (beg <= pos) & (pos < end);
  353. IF in # in0 THEN MarkRange(v, f, beg, end, in) END
  354. UNTIL ~isDown;
  355. IF in THEN
  356. MarkRange(v, f, beg, end, FALSE)
  357. END
  358. END
  359. END TrackRange;
  360. PROCEDURE Track (v: Link; f: Views.Frame; c: TextControllers.Controller;
  361. x, y: INTEGER; modifiers: SET);
  362. (* PRE: (c # NIL) & (f.view.ThisModel() = v.context.ThisModel()) OR (c = NIL) & (f.view = v) *)
  363. VAR l, r: Link; in: BOOLEAN;
  364. BEGIN
  365. GetLinkPair(v, l, r);
  366. IF l # NIL THEN
  367. IF c # NIL THEN TrackRange(c.view, f, l, r, x, y, in)
  368. ELSE TrackSingle(f, in)
  369. END;
  370. IF in THEN
  371. IF (Controllers.modify IN modifiers) & ((c = NIL) OR ~(Containers.noCaret IN c.opts)) THEN
  372. RevealCmd(l)
  373. ELSE
  374. CallCmd(l, Controllers.extend IN modifiers)
  375. END
  376. END
  377. END
  378. END Track;
  379. PROCEDURE TrackTarget (targ: Target; f: Views.Frame; modifiers: SET);
  380. VAR in: BOOLEAN;
  381. BEGIN
  382. TrackSingle(f, in);
  383. IF in & (Controllers.modify IN modifiers) THEN RevealTarget(targ) END
  384. END TrackTarget;
  385. PROCEDURE (v: Link) CopyFromSimpleView- (source: Views.View);
  386. BEGIN
  387. WITH source: Link DO
  388. ASSERT(source.leftSide = (source.cmd # NIL), 100);
  389. v.leftSide := source.leftSide;
  390. v.close := source.close;
  391. IF source.cmd # NIL THEN
  392. NEW(v.cmd, LEN(source.cmd^));
  393. v.cmd^ := source.cmd^$
  394. ELSE v.cmd := NIL
  395. END
  396. END
  397. END CopyFromSimpleView;
  398. PROCEDURE (t: Target) CopyFromSimpleView- (source: Views.View);
  399. BEGIN
  400. WITH source: Target DO
  401. ASSERT(source.leftSide = (source.ident # NIL), 100);
  402. t.leftSide := source.leftSide;
  403. IF source.ident # NIL THEN
  404. NEW(t.ident, LEN(source.ident^));
  405. t.ident^ := source.ident^$
  406. ELSE t.ident := NIL
  407. END
  408. END
  409. END CopyFromSimpleView;
  410. PROCEDURE (v: Link) Internalize- (VAR rd: Stores.Reader);
  411. VAR len: INTEGER; version: INTEGER; pos: INTEGER;
  412. BEGIN
  413. v.Internalize^(rd);
  414. IF rd.cancelled THEN RETURN END;
  415. rd.ReadVersion(minLinkVersion, maxLinkVersion, version);
  416. IF rd.cancelled THEN RETURN END;
  417. rd.ReadBool(v.leftSide);
  418. rd.ReadInt(len);
  419. IF len = 0 THEN v.cmd := NIL
  420. ELSE NEW(v.cmd, len); rd.ReadXString(v.cmd^)
  421. END;
  422. v.leftSide := v.cmd # NIL;
  423. IF v.leftSide THEN
  424. IF version = 1 THEN
  425. rd.ReadInt(v.close)
  426. ELSE
  427. Strings.Find(v.cmd, "StdLinks.ShowTarget", 0, pos);
  428. IF (pos # 0) THEN v.close := ifShiftDown
  429. ELSE v.close := never
  430. END
  431. END
  432. END
  433. END Internalize;
  434. PROCEDURE (v: Link) Externalize- (VAR wr: Stores.Writer);
  435. VAR pos, version: INTEGER;
  436. BEGIN
  437. v.Externalize^(wr);
  438. IF v.leftSide THEN
  439. Strings.Find(v.cmd, "StdLinks.ShowTarget", 0, pos);
  440. IF (pos = 0) & (v.close = never) OR (v.close = ifShiftDown) THEN version := 0
  441. ELSE version := 1
  442. END
  443. ELSE
  444. version := 0
  445. END;
  446. wr.WriteVersion(version);
  447. wr.WriteBool(v.cmd # NIL);
  448. IF v.cmd = NIL THEN wr.WriteInt(0)
  449. ELSE wr.WriteInt(LEN(v.cmd^)); wr.WriteXString(v.cmd^)
  450. END;
  451. IF version = 1 THEN wr.WriteInt(v.close) END
  452. END Externalize;
  453. PROCEDURE (t: Target) Internalize- (VAR rd: Stores.Reader);
  454. VAR len: INTEGER; version: INTEGER;
  455. BEGIN
  456. t.Internalize^(rd);
  457. IF rd.cancelled THEN RETURN END;
  458. rd.ReadVersion(minTargVersion, maxTargVersion, version);
  459. IF rd.cancelled THEN RETURN END;
  460. rd.ReadBool(t.leftSide);
  461. rd.ReadInt(len);
  462. IF len = 0 THEN t.ident := NIL
  463. ELSE NEW(t.ident, len); rd.ReadXString(t.ident^)
  464. END;
  465. t.leftSide := t.ident # NIL
  466. END Internalize;
  467. PROCEDURE (t: Target) Externalize- (VAR wr: Stores.Writer);
  468. BEGIN
  469. t.Externalize^(wr);
  470. wr.WriteVersion(maxTargVersion);
  471. wr.WriteBool(t.ident # NIL);
  472. IF t.ident = NIL THEN wr.WriteInt(0)
  473. ELSE wr.WriteInt(LEN(t.ident^)); wr.WriteXString(t.ident^)
  474. END
  475. END Externalize;
  476. PROCEDURE RestoreView (v: Views.View; f: Views.Frame; icon: ARRAY OF SHORTCHAR);
  477. VAR c: Models.Context; a: TextModels.Attributes; font: Fonts.Font; color: Ports.Color;
  478. asc, dsc, w: INTEGER;
  479. BEGIN
  480. c := v.context;
  481. IF (c # NIL) & (c IS TextModels.Context) THEN
  482. a := c(TextModels.Context).Attr();
  483. font := Fonts.dir.This(iconFont, a.font.size, {}, Fonts.normal);
  484. color := a.color
  485. ELSE font := Fonts.dir.Default(); color := Ports.black
  486. END;
  487. IF coloredBackg THEN
  488. f.DrawRect(f.l, f.t, f.r, f.b, Ports.fill, Ports.grey25) END;
  489. font.GetBounds(asc, dsc, w);
  490. f.DrawSString(1*Ports.mm DIV 2, asc, color, icon, font)
  491. END RestoreView;
  492. PROCEDURE (v: Link) Restore* (f: Views.Frame; l, t, r, b: INTEGER);
  493. BEGIN
  494. IF v.leftSide THEN RestoreView(v, f, linkLeft)
  495. ELSE RestoreView(v, f, linkRight)
  496. END
  497. END Restore;
  498. PROCEDURE (targ: Target) Restore* (f: Views.Frame; l, t, r, b: INTEGER);
  499. BEGIN
  500. IF targ.leftSide THEN RestoreView(targ, f, targetLeft)
  501. ELSE RestoreView(targ, f, targetRight)
  502. END
  503. END Restore;
  504. PROCEDURE SizePref (v: Views.View; icon: ARRAY OF SHORTCHAR; VAR msg: Properties.SizePref);
  505. VAR c: Models.Context; a: TextModels.Attributes; font: Fonts.Font;
  506. asc, dsc, w: INTEGER;
  507. BEGIN
  508. c := v.context;
  509. IF (c # NIL) & (c IS TextModels.Context) THEN
  510. a := c(TextModels.Context).Attr();
  511. font := Fonts.dir.This(iconFont, a.font.size, {}, Fonts.normal)
  512. ELSE
  513. font := Fonts.dir.Default()
  514. END;
  515. msg.w := font.SStringWidth(icon) + 1*Ports.mm;
  516. font.GetBounds(asc, dsc, w);
  517. msg.h := asc + dsc
  518. END SizePref;
  519. PROCEDURE (v: Link) HandlePropMsg- (VAR msg: Properties.Message);
  520. VAR a: TextModels.Attributes; c: Models.Context; asc, dsc, w: INTEGER; l, r: Link;
  521. BEGIN
  522. WITH msg: Properties.SizePref DO
  523. IF v.leftSide THEN SizePref(v, linkLeft, msg)
  524. ELSE SizePref(v, linkRight, msg)
  525. END
  526. | msg: Properties.FocusPref DO
  527. msg.hotFocus := TRUE
  528. | msg: Properties.ResizePref DO
  529. msg.fixed := TRUE
  530. | msg: TextModels.Pref DO
  531. msg.opts := {TextModels.hideable}
  532. | msg: TextControllers.FilterPref DO
  533. msg.filter := TRUE
  534. | msg: TextSetters.Pref DO c := v.context;
  535. IF (c # NIL) & (c IS TextModels.Context) THEN
  536. a := c(TextModels.Context).Attr();
  537. a.font.GetBounds(asc, dsc, w);
  538. msg.dsc := dsc
  539. END
  540. | msg: Properties.PollMsg DO
  541. IF v.leftSide THEN PollProp(v, msg)
  542. ELSE
  543. GetLinkPair(v, l, r);
  544. IF l # NIL THEN PollProp(l, msg) END
  545. END
  546. | msg: Properties.SetMsg DO
  547. IF v.leftSide THEN SetProp(v, msg)
  548. ELSE GetLinkPair(v, l, r); SetProp(l, msg)
  549. END
  550. ELSE
  551. END
  552. END HandlePropMsg;
  553. PROCEDURE (targ: Target) HandlePropMsg- (VAR msg: Properties.Message);
  554. VAR a: TextModels.Attributes; c: Models.Context; asc, dsc, w: INTEGER; l, r: Target;
  555. BEGIN
  556. WITH msg: Properties.SizePref DO
  557. IF targ.leftSide THEN SizePref(targ, targetLeft, msg)
  558. ELSE SizePref(targ, targetRight, msg)
  559. END
  560. | msg: Properties.FocusPref DO
  561. msg.hotFocus := TRUE
  562. | msg: Properties.ResizePref DO
  563. msg.fixed := TRUE
  564. | msg: TextModels.Pref DO
  565. msg.opts := {TextModels.hideable}
  566. | msg: TextSetters.Pref DO c := targ.context;
  567. IF (c # NIL) & (c IS TextModels.Context) THEN
  568. a := c(TextModels.Context).Attr();
  569. a.font.GetBounds(asc, dsc, w);
  570. msg.dsc := dsc
  571. END
  572. | msg: Properties.PollMsg DO
  573. IF targ.leftSide THEN PollProp(targ, msg)
  574. ELSE
  575. GetTargetPair(targ, l, r);
  576. IF l # NIL THEN PollProp(l, msg) END
  577. END
  578. | msg: Properties.SetMsg DO
  579. IF targ.leftSide THEN SetProp(targ, msg)
  580. ELSE GetTargetPair(targ, l, r); SetProp(l, msg)
  581. END
  582. ELSE
  583. END
  584. END HandlePropMsg;
  585. PROCEDURE (v: Link) HandleCtrlMsg* (f: Views.Frame;
  586. VAR msg: Controllers.Message; VAR focus: Views.View);
  587. PROCEDURE isHot(c: TextControllers.Controller; x, y: INTEGER; mod: SET): BOOLEAN;
  588. VAR pos, beg, end: INTEGER;
  589. BEGIN
  590. (* ignore alt, cmd, and middle clicks in edit mode *)
  591. IF ~(Containers.noCaret IN c.opts) & (mod * {17, 27, 28} # {}) THEN RETURN FALSE END;
  592. pos := ThisPos(c.view, f, x, y);
  593. (* ignore clicks in selection *)
  594. c.GetSelection(beg, end);
  595. IF (end > beg) & (pos >= beg) & (pos <= end) THEN RETURN FALSE END;
  596. IF v.leftSide THEN RETURN pos >= v.context(TextModels.Context).Pos()
  597. ELSE RETURN pos < v.context(TextModels.Context).Pos()
  598. END
  599. END isHot;
  600. BEGIN
  601. WITH msg: Controllers.PollCursorMsg DO
  602. msg.cursor := Ports.refCursor
  603. | msg: TextControllers.FilterPollCursorMsg DO
  604. IF isHot(msg.controller, msg.x, msg.y, {}) THEN
  605. msg.cursor := Ports.refCursor; msg.done := TRUE
  606. END
  607. | msg: Controllers.TrackMsg DO
  608. Track(v, f, NIL, msg.x, msg.y, msg.modifiers)
  609. | msg: TextControllers.FilterTrackMsg DO
  610. IF isHot(msg.controller, msg.x, msg.y, msg.modifiers) THEN
  611. Track(v, f, msg.controller, msg.x, msg.y, msg.modifiers);
  612. msg.done := TRUE
  613. END
  614. ELSE
  615. END
  616. END HandleCtrlMsg;
  617. PROCEDURE (targ: Target) HandleCtrlMsg* (f: Views.Frame; VAR msg: Controllers.Message;
  618. VAR focus: Views.View);
  619. BEGIN
  620. WITH msg: Controllers.TrackMsg DO TrackTarget(targ, f, msg.modifiers)
  621. ELSE
  622. END
  623. END HandleCtrlMsg;
  624. PROCEDURE (v: Link) GetCmd* (OUT cmd: ARRAY OF CHAR), NEW;
  625. BEGIN
  626. ASSERT(v.leftSide, 20);
  627. ASSERT(v.cmd # NIL, 100);
  628. cmd := v.cmd$
  629. END GetCmd;
  630. PROCEDURE (t: Target) GetIdent* (OUT ident: ARRAY OF CHAR), NEW;
  631. BEGIN
  632. ASSERT(t.leftSide, 20);
  633. ASSERT(t.ident # NIL, 100);
  634. ident := t.ident$
  635. END GetIdent;
  636. (* --------------- create commands and menu guards ------------------------ *)
  637. PROCEDURE GetParam (c: TextControllers.Controller; VAR param: ARRAY OF CHAR;
  638. VAR lbrBeg, lbrEnd, rbrBeg, rbrEnd: INTEGER);
  639. VAR rd: TextModels.Reader; i, beg, end: INTEGER;
  640. ch0, ch1, ch2: CHAR;
  641. BEGIN
  642. param[0] := 0X;
  643. IF (c # NIL) & c.HasSelection() THEN
  644. c.GetSelection(beg, end);
  645. IF end - beg > 4 THEN
  646. rd := c.text.NewReader(NIL);
  647. rd.SetPos(beg); rd.ReadChar(ch0);
  648. rd.SetPos(end-2); rd.ReadChar(ch1); rd.ReadChar(ch2);
  649. IF (ch0 = "<") & (ch1 = "<") & (ch2 = ">") THEN
  650. rd.SetPos(beg+1); rd.ReadChar(ch0); i := 0;
  651. WHILE ~rd.eot & (ch0 # ">") DO
  652. IF i < LEN(param) - 1 THEN param[i] := ch0; INC(i) END;
  653. rd.ReadChar(ch0)
  654. END;
  655. param[i] := 0X;
  656. lbrBeg := beg; lbrEnd := rd.Pos();
  657. rbrBeg := end -2; rbrEnd := end
  658. END
  659. END
  660. END
  661. END GetParam;
  662. PROCEDURE CreateGuard* (VAR par: Dialog.Par);
  663. VAR param: ARRAY 512 OF CHAR; lbrBeg, lbrEnd, rbrBeg, rbrEnd: INTEGER;
  664. BEGIN
  665. GetParam(TextControllers.Focus(), param, lbrBeg, lbrEnd, rbrBeg, rbrEnd);
  666. par.disabled := param = ""
  667. END CreateGuard;
  668. PROCEDURE InsertionAttr (c: TextControllers.Controller; pos: INTEGER): TextModels.Attributes;
  669. VAR rd: TextModels.Reader; r: TextRulers.Ruler; a: TextModels.Attributes; ch: CHAR;
  670. BEGIN
  671. rd := c.text.NewReader(NIL); a := NIL;
  672. rd.SetPos(pos); rd.ReadChar(ch); a := rd.attr;
  673. IF a = NIL THEN c.view.PollDefaults(r, a) END;
  674. RETURN a
  675. END InsertionAttr;
  676. PROCEDURE CreateLink*;
  677. VAR lbrBeg, lbrEnd, rbrBeg, rbrEnd: INTEGER;
  678. left, right: Link; c: TextControllers.Controller;
  679. cmd: ARRAY 512 OF CHAR;
  680. op: Stores.Operation;
  681. w: TextModels.Writer; a: TextModels.Attributes;
  682. BEGIN
  683. c := TextControllers.Focus();
  684. GetParam(TextControllers.Focus(), cmd, lbrBeg, lbrEnd, rbrBeg, rbrEnd);
  685. IF cmd # "" THEN
  686. w := c.text.NewWriter(NIL);
  687. Models.BeginScript(c.text, "#StdLinks:Create Link", op);
  688. a := InsertionAttr(c, rbrBeg);
  689. c.text.Delete(rbrBeg, rbrEnd);
  690. right := dir.NewLink("");
  691. w.SetPos(rbrBeg);
  692. IF a # NIL THEN w.SetAttr(a) END;
  693. w.WriteView(right, 0, 0);
  694. a := InsertionAttr(c, lbrBeg);
  695. c.text.Delete(lbrBeg, lbrEnd);
  696. left := dir.NewLink(cmd);
  697. w.SetPos(lbrBeg);
  698. IF a # NIL THEN w.SetAttr(a) END;
  699. w.WriteView(left, 0, 0);
  700. Models.EndScript(c.text, op)
  701. END
  702. END CreateLink;
  703. PROCEDURE CreateTarget*;
  704. VAR lbrBeg, lbrEnd, rbrBeg, rbrEnd: INTEGER;
  705. left, right: Target; c: TextControllers.Controller;
  706. ident: ARRAY 512 OF CHAR;
  707. op: Stores.Operation;
  708. w: TextModels.Writer; a: TextModels.Attributes;
  709. BEGIN
  710. c := TextControllers.Focus();
  711. GetParam(TextControllers.Focus(), ident, lbrBeg, lbrEnd, rbrBeg, rbrEnd);
  712. IF ident # "" THEN
  713. w := c.text.NewWriter(NIL);
  714. Models.BeginScript(c.text, "#StdLinks:Create Target", op);
  715. a := InsertionAttr(c, rbrBeg);
  716. c.text.Delete(rbrBeg, rbrEnd);
  717. right := dir.NewTarget("");
  718. w.SetPos(rbrBeg);
  719. IF a # NIL THEN w.SetAttr(a) END;
  720. w.WriteView(right, 0, 0);
  721. a := InsertionAttr(c, lbrBeg);
  722. c.text.Delete(lbrBeg, lbrEnd);
  723. left := dir.NewTarget(ident);
  724. w.SetPos(lbrBeg);
  725. IF a # NIL THEN w.SetAttr(a) END;
  726. w.WriteView(left, 0, 0);
  727. Models.EndScript(c.text, op)
  728. END
  729. END CreateTarget;
  730. PROCEDURE ShowTarget* (IN ident: ARRAY OF CHAR);
  731. VAR c: TextControllers.Controller; rd: TextModels.Reader;
  732. v: Views.View; left, right: Target; beg, end: INTEGER;
  733. BEGIN
  734. c := TextControllers.Focus();
  735. IF c # NIL THEN
  736. rd := c.text.NewReader(NIL);
  737. REPEAT rd.ReadView(v)
  738. UNTIL rd.eot OR (v # NIL) & (v IS Target) & v(Target).leftSide & (v(Target).ident^ = ident);
  739. IF ~rd.eot THEN
  740. GetTargetPair(v(Target), left, right);
  741. IF (left # NIL) & (right # NIL) THEN
  742. beg := left.context(TextModels.Context).Pos();
  743. end := right.context(TextModels.Context).Pos() + 1;
  744. c.SetSelection(beg, end);
  745. c.view.SetOrigin(beg, 0)
  746. ELSE
  747. Dialog.ShowParamMsg("target '^0' not found", ident, "", "")
  748. END
  749. ELSE
  750. Dialog.ShowParamMsg("target '^0' not found", ident, "", "")
  751. END
  752. END
  753. END ShowTarget;
  754. (* programming interface *)
  755. PROCEDURE (d: StdDirectory) NewLink (IN cmd: ARRAY OF CHAR): Link;
  756. VAR link: Link; i: INTEGER;
  757. BEGIN
  758. NEW(link); link.leftSide := cmd # "";
  759. IF link.leftSide THEN
  760. i := 0; WHILE cmd[i] # 0X DO INC(i) END;
  761. NEW(link.cmd, i + 1); link.cmd^ := cmd$
  762. ELSE
  763. link.cmd := NIL
  764. END;
  765. link.close := ifShiftDown;
  766. RETURN link
  767. END NewLink;
  768. PROCEDURE (d: StdDirectory) NewTarget (IN ident: ARRAY OF CHAR): Target;
  769. VAR t: Target; i: INTEGER;
  770. BEGIN
  771. NEW(t); t.leftSide := ident # "";
  772. IF t.leftSide THEN
  773. i := 0; WHILE ident[i] # 0X DO INC(i) END;
  774. NEW(t.ident, i + 1); t.ident^ := ident$
  775. ELSE
  776. t.ident := NIL
  777. END;
  778. RETURN t
  779. END NewTarget;
  780. PROCEDURE SetDir* (d: Directory);
  781. BEGIN
  782. ASSERT(d # NIL, 20);
  783. dir := d
  784. END SetDir;
  785. PROCEDURE Init;
  786. VAR font: Fonts.Font; d: StdDirectory;
  787. PROCEDURE DefaultAppearance;
  788. BEGIN font := Fonts.dir.Default(); iconFont := font.typeface;
  789. linkLeft := "Link"; linkRight := "~";
  790. targetLeft := "Targ"; targetRight := "~";
  791. coloredBackg := TRUE
  792. END DefaultAppearance;
  793. BEGIN
  794. NEW(d); dir := d; stdDir := d;
  795. IF Dialog.platform DIV 10 = 1 THEN (* Windows *)
  796. iconFont := "Wingdings";
  797. font := Fonts.dir.This(iconFont, 10*Fonts.point (*arbitrary*), {}, Fonts.normal);
  798. IF font.IsAlien() THEN DefaultAppearance
  799. ELSE
  800. linkLeft[0] := SHORT(CHR(246)); linkLeft[1] := 0X;
  801. linkRight[0] := SHORT(CHR(245)); linkRight[1] := 0X;
  802. targetLeft[0] := SHORT(CHR(164)); targetLeft[1] := 0X;
  803. targetRight[0] := SHORT(CHR(161)); targetRight[1] := 0X;
  804. coloredBackg := FALSE
  805. END
  806. ELSIF Dialog.platform DIV 10 = 2 THEN (* Mac *)
  807. DefaultAppearance
  808. ELSE
  809. DefaultAppearance
  810. END;
  811. NEW(cleaner);
  812. dialog.close.SetResources("#Std:links")
  813. END Init;
  814. BEGIN
  815. Init
  816. END StdLinks.