bclealcddisplay_editorform.pas 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. unit BCLeaLCDDisplay_EditorForm;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. LCLIntf, Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,
  6. Grids, ExtCtrls, Buttons, BCLeaLCDDisplay;
  7. type
  8. { TBCLeaLCDCharDefsEditor }
  9. TBCLeaLCDCharDefsEditor = class(TForm)
  10. btAdd: TBitBtn;
  11. btReplace: TBitBtn;
  12. btDelete: TBitBtn;
  13. btOK: TBitBtn;
  14. btCancel: TBitBtn;
  15. cbCharSelector: TComboBox;
  16. dgDotMatrix: TDrawGrid;
  17. ImageList1: TImageList;
  18. Label1: TLabel;
  19. pnButtons: TPanel;
  20. pnOKCancel: TPanel;
  21. procedure btAddClick(Sender: TObject);
  22. procedure btDeleteClick(Sender: TObject);
  23. procedure btReplaceClick(Sender: TObject);
  24. procedure cbCharSelectorChange(Sender: TObject);
  25. procedure dgDotMatrixKeyDown(Sender: TObject; var Key: word;
  26. {%H-}Shift: TShiftState);
  27. procedure dgDotMatrixMouseDown(Sender: TObject; {%H-}Button: TMouseButton;
  28. {%H-}Shift: TShiftState; X, Y: integer);
  29. procedure dgDotMatrixMouseMove(Sender: TObject; Shift: TShiftState; X, Y: integer);
  30. procedure dgDotMatrixPrepareCanvas({%H-}Sender: TObject; aCol, aRow: integer;
  31. {%H-}aState: TGridDrawState);
  32. procedure FormActivate(Sender: TObject);
  33. procedure FormCloseQuery(Sender: TObject; var CanClose: boolean);
  34. procedure FormDestroy(Sender: TObject);
  35. private
  36. FBCLeaLCDDisplay: TBCLeaLCDDisplay;
  37. FModified: boolean;
  38. FSavedCharDefs: TBCLeaCharDefs;
  39. FSelectedChar: string;
  40. FTmpDotRows: TDotRows;
  41. FOldRow, FOldCol: integer;
  42. procedure SetBCLeaLCDDisplay(AValue: TBCLeaLCDDisplay);
  43. procedure PopulateCharSelector;
  44. procedure SaveCharDefs;
  45. procedure ClearEditorGrid;
  46. procedure SetupEditorGrid;
  47. function GetDotMatrix: TDotRows;
  48. function DotSet(ACol, ARow: integer): boolean;
  49. procedure SetDot(ACol, ARow: integer; AValue: boolean);
  50. procedure ToggleDot(ACol, ARow: integer);
  51. public
  52. property BCLeaLCDDisplay: TBCLeaLCDDisplay read FBCLeaLCDDisplay write SetBCLeaLCDDisplay;
  53. end;
  54. var
  55. BCLeaLCDCharDefsEditor: TBCLeaLCDCharDefsEditor;
  56. implementation
  57. {$R *.lfm}
  58. uses
  59. Math, ButtonPanel;
  60. function CharInputBox(const ACaption, APrompt, ADefault: string): string;
  61. var
  62. F: TForm;
  63. ed: TEdit;
  64. lbl: TLabel;
  65. bp: TButtonPanel;
  66. begin
  67. F := TForm.CreateNew(nil);
  68. try
  69. F.Caption := ACaption;
  70. F.BorderStyle := bsDialog;
  71. lbl := TLabel.Create(F);
  72. lbl.AnchorSideTop.Control := F;
  73. lbl.AnchorSideLeft.Control := F;
  74. lbl.BorderSpacing.Top := F.Scale96ToFont(12);
  75. lbl.BorderSpacing.Bottom := F.Scale96ToFont(2);
  76. lbl.BorderSpacing.Left := F.Scale96ToFont(12);
  77. lbl.BorderSpacing.Right := F.Scale96ToFont(12);
  78. lbl.WordWrap := True;
  79. lbl.AutoSize := True;
  80. lbl.Caption := APrompt;
  81. lbl.Parent := F;
  82. lbl.AdjustSize;
  83. ed := TEdit.Create(F);
  84. ed.AnchorSideTop.Control := lbl;
  85. ed.AnchorSideTop.Side := asrBottom;
  86. ed.AnchorSideLeft.Control := F;
  87. ed.AnchorSideRight.Control := F;
  88. ed.AnchorSideRight.Side := asrRight;
  89. ed.Anchors := [akLeft, akTop, akRight];
  90. ed.BorderSpacing.Left := F.Scale96ToFont(12);
  91. ed.BorderSpacing.Right := F.Scale96ToFont(12);
  92. ed.BorderSpacing.Bottom := F.Scale96ToFont(18);
  93. ed.MaxLength := 1;
  94. ed.Text := ADefault;
  95. ed.Parent := F;
  96. ed.AdjustSize;
  97. bp := TButtonPanel.Create(F);
  98. bp.ShowButtons := [pbOK, pbCancel];
  99. bp.Parent := F;
  100. bp.AdjustSize;
  101. F.Constraints.MinHeight := ed.Top + ed.Height + ed.BorderSpacing.Bottom + bp.Height + 2 * bp.BorderSpacing.Around;
  102. F.AutoSize := True;
  103. F.Position := poScreenCenter;
  104. if F.ShowModal = mrOk then
  105. Result := ed.Text
  106. else
  107. Result := ADefault;
  108. finally
  109. F.Free;
  110. end;
  111. end;
  112. procedure TBCLeaLCDCharDefsEditor.PopulateCharSelector;
  113. var
  114. i: integer;
  115. begin
  116. cbCharSelector.DropdownCount := 24;
  117. cbCharSelector.Items.BeginUpdate;
  118. try
  119. cbCharSelector.Clear;
  120. for i := 0 to FBCLeaLCDDisplay.CharDefs.Count - 1 do
  121. cbCharSelector.Items.Add(FBCLeaLCDDisplay.CharDefs.CharByIndex[i]);
  122. finally
  123. cbCharSelector.Items.EndUpdate;
  124. end;
  125. end;
  126. procedure TBCLeaLCDCharDefsEditor.btDeleteClick(Sender: TObject);
  127. begin
  128. if FSelectedChar <> '' then
  129. begin
  130. FBCLeaLCDDisplay.CharDefs.Delete(FSelectedChar);
  131. FBCLeaLCDDisplay.Invalidate;
  132. end;
  133. PopulateCharSelector;
  134. end;
  135. procedure TBCLeaLCDCharDefsEditor.btAddClick(Sender: TObject);
  136. var
  137. newChar: string;
  138. begin
  139. newChar := CharInputBox('Dot matrix for...', 'Character', '');
  140. if newChar = '' then
  141. exit;
  142. // Check whether the new character already has a dot matrix.
  143. if FBCLeaLCDDisplay.CharDefs.Find(newChar) then
  144. begin
  145. MessageDlg(Format('Character "%s" already exists and cannot be added.', [newChar]),
  146. mtError, [mbOK], 0);
  147. exit;
  148. end;
  149. // Add new character and its dot matrix to the BCLeaLCDDisplay...
  150. FSelectedChar := newChar;
  151. FBCLeaLCDDisplay.CharDefs.Add(FSelectedChar, GetDotMatrix);
  152. FBCLeaLCDDisplay.Invalidate;
  153. // ... and update the editor form
  154. PopulateCharSelector;
  155. cbCharSelector.ItemIndex := cbCharSelector.Items.IndexOf(FSelectedChar);
  156. FModified := False;
  157. end;
  158. { Replaces the dotmatrix of the currently loaded character by the dotmatrix in
  159. the editor. }
  160. procedure TBCLeaLCDCharDefsEditor.btReplaceClick(Sender: TObject);
  161. begin
  162. if FSelectedChar <> '' then
  163. begin
  164. FBCLeaLCDDisplay.CharDefs.DotRows[FSelectedChar] := GetDotMatrix;
  165. FBCLeaLCDDisplay.Invalidate;
  166. FModified := False;
  167. end;
  168. end;
  169. procedure TBCLeaLCDCharDefsEditor.cbCharSelectorChange(Sender: TObject);
  170. begin
  171. FSelectedChar := cbCharSelector.Text;
  172. if FSelectedChar <> '' then
  173. FTmpDotRows := FBCLeaLCDDisplay.CharDefs.DotRows[FSelectedChar]
  174. else
  175. ClearEditorGrid;
  176. dgDotMatrix.Invalidate;
  177. FModified := False;
  178. end;
  179. procedure TBCLeaLCDCharDefsEditor.dgDotMatrixKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
  180. var
  181. r, c: integer;
  182. begin
  183. r := dgDotMatrix.Row;
  184. c := dgDotMatrix.Col;
  185. if Key = 32 then
  186. begin
  187. ToggleDot(c, r);
  188. dgDotMatrix.InvalidateCell(c, r);
  189. FModified := True;
  190. end;
  191. end;
  192. procedure TBCLeaLCDCharDefsEditor.dgDotMatrixMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: integer);
  193. var
  194. r, c: integer;
  195. begin
  196. dgDotMatrix.MouseToCell(X, Y, c, r);
  197. ToggleDot(c, r);
  198. dgDotMatrix.InvalidateCell(c, r);
  199. FOldRow := r;
  200. FOldCol := c;
  201. FModified := True;
  202. end;
  203. procedure TBCLeaLCDCharDefsEditor.dgDotMatrixMouseMove(Sender: TObject; Shift: TShiftState; X, Y: integer);
  204. var
  205. r, c: integer;
  206. begin
  207. if Shift = [ssLeft] then
  208. begin
  209. dgDotMatrix.MouseToCell(X, Y, c, r);
  210. if (c <> FOldCol) or (r <> FOldRow) then
  211. begin
  212. ToggleDot(c, r);
  213. dgDotMatrix.InvalidateCell(c, r);
  214. FOldRow := r;
  215. FOldCol := c;
  216. FModified := True;
  217. end;
  218. end;
  219. end;
  220. procedure TBCLeaLCDCharDefsEditor.dgDotMatrixPrepareCanvas(Sender: TObject; aCol, aRow: integer; aState: TGridDrawState);
  221. begin
  222. if DotSet(ACol, ARow) then
  223. dgDotMatrix.Canvas.Brush.Color := clBlack
  224. else
  225. dgDotMatrix.Canvas.Brush.Color := clWhite;
  226. end;
  227. procedure TBCLeaLCDCharDefsEditor.FormActivate(Sender: TObject);
  228. var
  229. w: integer;
  230. begin
  231. w := Max(btOK.Width, btCancel.Width);
  232. btOK.Constraints.MinWidth := w;
  233. btCancel.Constraints.MinWidth := w;
  234. end;
  235. procedure TBCLeaLCDCharDefsEditor.FormCloseQuery(Sender: TObject; var CanClose: boolean);
  236. const
  237. mrReplace = -20;
  238. mrAddAs = -21;
  239. var
  240. res: integer;
  241. begin
  242. if FModified and (ModalResult = mrOk) then
  243. begin
  244. res := QuestionDlg('Confirmation', 'Current dotmatrix of "' + FSelectedChar + '" has not yet been applied. What do you want to do?',
  245. mtConfirmation, [mrReplace, 'Replace', mrAddAs, 'Add as...', 'isDefault', mrCancel, 'Cancel'], 0);
  246. case res of
  247. mrReplace:
  248. btReplaceClick(nil);
  249. mrAddAs:
  250. btAddClick(nil);
  251. mrCancel:
  252. ;
  253. end;
  254. CanClose := (res <> mrCancel) and (not FModified);
  255. end;
  256. if CanClose and (ModalResult <> mrOk) then
  257. begin
  258. FBCLeaLCDDisplay.CharDefs.Assign(FSavedCharDefs);
  259. FBCLeaLCDDisplay.Invalidate;
  260. end;
  261. end;
  262. procedure TBCLeaLCDCharDefsEditor.FormDestroy(Sender: TObject);
  263. begin
  264. FreeAndNil(FSavedCharDefs);
  265. end;
  266. function TBCLeaLCDCharDefsEditor.GetDotMatrix: TDotRows;
  267. begin
  268. Result := FTmpDotRows;
  269. end;
  270. { Tests whether the dot corresponding to the specified grid column and row is
  271. set. Note that the low-bit is at the right! }
  272. function TBCLeaLCDCharDefsEditor.DotSet(ACol, ARow: integer): boolean;
  273. var
  274. c: integer;
  275. begin
  276. c := dgDotMatrix.ColCount - 1 - ACol;
  277. Result := FTmpDotRows[ARow] and (1 shl c) <> 0; // avoid integer helper to keep usability with old fpc versions
  278. end;
  279. { Sets the dot in the specified grid column and row if AValue is true,
  280. or clears it if AValue is false.
  281. Note that the low-bit is at the right of the grid! }
  282. procedure TBCLeaLCDCharDefsEditor.SetDot(ACol, ARow: integer; AValue: boolean);
  283. var
  284. c: integer;
  285. lDotRows: TDotRows;
  286. begin
  287. c := dgDotMatrix.ColCount - 1 - ACol;
  288. lDotRows := CopyDotRows(FTmpDotRows);
  289. if AValue then
  290. lDotRows[ARow] := lDotRows[ARow] or (1 shl c)
  291. else
  292. lDotRows[ARow] := lDotRows[ARow] and not (1 shl c); // avoid integer helper to keep usability with old fpc version
  293. FTmpDotRows := CopyDotRows(lDotRows);
  294. end;
  295. { Toggles the dot in the specified grid column/row }
  296. procedure TBCLeaLCDCharDefsEditor.ToggleDot(ACol, ARow: integer);
  297. begin
  298. SetDot(ACol, ARow, not DotSet(ACol, ARow));
  299. end;
  300. { Save the char defs so that they can be restored if the form is not closed by OK. }
  301. procedure TBCLeaLCDCharDefsEditor.SaveCharDefs;
  302. begin
  303. FSavedCharDefs.Free;
  304. FSavedCharDefs := TBCLeaCharDefs.Create(nil);
  305. FSavedCharDefs.Assign(FBCLeaLCDDisplay.CharDefs);
  306. end;
  307. procedure TBCLeaLCDCharDefsEditor.SetBCLeaLCDDisplay(AValue: TBCLeaLCDDisplay);
  308. begin
  309. FBCLeaLCDDisplay := AValue;
  310. SetLength(FTmpDotRows, FBCLeaLCDDisplay.DotRowCount);
  311. SaveCharDefs;
  312. PopulateCharSelector;
  313. SetupEditorGrid;
  314. end;
  315. procedure TBCLeaLCDCharDefsEditor.ClearEditorGrid;
  316. var
  317. i: integer;
  318. begin
  319. for i := 0 to High(FTmpDotRows) do
  320. FTmpDotRows[i] := 0;
  321. end;
  322. { Reads the size of the dot matrix from FBCLeaLCDDisplay and use it to define the
  323. number of rows and columns in the editor grid. }
  324. procedure TBCLeaLCDCharDefsEditor.SetupEditorGrid;
  325. begin
  326. dgDotMatrix.RowCount := FBCLeaLCDDisplay.DotRowCount;
  327. ClearEditorGrid;
  328. dgDotmatrix.ClientWidth := dgDotMatrix.ColCount * dgDotMatrix.DefaultColWidth;
  329. dgDotMatrix.ClientHeight := dgDotMatrix.RowCount * dgDotMatrix.DefaultRowHeight;
  330. dgDotMatrix.Constraints.MinWidth := dgDotMatrix.Width;
  331. dgDotMatrix.Constraints.MinHeight := dgDotMatrix.Height;
  332. end;
  333. end.