Oberon.ColorTools.Mod 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. (* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
  2. Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
  3. MODULE ColorTools IN Oberon; (** portable *) (** jm 19.1.95 *)
  4. (** ColorPicker implementation *)
  5. (*
  6. jm 1.3.93 - fixed selection
  7. - increased size of command string
  8. 25.7.94 - added ChangeColor command
  9. *)
  10. IMPORT
  11. Objects, Gadgets, Effects, Display, Display3, Oberon, Files, Input, Texts, Views, Printer, Printer3;
  12. CONST
  13. VersionNo = 2; ModName = "ColorTools";
  14. TYPE
  15. ColorPicker* = POINTER TO ColorPickerDesc;
  16. ColorPickerDesc* = RECORD (Gadgets.FrameDesc)
  17. cmd*: ARRAY 64 OF CHAR; (** Cmd attribute *)
  18. colors*: ARRAY 256 OF INTEGER; (** colors to be displayed *)
  19. col*: INTEGER; (** last color to be picked *)
  20. END;
  21. VAR
  22. W: Texts.Writer;
  23. (* --- Version check --- *)
  24. PROCEDURE WriteVersion(VAR R: Files.Rider);
  25. BEGIN
  26. Files.WriteNum(R, VersionNo);
  27. END WriteVersion;
  28. (* ----------- Color Picker ---------- *)
  29. PROCEDURE Parse(VAR s: ARRAY OF CHAR; VAR n: INTEGER; VAR values: ARRAY OF INTEGER);
  30. VAR i: INTEGER; val: LONGINT; neg: BOOLEAN;
  31. BEGIN
  32. n := 0;
  33. i := 0;
  34. LOOP
  35. WHILE (s[i] # 0X) & (s[i] <= " ") DO INC(i) END;
  36. IF s[i] = 0X THEN EXIT END;
  37. IF s[i] = "-" THEN INC(i); neg := TRUE ELSE neg := FALSE END;
  38. IF (s[i] >= "0") & (s[i] <= "9") THEN
  39. val := 0;
  40. WHILE (s[i] >= "0") & (s[i] <= "9") DO val := val * 10 + ORD(s[i]) - ORD("0"); INC(i) END;
  41. IF neg THEN val := -val END;
  42. IF val < -127 THEN val := -127
  43. ELSIF val > 255 THEN val := 255
  44. END;
  45. values[n] := ABS(SHORT(val)); INC(n);
  46. ELSE EXIT
  47. END
  48. END
  49. END Parse;
  50. PROCEDURE Unparse(n: INTEGER; VAR values: ARRAY OF INTEGER; VAR s: ARRAY OF CHAR);
  51. VAR i, j: INTEGER;
  52. PROCEDURE Out(i: LONGINT);
  53. VAR k: INTEGER; x0: LONGINT; a: ARRAY 10 OF CHAR;
  54. BEGIN k := 0;
  55. IF i < 0 THEN x0 := -i
  56. ELSE x0 := i
  57. END;
  58. REPEAT
  59. a[k] := CHR(x0 MOD 10 + 30H); x0 := x0 DIV 10; INC(k)
  60. UNTIL x0 = 0;
  61. IF i < 0 THEN s[j] := "-"; INC(j); END;
  62. REPEAT DEC(k); s[j] := a[k]; INC(j); UNTIL k = 0;
  63. s[j] := " "; INC(j);
  64. END Out;
  65. BEGIN
  66. j := 0;
  67. i := 0;
  68. WHILE i < n DO Out(values[i]); INC(i) END; s[j] := 0X;
  69. END Unparse;
  70. PROCEDURE ColorPickerAttr(F: ColorPicker; VAR M: Objects.AttrMsg);
  71. VAR n: INTEGER;
  72. BEGIN
  73. IF M.id = Objects.get THEN
  74. IF M.name = "Gen" THEN M.class := Objects.String; COPY("ColorTools.NewColorPicker", M.s); M.res := 0
  75. ELSIF M.name = "Cmd" THEN M.class := Objects.String; COPY(F.cmd, M.s); M.res := 0
  76. ELSIF M.name = "Col" THEN M.class := Objects.Int; M.i := F.col; M.res := 0
  77. ELSIF M.name = "Colors" THEN M.class := Objects.String; Unparse(16, F.colors, M.s); M.res := 0
  78. ELSE Gadgets.framehandle(F, M);
  79. END;
  80. ELSIF M.id = Objects.set THEN
  81. IF M.name = "Cmd" THEN
  82. IF (M.class = Objects.String) THEN COPY(M.s, F.cmd); M.res := 0 END;
  83. ELSIF M.name = "Col" THEN
  84. IF (M.class = Objects.Int) THEN F.col := SHORT(M.i); M.res := 0; END
  85. ELSIF M.name = "Colors" THEN
  86. IF (M.class = Objects.String) THEN
  87. Parse(M.s, n, F.colors);
  88. WHILE n < 256 DO F.colors[n] := n; INC(n) END;
  89. M.res := 0
  90. END
  91. ELSE Gadgets.framehandle(F, M);
  92. END;
  93. ELSIF M.id = Objects.enum THEN
  94. M.Enum("Colors"); M.Enum("Col"); M.Enum("Cmd");
  95. Gadgets.framehandle(F, M);
  96. END;
  97. END ColorPickerAttr;
  98. PROCEDURE Grid(F: ColorPicker; R: Display3.Mask; b, x, y: INTEGER);
  99. VAR i, j, c, max: INTEGER;
  100. BEGIN
  101. IF Display.Depth(0) >= 8 THEN max := 16 ELSE max := 4 END;
  102. c := 0; j := max - 1;
  103. WHILE j >= 0 DO
  104. i := 0;
  105. WHILE i < max DO Display3.ReplConst(R, F.colors[c] , x + i * b, y + j * b, b, b, Display.replace); INC(i); INC(c) END;
  106. DEC(j);
  107. END;
  108. END Grid;
  109. PROCEDURE ClipAgainst(VAR x, y, w, h: INTEGER; x1, y1, w1, h1: INTEGER);
  110. VAR r, t, r1, t1: INTEGER;
  111. BEGIN
  112. r := x + w - 1; r1 := x1 + w1 - 1; t := y + h - 1; t1 := y1 + h1 - 1;
  113. IF x < x1 THEN x := x1 END;
  114. IF y < y1 THEN y := y1 END;
  115. IF r > r1 THEN r := r1 END;
  116. IF t > t1 THEN t := t1 END;
  117. w := r - x + 1; h := t - y + 1;
  118. END ClipAgainst;
  119. PROCEDURE PickColor(F: ColorPicker; x, y, w, h: INTEGER; VAR M: Oberon.InputMsg);
  120. VAR bw, bw4: INTEGER;
  121. VAR keys, keysum: SET; X, Y, i, j, li, lj, ofs, px, py, b, max: INTEGER; block: Views.Block;
  122. PROCEDURE Cell(X, Y: INTEGER; VAR i, j: INTEGER);
  123. BEGIN
  124. IF Effects.Inside(X, Y, x+2, y + 2 + ofs, bw, bw) THEN
  125. i := (X - x - 2) DIV b; j := (Y - (y + 2 + ofs)) DIV b;
  126. ELSE
  127. i := -1; j := -1;
  128. END;
  129. END Cell;
  130. PROCEDURE Highlight(colno, i, j: INTEGER);
  131. BEGIN
  132. IF i >= 0 THEN
  133. Oberon.FadeCursor(Oberon.Mouse);
  134. Display3.Rect(block.mask, F.colors[colno], Display.solid, x + 2 + i * b, y + 2 + ofs + j * b, b, b, 1, Display.replace);
  135. END;
  136. END Highlight;
  137. PROCEDURE CalcPlace(VAR px, py: INTEGER);
  138. VAR cx, cy, cw, ch: INTEGER; (* clipping area *) f: Objects.Object;
  139. BEGIN
  140. cx := 0; cy := 0; cw := Display.Width; ch := Display.Height;
  141. f := Gadgets.context;
  142. WHILE f # NIL DO
  143. IF f IS Gadgets.View THEN
  144. WITH f: Gadgets.View DO
  145. ClipAgainst(cx, cy, cw, ch, f.absX, f.absY, f.W, f.H);
  146. END
  147. END;
  148. f := f.dlink
  149. END;
  150. px := x; py := y + h - (bw4-1);
  151. IF px < cx THEN px := cx; END;
  152. IF px + bw4 >= cx + cw THEN px := cx + cw - 1 - bw4; END;
  153. IF py < cy THEN py := cy; END;
  154. IF py + bw4 >= cy + ch THEN py := cy + ch - 1 - bw4 END;
  155. END CalcPlace;
  156. BEGIN
  157. IF Display.Depth(0) >= 8 THEN bw := 112; max := 16 ELSE bw := 80; max := 4 END;
  158. bw4 := bw + 4; b := bw DIV max;
  159. CalcPlace(px, py);
  160. ofs := 0;
  161. x := px; y := py;
  162. Oberon.RemoveMarks(x, y+ofs, bw4, bw4);
  163. Views.GetBlock(x, y+ofs, bw4, bw4, M.dlink, block);
  164. Display3.ReplConst(block.mask, Display3.black, x, y+ofs, bw4, bw4, Display.replace);
  165. Grid(F, block.mask, b, x+2, y+2+ofs);
  166. Input.Mouse(keys, X, Y); keysum := keys;
  167. Cell(X, Y, li, lj); Highlight(15, li, lj);
  168. WHILE keys # {} DO
  169. Input.Mouse(keys, X, Y);
  170. Cell(X, Y, i, j);
  171. IF (i # li) OR (j # lj) THEN
  172. Highlight(li + (max - 1 - lj) * max, li, lj); li := i; lj := j; Highlight(15, li, lj);
  173. END;
  174. keysum := keysum + keys;
  175. Oberon.DrawCursor(Oberon.Mouse, Effects.Arrow, X, Y);
  176. END;
  177. Highlight(li + (max - 1 - lj) * max, li, lj);
  178. Oberon.RemoveMarks(x, y+ofs, bw4, bw4);
  179. Views.RestoreBlock(block);
  180. IF ((keysum = {1}) OR (Oberon.New & (M.keys = {2}))) & (i >= 0) THEN
  181. F.col := F.colors[li + (max - 1 - lj) * max];
  182. IF F.cmd[0] # 0X THEN
  183. Gadgets.Execute(F.cmd, F, M.dlink, NIL, NIL);
  184. END;
  185. END;
  186. END PickColor;
  187. PROCEDURE RestoreColorPicker(R: Display3.Mask; F: ColorPicker; x, y, w, h: INTEGER);
  188. VAR b, max: INTEGER;
  189. BEGIN
  190. IF Display.Depth(0) >= 8 THEN max := 16 ELSE max := 4 END;
  191. b := (w - 4) DIV max;
  192. Display3.ReplConst(R, Display3.black, x, y, w, h, Display.replace);
  193. Grid(F, R, b, x+2, y+2);
  194. IF Gadgets.selected IN F.state THEN Display3.FillPattern(R, Display3.white, Display3.selectpat, 0, 0, x, y, w, h, Display.paint); END;
  195. END RestoreColorPicker;
  196. PROCEDURE PrintColorPicker(F: ColorPicker; VAR M: Display.DisplayMsg);
  197. VAR R: Display3.Mask; x, y, w, h, b, i, j, c, max: INTEGER;
  198. PROCEDURE P(x: INTEGER): INTEGER;
  199. BEGIN RETURN SHORT(x * Display.Unit DIV Printer.Unit)
  200. END P;
  201. BEGIN
  202. Gadgets.MakePrinterMask(F, M.x, M.y, M.dlink, R);
  203. x := M.x; y := M.y; w := P(F.W); h := P(F.H);
  204. Printer3.ReplConst(R, Display3.black, x, y, w, h, Display.replace);
  205. IF Display.Depth(0) >= 8 THEN max := 16 ELSE max := 4 END;
  206. b := (F.W - 4) DIV max;
  207. x := M.x + P(2); y := M.y + P(2);
  208. c := 0; j := max - 1;
  209. WHILE j >= 0 DO
  210. i := 0;
  211. WHILE i < max DO
  212. Printer3.ReplConst(R, F.colors[c] , x + P(i * b), y + P(j * b), P(b), P(b), Display.replace);
  213. INC(i); INC(c)
  214. END;
  215. DEC(j);
  216. END;
  217. END PrintColorPicker;
  218. PROCEDURE CopyColorPicker*(VAR M: Objects.CopyMsg; from, to: ColorPicker);
  219. BEGIN
  220. Gadgets.CopyFrame(M, from, to); to.col := from.col; COPY(from.cmd, to.cmd); to.colors := from.colors;
  221. END CopyColorPicker;
  222. PROCEDURE ColorPickerHandler*(F: Objects.Object; VAR M: Objects.ObjMsg);
  223. VAR x, y, w, h, u, v: INTEGER; F0: ColorPicker; R: Display3.Mask; ver: LONGINT;
  224. BEGIN
  225. WITH F: ColorPicker DO
  226. IF M IS Objects.AttrMsg THEN
  227. WITH M: Objects.AttrMsg DO ColorPickerAttr(F, M) END;
  228. ELSIF M IS Objects.FileMsg THEN
  229. WITH M: Objects.FileMsg DO
  230. IF M.id = Objects.store THEN
  231. WriteVersion(M.R);
  232. Files.WriteString(M.R, F.cmd);
  233. x := 0; WHILE x < 256 DO Files.WriteInt(M.R, F.colors[x]); INC(x) END;
  234. Gadgets.framehandle(F, M)
  235. ELSIF M.id = Objects.load THEN
  236. Files.ReadNum(M.R, ver);
  237. IF ver = 1 THEN
  238. Files.ReadString(M.R, F.cmd);
  239. Gadgets.framehandle(F, M)
  240. ELSIF ver = VersionNo THEN
  241. Files.ReadString(M.R, F.cmd);
  242. x := 0; WHILE x < 256 DO
  243. Files.ReadInt(M.R, F.colors[x]);
  244. IF F.colors[x] < 0 THEN F.colors[x] := x END;
  245. INC(x)
  246. END;
  247. Gadgets.framehandle(F, M)
  248. ELSE
  249. Texts.WriteString(W, "Version "); Texts.WriteInt(W, VersionNo, 3); Texts.WriteString(W, " of ");
  250. Texts.WriteString(W, ModName);
  251. Texts.WriteString(W, " cannot read version "); Texts.WriteInt(W, ver, 3); Texts.WriteLn(W);
  252. Texts.Append(Oberon.Log, W.buf);
  253. HALT(99);
  254. END
  255. END
  256. END
  257. ELSIF M IS Objects.CopyMsg THEN
  258. WITH M: Objects.CopyMsg DO
  259. IF M.stamp = F.stamp THEN M.obj := F.dlink
  260. ELSE NEW(F0); F.stamp := M.stamp; F.dlink := F0; CopyColorPicker(M, F, F0); M.obj := F0
  261. END
  262. END
  263. ELSIF M IS Display.FrameMsg THEN
  264. WITH M: Display.FrameMsg DO
  265. x := M.x + F.X; y := M.y + F.Y; w := F.W; h := F.H; (* calculate actual coordinates *)
  266. u := M.x; v := M.y; (* store volatile info *)
  267. IF M IS Display.DisplayMsg THEN
  268. WITH M: Display.DisplayMsg DO
  269. IF M.device = Display.screen THEN
  270. IF (M.F = NIL) OR ((M.id = Display.full) & (M.F = F)) THEN
  271. Gadgets.MakeMask(F, x, y, M.dlink, R);
  272. RestoreColorPicker(R, F, x, y, w, h);
  273. ELSIF (M.id = Display.area) & (M.F = F) THEN
  274. Gadgets.MakeMask(F, x, y, M.dlink, R);
  275. Display3.AdjustMask(R, x + M.u, y + h - 1 + M.v, M.w, M.h);
  276. RestoreColorPicker(R, F, x, y, w, h);
  277. END
  278. ELSIF M.device = Display.printer THEN PrintColorPicker(F, M)
  279. END
  280. END
  281. ELSIF M IS Oberon.InputMsg THEN
  282. WITH M: Oberon.InputMsg DO
  283. IF ~(Gadgets.selected IN F.state) THEN
  284. IF (M.id = Oberon.track) & ((M.keys = {1}) OR (Oberon.New & (M.keys = {2}))) & Gadgets.InActiveArea(F, M) THEN
  285. PickColor(F, x, y, w, h, M); M.res := 0;
  286. ELSE
  287. Gadgets.framehandle(F, M);
  288. END;
  289. END;
  290. END;
  291. ELSE
  292. Gadgets.framehandle(F, M);
  293. END;
  294. M.x := u; M.y := v; (* restore volatile info *)
  295. END;
  296. ELSE
  297. Gadgets.framehandle(F, M);
  298. END;
  299. END;
  300. END ColorPickerHandler;
  301. PROCEDURE InitColorPicker*(F: ColorPicker);
  302. VAR i: INTEGER;
  303. BEGIN F.W := 32+4; F.H := 32+4; F.col := 15; F.state := {Gadgets.lockedsize};
  304. F.handle := ColorPickerHandler;
  305. F.cmd := "ColorTools.ChangeColor #Col ~";
  306. i := 0; WHILE i < 256 DO F.colors[i] := i; INC(i) END;
  307. END InitColorPicker;
  308. PROCEDURE NewColorPicker*;
  309. VAR F: ColorPicker;
  310. BEGIN
  311. NEW(F); InitColorPicker(F); Objects.NewObj := F;
  312. END NewColorPicker;
  313. (** Used in the form:
  314. ColorTools.ChangeColor <colno>
  315. Change the color of the selected text or the selected gadgets.
  316. *)
  317. PROCEDURE ChangeColor*;
  318. VAR col: INTEGER; S: Texts.Scanner;
  319. MOS: Display.SelectMsg; MTS: Oberon.SelectMsg; MA: Objects.AttrMsg; MU: Gadgets.UpdateMsg;
  320. obj: Objects.Object;
  321. BEGIN
  322. Texts.OpenScanner(S, Oberon.Par.text, Oberon.Par.pos);
  323. Texts.Scan(S); col := SHORT(S.i);
  324. MOS.id := Display.get; MOS.time := -1; MOS.F := NIL; MOS.obj := NIL;
  325. MTS.id := Oberon.get; MTS.time := -1; MTS.F := NIL; MTS.sel := NIL; MTS.text := NIL;
  326. Display.Broadcast(MOS); Display.Broadcast(MTS);
  327. IF (MTS.time # -1) & (((MOS.time-MTS.time) < 0) OR (MOS.time = -1)) THEN
  328. Texts.ChangeLooks(MTS.text, MTS.beg, MTS.end, {1}, NIL, SHORT(col), 0)
  329. ELSIF (MOS.time # -1) & (((MTS.time-MOS.time) < 0) OR (MTS.time = -1)) & (MOS.obj # NIL) THEN
  330. obj := MOS.obj;
  331. WHILE obj # NIL DO
  332. MA.id := Objects.set; MA.name := "Color"; MA.class := Objects.Int; MA.i := col; MA.res := -1;
  333. obj.handle(obj, MA);
  334. obj := obj.slink
  335. END;
  336. MU.obj := MOS.obj; MU.F := NIL; Display.Broadcast(MU);
  337. END
  338. END ChangeColor;
  339. BEGIN
  340. Texts.OpenWriter(W);
  341. END ColorTools.