2
0

CharacterLineup.Mod 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. MODULE CharacterLineup; (** AUTHOR "TF"; PURPOSE "Tool to identify a chinese character"; *)
  2. IMPORT
  3. KernelLog, Modules, WMComponents, WMStandardComponents, WMEditors,
  4. Strings, UTF8Strings, WMGraphics, UnihanParser, WM := WMWindowManager, WMGrids,
  5. WMCCGFonts, WMRectangles;
  6. CONST
  7. MaxCharacterCode = 200000;
  8. NofCols = 25;
  9. MaxFilters = 8;
  10. TYPE
  11. CharacterArray = POINTER TO ARRAY OF WMCCGFonts.Glyph;
  12. HistoEntry = RECORD ucs, freq : LONGINT; END;
  13. FilterHisto = POINTER TO ARRAY OF HistoEntry;
  14. Identifier = OBJECT(WMComponents.FormWindow)
  15. VAR
  16. mainPanel, toolbar, infobar, textInfoPanel, selectionPanel, filterPanel, paintBox : WMStandardComponents.Panel;
  17. characterEdit, pinyinEdit, mandarinEdit, cantoneseEdit, koreanEdit, definitionEdit, codeEdit : WMEditors.Editor;
  18. characters, filterComponents : WMGrids.GenericGrid;
  19. toggleFilter : WMStandardComponents.Button;
  20. curChar :LONGINT;
  21. fontinfo : WMCCGFonts.GenericFont;
  22. bigFont : WMGraphics.Font;
  23. charInfo : UnihanParser.Character;
  24. allCharacters: CharacterArray;
  25. nofCharacters: LONGINT;
  26. filtered : CharacterArray;
  27. filterArray : ARRAY MaxFilters OF LONGINT;
  28. nofFilters : LONGINT;
  29. nofInFilter : LONGINT;
  30. useFilter : BOOLEAN;
  31. relevantSubcomponents : FilterHisto;
  32. PROCEDURE &New*;
  33. PROCEDURE AddLabelEdit(parent : WMComponents.VisualComponent; VAR e : WMEditors.Editor; CONST caption : ARRAY OF CHAR);
  34. VAR l : WMStandardComponents.Label;
  35. g : WMStandardComponents.Panel;
  36. BEGIN
  37. NEW(g); g.bounds.SetHeight(30); g.alignment.Set(WMComponents.AlignTop);
  38. NEW(l); l.bounds.SetWidth(100); l.alignment.Set(WMComponents.AlignLeft); l.caption.SetAOC(caption); g.AddContent(l);
  39. NEW(e); e.alignment.Set(WMComponents.AlignClient); g.AddContent(e);
  40. e.multiLine.Set(FALSE);
  41. e.tv.textAlignV.Set(WMGraphics.AlignCenter);
  42. parent.AddContent(g)
  43. END AddLabelEdit;
  44. BEGIN
  45. SetTitle(WM.NewString("Hobbes' Chinese Tool"));
  46. NEW(mainPanel); mainPanel.bounds.SetExtents(800, 600);
  47. mainPanel.fillColor.Set(0FFFFFFFFH);
  48. NEW(toolbar); toolbar.bounds.SetHeight(30); toolbar.alignment.Set(WMComponents.AlignTop); mainPanel.AddContent(toolbar);
  49. NEW(infobar); infobar.bounds.SetHeight(256); infobar.alignment.Set(WMComponents.AlignTop); mainPanel.AddContent(infobar);
  50. (* information elements *)
  51. NEW(paintBox); paintBox.bounds.SetWidth(256); paintBox.alignment.Set(WMComponents.AlignLeft); infobar.AddContent(paintBox);
  52. paintBox.fillColor.Set(0FFFFFFFFH); paintBox.SetExtDrawHandler(PaintCharacter);
  53. bigFont := WMGraphics.GetFont("Single", 256, {});
  54. IF bigFont IS WMCCGFonts.Font THEN fontinfo := bigFont(WMCCGFonts.Font).gf END;
  55. bigFont := WMGraphics.GetFont("Cyberbit", 256, {});
  56. NEW(textInfoPanel); textInfoPanel.alignment.Set(WMComponents.AlignClient); infobar.AddContent(textInfoPanel);
  57. AddLabelEdit(textInfoPanel, characterEdit, "Character : ");
  58. characterEdit.SetFont(WMGraphics.GetFont("Single", 20, {}));
  59. characterEdit.onEnter.Add(NewCharacter);
  60. AddLabelEdit(textInfoPanel, pinyinEdit, "Pinyin : ");
  61. AddLabelEdit(textInfoPanel, mandarinEdit, "Mandarin : ");
  62. AddLabelEdit(textInfoPanel, cantoneseEdit, "Cantonese : ");
  63. AddLabelEdit(textInfoPanel, koreanEdit, "Korean : ");
  64. AddLabelEdit(textInfoPanel, definitionEdit, "Definition : ");
  65. definitionEdit.multiLine.Set(FALSE);
  66. definitionEdit.tv.textAlignV.Set(WMGraphics.AlignCenter);
  67. AddLabelEdit(textInfoPanel, codeEdit, "Code : ");
  68. codeEdit.onEnter.Add(NewCode);
  69. (* filter tool bar *)
  70. NEW(filterPanel); filterPanel.alignment.Set(WMComponents.AlignTop);
  71. filterPanel.bounds.SetHeight(80); mainPanel.AddContent(filterPanel);
  72. NEW(filterComponents); filterComponents.alignment.Set(WMComponents.AlignTop); filterPanel.AddContent(filterComponents);
  73. filterComponents.bounds.SetHeight(50);
  74. filterComponents.nofRows.Set(1);
  75. filterComponents.SetDrawCellProc(DrawFilterComponents);
  76. filterComponents.defaultColWidth.Set(30); filterComponents.defaultRowHeight.Set(30);
  77. filterComponents.onClick.Add(FilterSelection);
  78. NEW(toggleFilter); toggleFilter.onClick.Add(ToggleFilter); toggleFilter.SetCaption("Clear Filter");
  79. toggleFilter.alignment.Set(WMComponents.AlignTop); filterPanel.AddContent(toggleFilter);
  80. (* characters *)
  81. NEW(selectionPanel); selectionPanel.alignment.Set(WMComponents.AlignClient); mainPanel.AddContent(selectionPanel);
  82. NEW(characters); characters.alignment.Set(WMComponents.AlignClient); selectionPanel.AddContent(characters);
  83. characters.nofCols.Set(NofCols); characters.nofRows.Set(4);
  84. characters.defaultColWidth.Set(30); characters.defaultRowHeight.Set(30);
  85. characters.SetDrawCellProc(DrawAll);
  86. characters.onSelect.Add(SelectChar);
  87. Init(mainPanel.bounds.GetWidth(), mainPanel.bounds.GetHeight(), FALSE);
  88. manager := WM.GetDefaultManager();
  89. manager.Add(200, 200, SELF, {WM.FlagFrame, WM.FlagClose, WM.FlagMinimize});
  90. SetContent(mainPanel);
  91. LoadAllCharacters;
  92. filterComponents.nofCols.Set(LEN(relevantSubcomponents));
  93. characters.nofRows.Set(nofCharacters DIV NofCols + 1);
  94. useFilter := TRUE; nofFilters := 0;
  95. Filter(nofFilters, filterArray);
  96. END New;
  97. PROCEDURE LoadAllCharacters;
  98. VAR temp : CharacterArray;
  99. g : WMCCGFonts.Glyph;
  100. i, j, t, nof, nz : LONGINT;
  101. histo : FilterHisto;
  102. PROCEDURE UpdateHisto(g : WMCCGFonts.Glyph);
  103. VAR i, ucs : LONGINT;
  104. BEGIN
  105. FOR i := 0 TO g.nofSubComponents - 1 DO
  106. ucs := g.subComponents[i].refucs;
  107. IF (ucs >= 0) & (ucs <MaxCharacterCode) THEN
  108. INC(histo[g.subComponents[i].refucs].freq)
  109. ELSE
  110. KernelLog.String("Strange..."); KernelLog.Hex(ucs, 0); KernelLog.Ln
  111. END
  112. END
  113. END UpdateHisto;
  114. BEGIN
  115. KernelLog.String("Loading all characters"); KernelLog.Ln;
  116. IF fontinfo # NIL THEN
  117. (* subcomponent histogram *)
  118. NEW(histo, MaxCharacterCode);
  119. FOR i := 0 TO MaxCharacterCode - 1 DO histo[i].ucs := i; histo[i].freq := 0 END;
  120. NEW(temp, MaxCharacterCode);
  121. nof := 0;
  122. FOR i := 0 TO MaxCharacterCode - 1 DO
  123. g := fontinfo.GetGlyph(i, 0);
  124. IF g # NIL THEN
  125. UpdateHisto(g);
  126. temp[nof] := g; INC(nof);
  127. WHILE g.nextVariant # NIL DO
  128. g := g.nextVariant;
  129. UpdateHisto(g);
  130. temp[nof] := g; INC(nof)
  131. END
  132. END
  133. END;
  134. NEW(allCharacters, nof);
  135. NEW(filtered, nof);
  136. FOR i := 0 TO nof - 1 DO allCharacters[i] := temp[i] END;
  137. nofCharacters := nof
  138. END;
  139. KernelLog.Int(nofCharacters, 5); KernelLog.String(" characters available"); KernelLog.Ln;
  140. KernelLog.String("Sorting histogram"); KernelLog.Ln;
  141. (* count non-zero *)
  142. nz := 0; FOR i := 0 TO MaxCharacterCode - 1 DO IF histo[i].freq > 0 THEN INC(nz) END END;
  143. NEW(relevantSubcomponents, nz);
  144. nz := 0;
  145. FOR i := 0 TO MaxCharacterCode - 1 DO
  146. IF histo[i].freq > 0 THEN
  147. j := 0; WHILE (j < nz) & (relevantSubcomponents[j].freq > histo[i].freq) DO INC(j) END;
  148. (* move smaller freq up *)
  149. t := nz - 1; WHILE t >= j DO relevantSubcomponents[t + 1] := relevantSubcomponents[t]; DEC(t) END;
  150. relevantSubcomponents[j] := histo[i];
  151. INC(nz)
  152. END
  153. END;
  154. END LoadAllCharacters;
  155. PROCEDURE Update;
  156. VAR codeStr, charString : ARRAY 16 OF CHAR; i : LONGINT;
  157. BEGIN
  158. paintBox.Invalidate;
  159. IF UnihanParser.HasCode(curChar) THEN
  160. charInfo := UnihanParser.GetCharacter(curChar);
  161. ELSE charInfo := NIL
  162. END;
  163. i := 0; IF UTF8Strings.EncodeChar(curChar, charString, i) THEN characterEdit.SetAsString(charString)
  164. ELSE characterEdit.SetAsString("")
  165. END;
  166. Strings.IntToHexStr(curChar, 0, codeStr);
  167. codeEdit.SetAsString(codeStr);
  168. IF charInfo # NIL THEN
  169. IF charInfo.pinyin # NIL THEN pinyinEdit.SetAsString(charInfo.pinyin^) ELSE pinyinEdit.SetAsString("<unknown>") END;
  170. IF charInfo.mandarin # NIL THEN mandarinEdit.SetAsString(charInfo.mandarin^) ELSE mandarinEdit.SetAsString("<unknown>") END;
  171. IF charInfo.cantonese # NIL THEN cantoneseEdit.SetAsString(charInfo.cantonese^) ELSE cantoneseEdit.SetAsString("<unknown>") END;
  172. IF charInfo.korean # NIL THEN koreanEdit.SetAsString(charInfo.korean^) ELSE koreanEdit.SetAsString("<unknown>") END;
  173. IF charInfo.definition # NIL THEN definitionEdit.SetAsString(charInfo.definition^) ELSE definitionEdit.SetAsString("<unknown>") END;
  174. ELSE
  175. pinyinEdit.SetAsString("<unknown>");
  176. mandarinEdit.SetAsString("<unknown>");
  177. cantoneseEdit.SetAsString("<unknown>");
  178. koreanEdit.SetAsString("<unknown>");
  179. definitionEdit.SetAsString("<unknown>");
  180. END
  181. END Update;
  182. PROCEDURE IsComponentUsed(glyph : WMCCGFonts.Glyph; code : LONGINT) : BOOLEAN;
  183. VAR i : LONGINT;
  184. result : BOOLEAN;
  185. BEGIN
  186. result := FALSE;
  187. IF glyph.ucs = code THEN RETURN TRUE END;
  188. FOR i := 0 TO glyph.nofSubComponents - 1 DO IF glyph.subComponents[i].refucs = code THEN result := TRUE END END;
  189. RETURN result
  190. END IsComponentUsed;
  191. PROCEDURE Filter(nofSubs : LONGINT; CONST subs : ARRAY OF LONGINT);
  192. VAR nof, i, j : LONGINT; ok : BOOLEAN;
  193. BEGIN
  194. KernelLog.String("Filtering for "); KernelLog.Hex(subs[0], 0); KernelLog.Ln;
  195. nof := 0;
  196. FOR i := 0 TO nofCharacters - 1 DO
  197. ok := TRUE; FOR j := 0 TO nofSubs- 1 DO IF ~IsComponentUsed(allCharacters[i], subs[j]) THEN ok := FALSE END END;
  198. IF ok THEN filtered[nof] := allCharacters[i]; INC(nof) END;
  199. END;
  200. KernelLog.String("remaining : "); KernelLog.Int(nof, 5); KernelLog.Ln;
  201. nofInFilter := nof
  202. END Filter;
  203. PROCEDURE ToggleFilter(sender, data :ANY);
  204. BEGIN
  205. nofFilters := 0;
  206. filterComponents.Invalidate;
  207. Filter(nofFilters, filterArray);
  208. characters.Invalidate
  209. END ToggleFilter;
  210. PROCEDURE NewCharacter(sender, data :ANY);
  211. VAR code : ARRAY 16 OF CHAR; i : LONGINT;
  212. BEGIN
  213. characterEdit.GetAsString(code);
  214. i := 0;
  215. IF UTF8Strings.DecodeChar(code, i, curChar) THEN Update ELSE
  216. curChar := 0; Update
  217. END;
  218. END NewCharacter;
  219. PROCEDURE NewCode(sender, data :ANY);
  220. VAR code, res : LONGINT;
  221. codeStr: ARRAY 9 OF CHAR;
  222. BEGIN
  223. codeEdit.GetAsString(codeStr);
  224. Strings.HexStrToInt(codeStr, code, res);
  225. IF res = 0 THEN curChar := code; Update END
  226. END NewCode;
  227. PROCEDURE SelectChar(sender, data :ANY);
  228. VAR l, t, r, b, pos : LONGINT;
  229. BEGIN
  230. characters.GetSelection(l, t, r, b);
  231. pos := t * NofCols + l;
  232. IF useFilter THEN
  233. IF pos < nofInFilter THEN
  234. curChar := filtered[pos].ucs;
  235. Update
  236. END
  237. ELSE
  238. IF pos < nofCharacters THEN
  239. curChar := allCharacters[pos].ucs;
  240. Update
  241. END
  242. END
  243. END SelectChar;
  244. PROCEDURE FilterSelection(sender, data :ANY);
  245. VAR l, t, r, b, pos : LONGINT;
  246. BEGIN
  247. filterComponents.GetSelection(l, t, r, b);
  248. pos := l;
  249. IF (relevantSubcomponents # NIL) & (pos < LEN(relevantSubcomponents)) THEN
  250. IF IsInFilterArray(relevantSubcomponents[pos].ucs) THEN RemoveFromFilter(relevantSubcomponents[pos].ucs)
  251. ELSE AddToFilter(relevantSubcomponents[pos].ucs)
  252. END;
  253. filterComponents.Invalidate;
  254. Filter(nofFilters, filterArray);
  255. characters.Invalidate;
  256. characters.SetTopPosition(0, 0, TRUE);
  257. END
  258. END FilterSelection;
  259. PROCEDURE IsInFilterArray(ucs : LONGINT) : BOOLEAN;
  260. VAR i : LONGINT;
  261. BEGIN
  262. FOR i := 0 TO nofFilters - 1 DO IF filterArray[i] = ucs THEN RETURN TRUE END END;
  263. RETURN FALSE
  264. END IsInFilterArray;
  265. PROCEDURE AddToFilter(ucs : LONGINT);
  266. BEGIN
  267. IF nofFilters < MaxFilters - 1 THEN filterArray[nofFilters] := ucs; INC(nofFilters) END
  268. END AddToFilter;
  269. PROCEDURE RemoveFromFilter(ucs : LONGINT);
  270. VAR a, i : LONGINT;
  271. BEGIN
  272. a := 0;
  273. FOR i := 0 TO nofFilters - 1 DO IF filterArray[i] # ucs THEN filterArray[a] := filterArray[i]; INC(a) ELSE DEC(nofFilters) END END;
  274. END RemoveFromFilter;
  275. PROCEDURE PaintCharacter(canvas : WMGraphics.Canvas);
  276. VAR g : WMCCGFonts.Glyph;
  277. points : ARRAY 2560 OF WMGraphics.Point2d;
  278. BEGIN
  279. IF (bigFont IS WMCCGFonts.Font) THEN
  280. g := bigFont(WMCCGFonts.Font).gf.GetGlyph(curChar, 0);
  281. IF g # NIL THEN
  282. bigFont(WMCCGFonts.Font).gf.RenderGlyphReal(canvas, g, 0, 0, 256, 256, 0,
  283. TRUE, 0FFH, WMGraphics.ModeSrcOverDst, points);
  284. END;
  285. END;
  286. END PaintCharacter;
  287. PROCEDURE Close*;
  288. BEGIN
  289. Close^;
  290. winstance := NIL
  291. END Close;
  292. PROCEDURE DrawAll(canvas : WMGraphics.Canvas; w, h : LONGINT; state : SET; x, y : LONGINT);
  293. VAR pos : LONGINT;
  294. points : ARRAY 2560 OF WMGraphics.Point2d;
  295. BEGIN
  296. IF WMGrids.CellHighlighted IN state THEN
  297. canvas.Fill(WMRectangles.MakeRect(0, 0, w, h), LONGINT(0FFFF00FFH), WMGraphics.ModeCopy)
  298. ELSE
  299. canvas.Fill(WMRectangles.MakeRect(0, 0, w, h), LONGINT(0FFFFFFFFH), WMGraphics.ModeCopy)
  300. END;
  301. pos := y * NofCols + x;
  302. IF useFilter THEN
  303. IF pos < nofInFilter THEN
  304. IF fontinfo # NIL THEN
  305. IF filtered[pos] # NIL THEN fontinfo.RenderGlyphReal(canvas, filtered[pos], 0, 0, w, h, 0,
  306. FALSE, 0FFH, WMGraphics.ModeSrcOverDst, points) END
  307. END
  308. END
  309. ELSE
  310. IF pos < nofCharacters THEN
  311. IF fontinfo # NIL THEN
  312. IF allCharacters[pos] # NIL THEN
  313. fontinfo.RenderGlyphReal(canvas, allCharacters[pos], 0, 0, w, h, 0, FALSE, 0FFH, WMGraphics.ModeSrcOverDst, points) END
  314. END
  315. END
  316. END
  317. END DrawAll;
  318. PROCEDURE DrawFilterComponents(canvas : WMGraphics.Canvas; w, h : LONGINT; state : SET; x, y : LONGINT);
  319. VAR pos : LONGINT; g : WMCCGFonts.Glyph;
  320. points : ARRAY 2560 OF WMGraphics.Point2d;
  321. BEGIN
  322. pos := x;
  323. IF (relevantSubcomponents # NIL) & (pos < LEN(relevantSubcomponents)) THEN
  324. IF WMGrids.CellHighlighted IN state THEN
  325. canvas.Fill(WMRectangles.MakeRect(0, 0, w, h), LONGINT(0FFFF00FFH), WMGraphics.ModeCopy)
  326. ELSE
  327. canvas.Fill(WMRectangles.MakeRect(0, 0, w, h), LONGINT(0FFFFFFFFH), WMGraphics.ModeCopy)
  328. END;
  329. IF IsInFilterArray(relevantSubcomponents[pos].ucs) THEN
  330. canvas.Fill(WMRectangles.MakeRect(0, 0, w, h), 0FFC0H, WMGraphics.ModeSrcOverDst)
  331. END;
  332. IF fontinfo # NIL THEN
  333. g := fontinfo.GetGlyph(relevantSubcomponents[pos].ucs, 0);
  334. IF g # NIL THEN fontinfo.RenderGlyphReal(canvas, g, 0, 0, w, h, 0, FALSE, 0FFH, WMGraphics.ModeSrcOverDst, points) END
  335. END
  336. END;
  337. END DrawFilterComponents;
  338. END Identifier;
  339. VAR
  340. winstance : Identifier;
  341. PROCEDURE Open*;
  342. BEGIN
  343. IF winstance = NIL THEN NEW(winstance) END;
  344. END Open;
  345. PROCEDURE Cleanup;
  346. BEGIN
  347. IF winstance # NIL THEN winstance.Close END
  348. END Cleanup;
  349. BEGIN
  350. Modules.InstallTermHandler(Cleanup)
  351. END CharacterLineup.
  352. SystemTools.Free CharacterLineup ~ UnihanParser ~
  353. CharacterLineup.Open ~