drawing.inc 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. {
  2. "SHEdit" - Text editor with syntax highlighting
  3. Copyright (C) 1999-2000 by Sebastian Guenther ([email protected])
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. }
  10. // Drawing code of TSHTextEdit (renderer for syntax highlighting engine);
  11. // also handles cursor drawing
  12. procedure TSHTextEdit.DoHighlighting(var flags: Byte; source, dest: PChar);
  13. begin
  14. StrCopy(dest, source);
  15. end;
  16. function TSHTextEdit.CalcSHFlags(FlagsIn: Byte; source: String): Byte;
  17. var
  18. s: PChar;
  19. flags: Byte;
  20. begin
  21. GetMem(s, Length(source) * 3 + 4);
  22. flags := FlagsIn;
  23. DoHighlighting(flags, PChar(source), s);
  24. FreeMem(s, Length(source) * 3 + 4);
  25. Result := flags;
  26. end;
  27. procedure TSHTextEdit.HideCursor;
  28. begin
  29. Dec(CursorVisible);
  30. if CursorVisible >= 0 then
  31. FWidget.HideCursor(CursorX, CursorY);
  32. end;
  33. procedure TSHTextEdit.ShowCursor;
  34. begin
  35. Inc(CursorVisible);
  36. if CursorVisible = 1 then
  37. FWidget.ShowCursor(CursorX, CursorY);
  38. end;
  39. procedure TSHTextEdit.ChangeInLine(line: Integer);
  40. var
  41. CurLine: Integer;
  42. OldFlags, NewFlags: Byte;
  43. begin
  44. // Determine how many lines must be redrawn
  45. CurLine := line;
  46. if CurLine = 0 then
  47. NewFlags := 0
  48. else
  49. NewFlags := FDoc.LineFlags[CurLine - 1];
  50. while CurLine < FDoc.LineCount - 1 do begin
  51. NewFlags := CalcSHFlags(NewFlags, FDoc.LineText[CurLine]);
  52. OldFlags := FDoc.LineFlags[CurLine + 1] and not LF_SH_Valid;
  53. FDoc.LineFlags[CurLine + 1] := NewFlags or LF_SH_Valid;
  54. if OldFlags = (NewFlags and not LF_SH_Valid) then break;
  55. Inc(CurLine);
  56. end;
  57. // Redraw all lines with changed SH flags
  58. FWidget.InvalidateRect(FWidget.HorzPos, line, FWidget.PageWidth, (CurLine - line) + 1);
  59. end;
  60. procedure TSHTextEdit.DrawContent(x, y, w, h: Integer);
  61. var
  62. x2, y2: Integer;
  63. procedure PostprocessOutput(py: Integer);
  64. begin
  65. // Erase free space below the text area
  66. if py < y2 then
  67. FWidget.ClearRect(x, py, w, y2 - py);
  68. if (FCursorX >= x) and (FCursorY >= y) and
  69. (FCursorX <= x2) and (FCursorY <= y2) then
  70. ShowCursor;
  71. end;
  72. // If Lenght(s) < x, add as many spaces to s so that x will be at
  73. // the end of s.
  74. procedure ProvideSpace(var s: String; x: Integer);
  75. begin
  76. while Length(s) < x do
  77. s := s + ' ';
  78. end;
  79. var
  80. i, cx, LineNumber, CheckLine: Integer;
  81. OrigStr, s, s2: PChar;
  82. spos, smem: Integer;
  83. flags: Byte;
  84. InSel: Boolean;
  85. LastCol: Char;
  86. LineWithSpace: String; // used for virtual whitespace expanding
  87. begin
  88. if x < 0 then begin
  89. Inc(w, x);
  90. x := 0;
  91. end;
  92. if y < 0 then begin
  93. Inc(h, y);
  94. y := 0;
  95. end;
  96. if (w < 0) or (h < 0) then exit;
  97. x2 := x + w;
  98. y2 := y + h;
  99. if (FCursorX >= x) and (FCursorY >= y) and
  100. (FCursorX <= x2) and (FCursorY <= y2) then
  101. //HideCursor;
  102. Dec(CursorVisible);
  103. if (FDoc = nil) or (FDoc.LineCount <= y) then begin
  104. PostprocessOutput(y);
  105. exit;
  106. end;
  107. LineNumber := y;
  108. // Check if syntax highlighting flags are valid:
  109. if (FDoc.LineFlags[LineNumber] and LF_SH_Valid) <> 0 then
  110. flags := FDoc.LineFlags[LineNumber] and not LF_SH_Valid
  111. else begin
  112. // search for last valid line before the first line to be drawn
  113. CheckLine := LineNumber;
  114. while (CheckLine >= 0) and
  115. ((FDoc.LineFlags[CheckLine] and LF_SH_Valid) = 0) do Dec(CheckLine);
  116. if CheckLine >= 0 then begin
  117. flags := FDoc.LineFlags[CheckLine] and not LF_SH_Valid;
  118. // Recalc SH flags for all lines between last valid and first visible line
  119. while (CheckLine < LineNumber) do begin
  120. flags := CalcSHFlags(flags, FDoc.LineText[CheckLine]);
  121. FDoc.LineFlags[CheckLine] := flags or LF_SH_Valid;
  122. Inc(CheckLine);
  123. end;
  124. end else
  125. flags := 0;
  126. end;
  127. // if FSel.IsValid then writeln('Selection: ',FSel.OStartX,',',FSel.OStartY,'-',FSel.OEndX,',',FSel.OEndY);
  128. while (LineNumber < FDoc.LineCount) and (LineNumber <= y2) do begin
  129. i := 0;
  130. // Do virtual whitespace expanding
  131. LineWithSpace := FDoc.LineText[LineNumber];
  132. if LineNumber = FSel.OStartY then
  133. ProvideSpace(LineWithSpace, FSel.OStartX);
  134. if LineNumber = FSel.OEndY then
  135. ProvideSpace(LineWithSpace, FSel.OEndX);
  136. if LineNumber = FCursorY then
  137. ProvideSpace(LineWithSpace, FCursorX);
  138. // Call syntax highlighter for this line
  139. smem := Length(LineWithSpace) * 3 + 8;
  140. GetMem(s, smem);
  141. FDoc.LineFlags[LineNumber] := flags or LF_SH_Valid;
  142. OrigStr := PChar(LineWithSpace);
  143. DoHighlighting(flags, OrigStr, s);
  144. // Handle current selection
  145. if FSel.IsValid then
  146. if (LineNumber > FSel.OStartY) and (LineNumber < FSel.OEndY) then begin
  147. ASSERT(smem > StrLen(OrigStr) + 2);
  148. s[0] := LF_Escape;
  149. s[1] := Chr(shSelected);
  150. StrCopy(@s[2], OrigStr);
  151. end else if OrigStr[0] = #0 then begin
  152. if LineNumber = FSel.OStartY then begin
  153. ASSERT(smem >= 3);
  154. s[0] := LF_Escape;
  155. s[1] := Chr(shSelected);
  156. s[2] := #0;
  157. end;
  158. end else if ((LineNumber = FSel.OStartY) or (LineNumber = FSel.OEndY))
  159. and not FSel.IsEmpty then begin
  160. s2 := StrNew(s);
  161. spos := 0;
  162. i := 0;
  163. cx := 0;
  164. if LineNumber > FSel.OStartY then begin
  165. ASSERT(smem >= 2);
  166. s[0] := LF_Escape;
  167. s[1] := Chr(shSelected);
  168. InSel := True;
  169. spos := 2;
  170. end else
  171. InSel := False;
  172. LastCol := Chr(shDefault);
  173. while True do begin
  174. ASSERT(i <= StrLen(s2));
  175. if s2[i] = LF_Escape then begin
  176. LastCol := s2[i + 1];
  177. if not InSel then begin
  178. ASSERT(smem > spos + 1);
  179. s[spos] := LF_Escape;
  180. s[spos + 1] := LastCol;
  181. Inc(spos, 2);
  182. end;
  183. Inc(i, 2);
  184. end else begin
  185. if InSel then begin
  186. if (LineNumber = FSel.OEndY) and (cx = FSel.OEndX) then begin
  187. ASSERT(smem > spos + 1);
  188. s[spos] := LF_Escape;
  189. s[spos + 1] := LastCol;
  190. Inc(spos, 2);
  191. InSel := False;
  192. end;
  193. end else
  194. if (LineNumber = FSel.OStartY) and (cx = FSel.OStartX) then begin
  195. ASSERT(smem > spos + 1);
  196. s[spos] := LF_Escape;
  197. s[spos + 1] := Chr(shSelected);
  198. Inc(spos, 2);
  199. InSel := True;
  200. end;
  201. if s2[i] = #0 then break; // only exit of 'while' loop!
  202. ASSERT(smem > spos);
  203. s[spos] := s2[i];
  204. Inc(spos);
  205. Inc(i);
  206. Inc(cx);
  207. end;
  208. end;
  209. ASSERT(smem > spos);
  210. s[spos] := #0;
  211. StrDispose(s2);
  212. end;
  213. FWidget.DrawTextLine(x, x2, LineNumber, s);
  214. FreeMem(s);
  215. Inc(LineNumber);
  216. end;
  217. PostprocessOutput(LineNumber);
  218. end;