CnHexEditor.pas 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460
  1. {******************************************************************************}
  2. { CnPack For Delphi/C++Builder }
  3. { 中国人自己的开放源码第三方开发包 }
  4. { (C)Copyright 2001-2006 CnPack 开发组 }
  5. { ------------------------------------ }
  6. { }
  7. { 本开发包是开源的自由软件,您可以遵照 CnPack 的发布协议来修 }
  8. { 改和重新发布这一程序。 }
  9. { }
  10. { 发布这一开发包的目的是希望它有用,但没有任何担保。甚至没有 }
  11. { 适合特定目的而隐含的担保。更详细的情况请参阅 CnPack 发布协议。 }
  12. { }
  13. { 您应该已经和开发包一起收到一份 CnPack 发布协议的副本。如果 }
  14. { 还没有,可访问我们的网站: }
  15. { }
  16. { 网站地址:http://www.cnpack.org }
  17. { 电子邮件:master@cnpack.org }
  18. { }
  19. {******************************************************************************}
  20. unit CnHexEditor;
  21. {* |<PRE>
  22. ================================================================================
  23. * 软件名称:CnPack 可视化组件包
  24. * 单元名称:CnHexEditor 文件十六进制查看修改实现单元
  25. * 单元作者:Zswang(原创) 2006-12-28 wjhu111@21cn.com
  26. * Guye (移植)
  27. * 备 注:该单元为 CnPack 组件包的一部分,实现了文件十六进制查看功能, 为可视
  28. 化组件, 但本次版本功能上欠佳, 预计复制等功能在下个版本中升级修改。
  29. * 开发平台:PWinXP + Delphi 7
  30. * 兼容测试:PWin9X/2000/XP + Delphi 5/6/7 + C++Builder 5/6
  31. * 本 地 化:该单元中的字符串均符合本地化处理方式
  32. * 单元标识:$Id$
  33. * 修改记录:2012.09.26 V1.2
  34. * 增加一DataChange方法供修改MemoryStream后更新界面用,感谢veket
  35. * 2012.03.03 V1.1
  36. * 暂时屏蔽CMFONTCHANGED的第一次消息以免画错,原因不详
  37. * 2008.01.15 V1.0 by Guye
  38. * 优化代码, 修改移植入 CnPack
  39. ================================================================================
  40. |</PRE>}
  41. interface
  42. {$I CnPack.inc}
  43. uses
  44. Windows, Messages, SysUtils, Classes, Controls, Graphics, Forms;
  45. type
  46. TCnWMImeChar = packed record
  47. Msg: Cardinal;
  48. case Integer of
  49. 0: (
  50. CharCode: Word;
  51. KeyData: Longint;
  52. Result: Longint);
  53. 1: (
  54. CharCode1: Byte;
  55. CharCode2: Byte);
  56. end;
  57. type
  58. TCnMouseObject = (moNone, moAddress, moHex, moChar);
  59. type
  60. TCnHexEditor = class(TCustomControl)
  61. private
  62. { Private declarations }
  63. FFirstCmFontChanged: Boolean;
  64. FMemoryStream: TMemoryStream;
  65. FBaseAddress: Integer;
  66. FLineCount: Integer;
  67. FVisibleChars: Integer;
  68. FTopLine: Integer;
  69. FLeftLine: Integer;
  70. FRowIndex: Integer;
  71. FVisibleLines: Integer;
  72. FItemHeight: Integer;
  73. FItemWidth: Integer;
  74. FColIndex: Integer;
  75. FColType: TCnMouseObject;
  76. FReadOnly: Boolean;
  77. FSelLength: Integer;
  78. FSelStart: Integer;
  79. FAnchorStart: Integer;
  80. FAnchorOffset: Integer;
  81. FHexChar: Char;
  82. FOnSelectionChange: TNotifyEvent;
  83. FChangeDataSize: Boolean;
  84. procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
  85. procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
  86. procedure WMHScroll(var Message: TWMHScroll); message WM_HSCROLL;
  87. procedure WMSize(var Message: TWMSize); message WM_SIZE;
  88. procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
  89. procedure AdjustScrollBars;
  90. procedure SetRowIndex(Value: Integer);
  91. procedure SetColIndex(Value: Integer);
  92. procedure SetLeftLine(Value: Integer);
  93. procedure SetTopLine(Value: Integer);
  94. procedure SetBaseAddress(const Value: Integer);
  95. function LineViewText(mLineIndex: Integer): string;
  96. function SelectionViewText(mColType: TCnMouseObject; mLineIndex: Integer;
  97. mStart, mEnd: Integer): string;
  98. property TopLine: Integer read FTopLine write SetTopLine;
  99. property LeftLine: Integer read FLeftLine write SetLeftLine;
  100. function MouseObject(mPoint: TPoint; var nCoordinate: TPoint): TCnMouseObject;
  101. function CoordinateToPoint(mMouseObject: TCnMouseObject; mCoordinate: TPoint): TPoint;
  102. function PositionToCoordinate(mPosition: Integer): TPoint;
  103. function CoordinatePosition(mCoordinate: TPoint): Integer;
  104. function ColToChar(mColType: TCnMouseObject; mCol: Integer): Integer;
  105. procedure SetColType(const Value: TCnMouseObject);
  106. function RowMaxIndex(mRowIndex: Integer): Integer;
  107. procedure SetReadOnly(const Value: Boolean);
  108. procedure SetSelLength(const Value: Integer);
  109. procedure SetSelStart(Value: Integer);
  110. procedure SetAnchorOffset(Value: Integer);
  111. procedure WMIMECHAR(var Msg: TCnWMImeChar); message WM_IME_CHAR;
  112. procedure WMCHAR(var Msg: TWMChar); message WM_CHAR;
  113. protected
  114. { Protected declarations }
  115. function GetSelText: string; virtual;
  116. procedure SetSelText(const Value: string); virtual;
  117. procedure DoChange; virtual;
  118. procedure SelectionChange; virtual;
  119. procedure CreateParams(var Params: TCreateParams); override;
  120. procedure Paint; override;
  121. procedure DoExit; override;
  122. procedure DoEnter; override;
  123. procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  124. procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
  125. procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
  126. procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
  127. function DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean; override;
  128. function DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean; override;
  129. public
  130. { Public declarations }
  131. constructor Create(AOwner: TComponent); override;
  132. destructor Destroy; override;
  133. procedure LoadFromBuffer(const Buffer; Size: Integer);
  134. procedure LoadFromStream(Stream: TStream);
  135. procedure LoadFromFile(FileName: TFileName);
  136. procedure SaveToStream(Stream: TStream);
  137. procedure SaveToFile(FileName: TFileName);
  138. procedure SaveToBuffer(var Buffer; Size: Integer);
  139. property MemoryStream: TMemoryStream read FMemoryStream;
  140. property BaseAddress: Integer read FBaseAddress write SetBaseAddress; //基地址
  141. property RowIndex: Integer read FRowIndex write SetRowIndex; //当前行数
  142. property ColIndex: Integer read FColIndex write SetColIndex; //当前列数
  143. property ColType: TCnMouseObject read FColType write SetColType; //当前列是否十六进制
  144. property SelStart: Integer read FSelStart write SetSelStart; //选择文本的开始位置
  145. property SelLength: Integer read FSelLength write SetSelLength; //选择文本的长度
  146. property SelText: string read GetSelText write SetSelText; //选中的文本
  147. property AnchorOffset: Integer read FAnchorOffset write SetAnchorOffset;
  148. function ScrollIntoView: Boolean;
  149. procedure UpdateCaret;
  150. procedure DataChange;
  151. published
  152. { Published declarations }
  153. property Align;
  154. property Anchors;
  155. property Enabled;
  156. property Font;
  157. property Color;
  158. property ReadOnly: Boolean read FReadOnly write SetReadOnly default False;
  159. property ChangeDataSize: Boolean read FChangeDataSize write FChangeDataSize default True;
  160. property ParentFont;
  161. property ParentColor;
  162. property PopupMenu;
  163. property TabOrder;
  164. property TabStop;
  165. property Visible;
  166. property OnEnter;
  167. property OnExit;
  168. property OnKeyDown;
  169. property OnKeyPress;
  170. property OnKeyUp;
  171. property OnSelectionChange: TNotifyEvent read FOnSelectionChange write FOnSelectionChange;
  172. end;
  173. implementation
  174. uses Math;
  175. //------------------------------------------------------------------------------
  176. // 流插入数据
  177. //------------------------------------------------------------------------------
  178. function CnInsertStream(Stream: TStream; Offset: Integer; const Buffer;
  179. Length: Integer): Boolean;
  180. var
  181. vBuffer: array[0..$1000-1] of Char;
  182. I, L: Integer;
  183. begin
  184. Result := False;
  185. if not Assigned(Stream) then Exit;
  186. if Length <= 0 then Exit;
  187. if Offset >= Stream.Size then Exit;
  188. if Offset < 0 then Exit;
  189. I := Stream.Size;
  190. Stream.Size := Stream.Size + Length;
  191. repeat
  192. if Offset + Length <= I - SizeOf(vBuffer) then
  193. L := SizeOf(vBuffer)
  194. else L := I - Offset;
  195. Stream.Position := I - L;
  196. Stream.Read(vBuffer, L);
  197. Stream.Position := I - L + Length;
  198. Stream.Write(vBuffer, L);
  199. I := I - L + Length;
  200. until L < SizeOf(vBuffer);
  201. Stream.Position := Offset;
  202. Stream.Write(Buffer, Length);
  203. end;
  204. //------------------------------------------------------------------------------
  205. // 删除流数据
  206. //------------------------------------------------------------------------------
  207. function CnDeleteStream(Stream: TStream; Offset: Integer;
  208. Length: Integer): Boolean;
  209. var
  210. Buffer: array[0..$1000-1] of Char;
  211. I, L: Integer;
  212. begin
  213. Result := False;
  214. if not Assigned(Stream) then Exit;
  215. if Length <= 0 then Exit;
  216. if Offset >= Stream.Size then Exit;
  217. if Offset < 0 then Exit;
  218. if Offset + Length >= Stream.Size then
  219. Stream.Size := Offset
  220. else
  221. begin
  222. I := Offset;
  223. repeat
  224. Stream.Position := I + Length;
  225. L := Stream.Read(Buffer, SizeOf(Buffer));
  226. Stream.Position := I;
  227. Stream.Write(Buffer, L);
  228. Inc(I, L);
  229. until L < SizeOf(Buffer);
  230. Stream.Size := Stream.Size - Length;
  231. end;
  232. Result := True;
  233. end;
  234. procedure TCnHexEditor.AdjustScrollBars;
  235. var
  236. ScrlInfo: TScrollInfo;
  237. begin
  238. SetScrollRange(Handle, SB_VERT, 0, FLineCount, True);
  239. SetScrollRange(Handle, SB_HORZ, 0, 76, True);
  240. ScrlInfo.fMask := SIF_PAGE;
  241. ScrlInfo.nPage := FVisibleLines;
  242. SetScrollInfo(Handle, SB_VERT, ScrlInfo, True);
  243. ScrlInfo.fMask := SIF_PAGE;
  244. ScrlInfo.nPage := FVisibleChars;
  245. SetScrollInfo(Handle, SB_HORZ, ScrlInfo, True);
  246. end;
  247. procedure TCnHexEditor.CMFontChanged(var Message: TMessage);
  248. begin
  249. inherited;
  250. Canvas.Font := Self.Font;
  251. // First Font Changed Message Will cause Draw Invert to out of Parent. Dont know why.
  252. if FFirstCmFontChanged then
  253. begin
  254. FFirstCmFontChanged := False;
  255. Exit;
  256. end;
  257. DoChange;
  258. end;
  259. function TCnHexEditor.CoordinateToPoint(mMouseObject: TCnMouseObject;
  260. mCoordinate: TPoint): TPoint;
  261. begin
  262. case mMouseObject of
  263. moChar, moHex:
  264. begin
  265. Result.Y := mCoordinate.Y * FItemHeight;
  266. Result.X := ColToChar(mMouseObject, mCoordinate.X) * FItemWidth;
  267. end;
  268. moAddress:
  269. begin
  270. Result.Y := mCoordinate.Y * FItemHeight;
  271. Result.X := 0;
  272. end;
  273. else Result := Point(-1, -1);
  274. end;
  275. Result.X := Result.X - FLeftLine * FItemWidth;
  276. Result.Y := Result.Y - FTopLine * FItemHeight;
  277. end;
  278. constructor TCnHexEditor.Create(AOwner: TComponent);
  279. begin
  280. inherited;
  281. ControlStyle := [csFramed, csCaptureMouse];
  282. Width := 300;
  283. Height := 200;
  284. ParentColor := False;
  285. Color := clWindow;
  286. FMemoryStream := TMemoryStream.Create;
  287. DoubleBuffered := True;
  288. FChangeDataSize := True;
  289. FColType := moHex;
  290. FFirstCmFontChanged := True;
  291. try
  292. Font.Name := 'Fixedsys'; // 用等宽字体
  293. except
  294. ;
  295. end;
  296. end;
  297. procedure TCnHexEditor.CreateParams(var Params: TCreateParams);
  298. begin
  299. inherited CreateParams(Params);
  300. with Params do
  301. Style := Style or WS_VSCROLL or WS_HSCROLL;
  302. end;
  303. destructor TCnHexEditor.Destroy;
  304. begin
  305. FMemoryStream.Free;
  306. inherited;
  307. end;
  308. procedure TCnHexEditor.DoChange;
  309. begin
  310. FItemHeight := Canvas.TextHeight('A');
  311. FItemWidth := Canvas.TextWidth('D');
  312. FLineCount := (FMemoryStream.Size div 16) + 1;
  313. FVisibleChars := (ClientWidth div Canvas.TextWidth('D')) + 1;
  314. FVisibleLines := (ClientHeight div FItemHeight) + 1;
  315. LeftLine := Min(LeftLine, 76 - FVisibleChars + 1);
  316. TopLine := Min(TopLine, FLineCount - FVisibleLines + 1);
  317. AdjustScrollBars;
  318. UpdateCaret;
  319. Invalidate;
  320. ScrollIntoView;
  321. if Assigned(FOnSelectionChange) then FOnSelectionChange(Self);
  322. end;
  323. function TCnHexEditor.DoMouseWheelDown(Shift: TShiftState;
  324. MousePos: TPoint): Boolean;
  325. begin
  326. Result := inherited DoMouseWheelDown(Shift, MousePos);
  327. Perform(WM_VSCROLL, MakeWParam(SB_PAGEDOWN, 0), 0);
  328. end;
  329. function TCnHexEditor.DoMouseWheelUp(Shift: TShiftState;
  330. MousePos: TPoint): Boolean;
  331. begin
  332. Result := inherited DoMouseWheelUp(Shift, MousePos);
  333. Perform(WM_VSCROLL, MakeWParam(SB_PAGEUP, 0), 0);
  334. end;
  335. procedure TCnHexEditor.KeyDown(var Key: Word; Shift: TShiftState);
  336. var
  337. CaretPoint: TPoint;
  338. begin
  339. inherited;
  340. case Key of
  341. VK_BACK:
  342. begin
  343. if not FChangeDataSize then Exit;
  344. if FSelLength <= 0 then
  345. begin
  346. if FSelStart <= 0 then Exit;
  347. Dec(FSelStart);
  348. if CnDeleteStream(FMemoryStream, FSelStart, 1) then
  349. begin
  350. CaretPoint := PositionToCoordinate(FSelStart);
  351. FColIndex := CaretPoint.X;
  352. FRowIndex := CaretPoint.Y;
  353. DoChange;
  354. end;
  355. end
  356. else begin
  357. if CnDeleteStream(FMemoryStream, FSelStart, FSelLength) then
  358. begin
  359. FSelLength := 0;
  360. CaretPoint := PositionToCoordinate(FSelStart + FSelLength);
  361. FColIndex := CaretPoint.X;
  362. FRowIndex := CaretPoint.Y;
  363. DoChange;
  364. end;
  365. end;
  366. end;
  367. VK_DELETE:
  368. begin
  369. if not FChangeDataSize then Exit;
  370. if FSelLength <= 0 then
  371. begin
  372. if CnDeleteStream(FMemoryStream, FSelStart, 1) then DoChange;
  373. end
  374. else begin
  375. if CnDeleteStream(FMemoryStream, FSelStart, FSelLength) then
  376. begin
  377. FSelLength := 0;
  378. CaretPoint := PositionToCoordinate(FSelStart + FSelLength);
  379. FColIndex := CaretPoint.X;
  380. FRowIndex := CaretPoint.Y;
  381. DoChange;
  382. end;
  383. end;
  384. end;
  385. VK_SHIFT:
  386. begin
  387. if FSelLength <= 0 then
  388. begin
  389. FAnchorStart := FSelStart;
  390. FAnchorOffset := 0;
  391. FHexChar := #0;
  392. end;
  393. end;
  394. VK_DOWN:
  395. begin
  396. if ssShift in Shift then
  397. AnchorOffset := AnchorOffset + 16
  398. else
  399. begin
  400. RowIndex := RowIndex + 1;
  401. SelectionChange;
  402. end;
  403. end;
  404. VK_UP:
  405. begin
  406. if ssShift in Shift then
  407. AnchorOffset := AnchorOffset - 16
  408. else
  409. begin
  410. RowIndex := RowIndex - 1;
  411. SelectionChange;
  412. end;
  413. end;
  414. VK_NEXT:
  415. begin
  416. RowIndex := RowIndex + FVisibleLines;
  417. if ssShift in Shift then
  418. else SelectionChange;
  419. end;
  420. VK_PRIOR:
  421. begin
  422. RowIndex := RowIndex - FVisibleLines;
  423. if ssShift in Shift then
  424. else SelectionChange;
  425. end;
  426. VK_HOME:
  427. begin
  428. ColIndex := 0;
  429. if ssCtrl in Shift then RowIndex := 0;
  430. if ssShift in Shift then
  431. else SelectionChange;
  432. end;
  433. VK_END:
  434. begin
  435. ColIndex := 15;
  436. if ssCtrl in Shift then RowIndex := FLineCount - 1;
  437. if ssShift in Shift then
  438. else SelectionChange;
  439. end;
  440. VK_LEFT:
  441. begin
  442. if ssShift in Shift then
  443. AnchorOffset := AnchorOffset - 1
  444. else
  445. begin
  446. if ColIndex > 0 then
  447. ColIndex := ColIndex - 1
  448. else if RowIndex > 0 then
  449. begin
  450. RowIndex := RowIndex - 1;
  451. ColIndex := RowMaxIndex(RowIndex);
  452. end;
  453. SelectionChange;
  454. end;
  455. end;
  456. VK_RIGHT:
  457. begin
  458. if ssShift in Shift then
  459. AnchorOffset := AnchorOffset + 1
  460. else
  461. begin
  462. if ColIndex < 15 then
  463. ColIndex := ColIndex + 1
  464. else if RowIndex < FLineCount - 1 then
  465. begin
  466. ColIndex := 0;
  467. RowIndex := RowIndex + 1;
  468. end;
  469. SelectionChange;
  470. end;
  471. end;
  472. VK_TAB: if ColType = moHex then ColType := moChar else ColType := moHex;
  473. end;
  474. end;
  475. function TCnHexEditor.LineViewText(mLineIndex: Integer): string;
  476. const
  477. SHexDigits : array[0..15] of Char = '0123456789ABCDEF';
  478. var
  479. I, L: Integer;
  480. vBytes: array[0..15] of Byte;
  481. S: string;
  482. begin
  483. Result := '';
  484. if mLineIndex < 0 then Exit;
  485. FMemoryStream.Position := mLineIndex * 16;
  486. L := FMemoryStream.Read(vBytes, 16);
  487. Result := Format('%.8x ', [FBaseAddress + mLineIndex * 16]);
  488. S := '';
  489. for I := 0 to 15 do
  490. begin
  491. if I = 8 then Result := Result + ' ';
  492. if I < L then
  493. begin
  494. if vBytes[I] in [32..126] then
  495. S := S + Chr(vBytes[I])
  496. else S := S + '.';
  497. Result := Result + SHexDigits[vBytes[I] shr $04] +
  498. SHexDigits[vBytes[I] and $0F] + ' '
  499. end else
  500. begin
  501. Result := Result + ' ';
  502. S := S + ' ';
  503. end;
  504. end;
  505. Result := Result + ' ' + S;
  506. end;
  507. procedure TCnHexEditor.LoadFromFile(FileName: TFileName);
  508. begin
  509. if FileExists(FileName) then
  510. FMemoryStream.LoadFromFile(FileName)
  511. else FMemoryStream.Clear;
  512. FSelLength := 0;
  513. FSelStart := 0;
  514. FColIndex := 0;
  515. FRowIndex := 0;
  516. DoChange;
  517. end;
  518. procedure TCnHexEditor.LoadFromStream(Stream: TStream);
  519. begin
  520. FMemoryStream.Clear;
  521. FMemoryStream.LoadFromStream(Stream);
  522. FSelLength := 0;
  523. FSelStart := 0;
  524. FColIndex := 0;
  525. FRowIndex := 0;
  526. DoChange;
  527. end;
  528. procedure TCnHexEditor.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
  529. Y: Integer);
  530. var
  531. vCoordinate: TPoint;
  532. begin
  533. inherited;
  534. if not Focused then SetFocus;
  535. if Button = mbRight then Exit;
  536. case MouseObject(Point(X, Y), vCoordinate) of
  537. moAddress: ;
  538. moHex:
  539. begin
  540. FColIndex := vCoordinate.X;
  541. FColType := moHex;
  542. FRowIndex := vCoordinate.Y;
  543. FSelStart := Max(Min(CoordinatePosition(vCoordinate), FMemoryStream.Size), 0);
  544. vCoordinate := PositionToCoordinate(FSelStart);
  545. FColIndex := vCoordinate.X;
  546. FRowIndex := vCoordinate.Y;
  547. FAnchorStart := FSelStart;
  548. FAnchorOffset := 0;
  549. FHexChar := #0;
  550. SelLength := 0;
  551. UpdateCaret;
  552. SelectionChange;
  553. end;
  554. moChar:
  555. begin
  556. FColIndex := vCoordinate.X;
  557. FColType := moChar;
  558. RowIndex := vCoordinate.Y;
  559. FSelStart := Max(Min(CoordinatePosition(vCoordinate), FMemoryStream.Size), 0);
  560. vCoordinate := PositionToCoordinate(FSelStart);
  561. FColIndex := vCoordinate.X;
  562. FRowIndex := vCoordinate.Y;
  563. FAnchorStart := FSelStart;
  564. FAnchorOffset := 0;
  565. FHexChar := #0;
  566. SelLength := 0;
  567. UpdateCaret;
  568. SelectionChange;
  569. end;
  570. moNone:;
  571. end;
  572. end;
  573. procedure TCnHexEditor.MouseMove(Shift: TShiftState; X, Y: Integer);
  574. var
  575. vCoordinate: TPoint;
  576. vAnchorType: TCnMouseObject;
  577. begin
  578. inherited;
  579. if not Focused then Exit;
  580. { TODO -c2006.11.17 -oZswangY37 : 考虑拖拽移动内容 }
  581. if ssLeft in Shift then
  582. begin
  583. vCoordinate := CoordinateToPoint(FColType, Point(15, 0));
  584. if X >= vCoordinate.X + FItemWidth then
  585. begin
  586. vCoordinate := CoordinateToPoint(FColType, Point(0, 0));
  587. X := vCoordinate.X;
  588. Y := Y + FItemHeight;
  589. end;
  590. vCoordinate := CoordinateToPoint(FColType, Point(0, 0));
  591. X := Max(vCoordinate.X, X);
  592. vCoordinate := CoordinateToPoint(FColType, Point(15, 0));
  593. X := Min(vCoordinate.X, X);
  594. vAnchorType := MouseObject(Point(X, Y), vCoordinate);
  595. if vAnchorType <> FColType then Exit;
  596. AnchorOffset := CoordinatePosition(vCoordinate) - FAnchorStart;
  597. end;
  598. case MouseObject(Point(X, Y), vCoordinate) of
  599. moAddress: Cursor := crDefault;
  600. moHex: Cursor := crIBeam;
  601. moChar: Cursor := crIBeam;
  602. moNone: Cursor := crDefault;
  603. end;
  604. end;
  605. function TCnHexEditor.MouseObject(mPoint: TPoint; var nCoordinate: TPoint): TCnMouseObject;
  606. var
  607. vRow, vCol: Integer;
  608. begin
  609. vRow := (mPoint.Y + FItemHeight * FTopLine) div FItemHeight;
  610. vCol := (mPoint.X + FItemWidth * FLeftLine + FItemWidth div 2) div FItemWidth;
  611. case vCol of
  612. 0..9:
  613. begin
  614. Result := moAddress;
  615. nCoordinate.X := vRow;
  616. nCoordinate.Y := vRow;
  617. end;
  618. 10..58:
  619. begin
  620. Result := moHex;
  621. case vCol of
  622. 10..33: nCoordinate.X := (vCol - 10) div 3;
  623. 34..35: nCoordinate.X := 8;
  624. 36..58:
  625. begin
  626. nCoordinate.X := (vCol - 11) div 3;
  627. end;
  628. else nCoordinate.X := vCol;
  629. end;
  630. nCoordinate.Y := vRow;
  631. end;
  632. 60..76:
  633. begin
  634. Result := moChar;
  635. nCoordinate.X := Min(vCol - 60, 15);
  636. nCoordinate.Y := vRow;
  637. end;
  638. else Result := moNone;
  639. end;
  640. end;
  641. procedure TCnHexEditor.Paint;
  642. var
  643. I: Integer;
  644. vSelStart, vSelEnd: TPoint;
  645. vCurrLine: Integer;
  646. vPoint: TPoint;
  647. Rect: TRect;
  648. vUnColType: TCnMouseObject;
  649. begin
  650. inherited;
  651. Canvas.Brush.Style := bsClear;
  652. Canvas.Font.Assign(Font);
  653. if FSelLength > 0 then
  654. begin
  655. vSelStart := PositionToCoordinate(FSelStart);
  656. vSelEnd := PositionToCoordinate(FSelStart + FSelLength - 1);
  657. end;
  658. for I := 0 to FVisibleLines - 1 do
  659. begin
  660. vCurrLine := I + FTopLine;
  661. if vCurrLine >= FLineCount then Break;
  662. Canvas.TextOut(
  663. -FItemWidth * FLeftLine, I * FItemHeight, LineViewText(vCurrLine));
  664. ///////Begin 绘制选中区域
  665. if (FSelLength > 0) and
  666. (vCurrLine >= vSelStart.Y) and (vCurrLine <= vSelEnd.Y) then
  667. begin
  668. Canvas.Brush.Color := clHighlight;
  669. Canvas.Font.Color := clHighlightText;
  670. if (vCurrLine = vSelStart.Y) and (vCurrLine = vSelEnd.Y) then
  671. begin
  672. vPoint := CoordinateToPoint(FColType, Point(vSelStart.X, vCurrLine));
  673. Canvas.TextOut(
  674. vPoint.X, vPoint.Y, SelectionViewText(FColType, vCurrLine, vSelStart.X, vSelEnd.X));
  675. end else if vCurrLine = vSelStart.Y then
  676. begin
  677. vPoint := CoordinateToPoint(FColType, Point(vSelStart.X, vCurrLine));
  678. Canvas.TextOut(
  679. vPoint.X, vPoint.Y, SelectionViewText(FColType, vCurrLine, vSelStart.X, 15));
  680. end else if vCurrLine = vSelEnd.Y then
  681. begin
  682. vPoint := CoordinateToPoint(FColType, Point(0, vCurrLine));
  683. Canvas.TextOut(
  684. vPoint.X, vPoint.Y, SelectionViewText(FColType, vCurrLine, 0, vSelEnd.X))
  685. end else if (vCurrLine > vSelStart.Y) and (vCurrLine < vSelEnd.Y) then
  686. begin
  687. vPoint := CoordinateToPoint(FColType, Point(0, vCurrLine));
  688. Canvas.TextOut(
  689. vPoint.X, vPoint.Y, SelectionViewText(FColType, vCurrLine, 0, 15))
  690. end;
  691. Canvas.Brush.Style := bsClear;
  692. if FColType = moChar then
  693. vUnColType := moHex
  694. else vUnColType := moChar;
  695. if (vCurrLine = vSelStart.Y) and (vCurrLine = vSelEnd.Y) then
  696. begin
  697. Rect.TopLeft := CoordinateToPoint(vUnColType, Point(vSelStart.X, vCurrLine));
  698. Rect.BottomRight := CoordinateToPoint(vUnColType, Point(vSelEnd.X, vCurrLine));
  699. Rect.BottomRight.X := Rect.BottomRight.X + FItemWidth *(1 + Ord(vUnColType = moHex));
  700. Rect.BottomRight.Y := Rect.BottomRight.Y + FItemHeight;
  701. Canvas.Rectangle(Rect);
  702. end else if vCurrLine = vSelStart.Y then
  703. begin
  704. Rect.TopLeft := CoordinateToPoint(vUnColType, Point(vSelStart.X, vCurrLine));
  705. Rect.BottomRight := CoordinateToPoint(vUnColType, Point(15, vCurrLine));
  706. Rect.BottomRight.X := Rect.BottomRight.X + FItemWidth *(1 + Ord(vUnColType = moHex));
  707. Rect.BottomRight.Y := Rect.BottomRight.Y + FItemHeight;
  708. Canvas.MoveTo(Rect.TopLeft.X, Rect.TopLeft.Y);
  709. Canvas.LineTo(Rect.TopLeft.X, Rect.BottomRight.Y);
  710. Canvas.MoveTo(Rect.BottomRight.X, Rect.TopLeft.Y);
  711. Canvas.LineTo(Rect.BottomRight.X, Rect.BottomRight.Y);
  712. Canvas.MoveTo(Rect.TopLeft.X, Rect.TopLeft.Y);
  713. Canvas.LineTo(Rect.BottomRight.X, Rect.TopLeft.Y);
  714. Rect.BottomRight := CoordinateToPoint(vUnColType, Point(0, vCurrLine));
  715. Rect.BottomRight.Y := Rect.BottomRight.Y + FItemHeight;
  716. Canvas.MoveTo(Rect.TopLeft.X, Rect.BottomRight.Y);
  717. Canvas.LineTo(Rect.BottomRight.X, Rect.BottomRight.Y);
  718. end else if vCurrLine = vSelEnd.Y then
  719. begin
  720. Rect.TopLeft := CoordinateToPoint(vUnColType, Point(0, vCurrLine));
  721. Rect.BottomRight := CoordinateToPoint(vUnColType, Point(vSelEnd.X, vCurrLine));
  722. Rect.BottomRight.X := Rect.BottomRight.X + FItemWidth *(1 + Ord(vUnColType = moHex));
  723. Rect.BottomRight.Y := Rect.BottomRight.Y + FItemHeight;
  724. Canvas.MoveTo(Rect.TopLeft.X, Rect.TopLeft.Y);
  725. Canvas.LineTo(Rect.TopLeft.X, Rect.BottomRight.Y);
  726. Canvas.MoveTo(Rect.BottomRight.X, Rect.TopLeft.Y);
  727. Canvas.LineTo(Rect.BottomRight.X, Rect.BottomRight.Y);
  728. Canvas.MoveTo(Rect.TopLeft.X, Rect.BottomRight.Y);
  729. Canvas.LineTo(Rect.BottomRight.X, Rect.BottomRight.Y);
  730. Rect.TopLeft := CoordinateToPoint(vUnColType, Point(vSelEnd.X, vCurrLine));
  731. Rect.TopLeft.X := Rect.TopLeft.X + FItemWidth *(1 + Ord(vUnColType = moHex));
  732. Rect.BottomRight := CoordinateToPoint(vUnColType, Point(15, vCurrLine));
  733. Rect.BottomRight.X := Rect.BottomRight.X + FItemWidth *(1 + Ord(vUnColType = moHex));
  734. Canvas.MoveTo(Rect.TopLeft.X, Rect.TopLeft.Y);
  735. Canvas.LineTo(Rect.BottomRight.X, Rect.TopLeft.Y);
  736. end else if (vCurrLine > vSelStart.Y) and (vCurrLine < vSelEnd.Y) then
  737. begin
  738. Rect.TopLeft := CoordinateToPoint(vUnColType, Point(0, vCurrLine));
  739. Rect.BottomRight := CoordinateToPoint(vUnColType, Point(15, vCurrLine));
  740. Rect.BottomRight.X := Rect.BottomRight.X + FItemWidth *(1 + Ord(vUnColType = moHex));
  741. Rect.BottomRight.Y := Rect.BottomRight.Y + FItemHeight;
  742. Canvas.MoveTo(Rect.TopLeft.X, Rect.TopLeft.Y);
  743. Canvas.LineTo(Rect.TopLeft.X, Rect.BottomRight.Y);
  744. Canvas.MoveTo(Rect.BottomRight.X, Rect.TopLeft.Y);
  745. Canvas.LineTo(Rect.BottomRight.X, Rect.BottomRight.Y);
  746. end;
  747. Canvas.Font.Assign(Font);
  748. end;
  749. ///////End 绘制选中区域
  750. end;
  751. end;
  752. procedure TCnHexEditor.SaveToFile(FileName: TFileName);
  753. begin
  754. FMemoryStream.SaveToFile(FileName);
  755. end;
  756. procedure TCnHexEditor.SaveToStream(Stream: TStream);
  757. begin
  758. FMemoryStream.SaveToStream(Stream);
  759. end;
  760. function TCnHexEditor.ScrollIntoView: Boolean;
  761. var
  762. vCharIndex: Integer;
  763. begin
  764. Result := False;
  765. if FRowIndex < FTopLine then
  766. begin
  767. Result := True;
  768. TopLine := FRowIndex;
  769. end
  770. else if FRowIndex >= (FTopLine + FVisibleLines) - 1 then
  771. begin
  772. TopLine := FRowIndex - (FVisibleLines - 2);
  773. Result := True;
  774. end;
  775. vCharIndex := ColToChar(FColType, FColIndex);
  776. if vCharIndex < FLeftLine then
  777. begin
  778. Result := True;
  779. LeftLine := vCharIndex;
  780. end
  781. else if vCharIndex >= (FLeftLine + FVisibleChars) - 1 then
  782. begin
  783. Result := True;
  784. LeftLine := vCharIndex - (FVisibleChars - 2);
  785. end;
  786. AdjustScrollBars;
  787. end;
  788. procedure TCnHexEditor.SetBaseAddress(const Value: Integer);
  789. begin
  790. FBaseAddress := Value;
  791. Invalidate;
  792. end;
  793. procedure TCnHexEditor.SetRowIndex(Value: Integer);
  794. var
  795. R: TRect;
  796. begin
  797. if Value <> FRowIndex then
  798. begin
  799. if Value < 0 then Value := 0;
  800. if Value >= FLineCount then Value := FLineCount - 1;
  801. if (FRowIndex >= FTopLine) and (FRowIndex < FTopLine + FVisibleLines - 1) then
  802. begin
  803. R := Bounds(0, 0, 1, FItemHeight);
  804. OffsetRect(R, 0, (FRowIndex - FTopLine) * FItemHeight);
  805. Windows.InvalidateRect(Handle, @R, True);
  806. end;
  807. FRowIndex := Value;
  808. R := Bounds(0, 0, 1, FItemHeight);
  809. OffsetRect(R, 0, (FRowIndex - FTopLine) * FItemHeight);
  810. Windows.InvalidateRect(Handle, @R, True);
  811. if FRowIndex = FLineCount - 1 then
  812. begin
  813. ColIndex := Min(ColIndex, RowMaxIndex(FRowIndex));
  814. ScrollIntoView;
  815. UpdateCaret;
  816. Exit;
  817. end;
  818. ScrollIntoView;
  819. UpdateCaret;
  820. end;
  821. end;
  822. procedure TCnHexEditor.SetLeftLine(Value: Integer);
  823. var
  824. LinesMoved: Integer;
  825. Rect: TRect;
  826. begin
  827. if Value <> FLeftLine then
  828. begin
  829. if Value < 0 then Value := 0;
  830. if Value >= 76 then Value := 76 - 1;
  831. LinesMoved := FLeftLine - Value;
  832. FLeftLine := Value;
  833. SetScrollPos(Handle, SB_HORZ, FLeftLine, True);
  834. if Abs(LinesMoved) = 1 then
  835. begin
  836. Rect := Bounds(1, 0, ClientWidth - FItemWidth, ClientHeight);
  837. if LinesMoved = 1 then OffsetRect(Rect, FItemWidth, 0);
  838. ScrollWindow(Handle, FItemWidth * LinesMoved, 0, @Rect, nil);
  839. if LinesMoved = -1 then
  840. begin
  841. Rect.Left := ClientWidth - FItemWidth;
  842. Rect.Right := ClientWidth;
  843. end
  844. else
  845. begin
  846. Rect.Left := 0;
  847. Rect.Right := FItemWidth;
  848. end;
  849. InvalidateRect(Handle, @Rect, False);
  850. end else Invalidate;
  851. UpdateCaret;
  852. end;
  853. end;
  854. procedure TCnHexEditor.SetTopLine(Value: Integer);
  855. var
  856. LinesMoved: Integer;
  857. Rect: TRect;
  858. begin
  859. if Value <> FTopLine then
  860. begin
  861. if Value < 0 then Value := 0;
  862. if Value >= FLineCount then Value := FLineCount - 1;
  863. LinesMoved := FTopLine - Value;
  864. FTopLine := Value;
  865. SetScrollPos(Handle, SB_VERT, FTopLine, True);
  866. if Abs(LinesMoved) = 1 then
  867. begin
  868. Rect := Bounds(1, 0, ClientWidth, ClientHeight - FItemHeight);
  869. if LinesMoved = 1 then OffsetRect(Rect, 0, FItemHeight);
  870. ScrollWindow(Handle, 0, FItemHeight * LinesMoved, @Rect, nil);
  871. if LinesMoved = -1 then
  872. begin
  873. Rect.Top := ClientHeight - FItemHeight;
  874. Rect.Bottom := ClientHeight;
  875. end
  876. else
  877. begin
  878. Rect.Top := 0;
  879. Rect.Bottom := FItemHeight;
  880. end;
  881. InvalidateRect(Handle, @Rect, False);
  882. end else Invalidate;
  883. UpdateCaret;
  884. end;
  885. end;
  886. procedure TCnHexEditor.UpdateCaret;
  887. var
  888. vPos: TPoint;
  889. begin
  890. DestroyCaret;
  891. if not Focused then Exit;
  892. if FSelLength > 0 then Exit;
  893. CreateCaret(Handle, 0, 2, Canvas.TextHeight('|'));
  894. ShowCaret(Handle);
  895. vPos := CoordinateToPoint(FColType, Point(FColIndex, FRowIndex));
  896. if (FColType = moHex) and (FHexChar <> #0) then
  897. vPos.X := vPos.X + FItemWidth * 2;
  898. SetCaretPos(vPos.X, vPos.Y);
  899. end;
  900. procedure TCnHexEditor.DataChange;
  901. begin
  902. DoChange;
  903. end;
  904. procedure TCnHexEditor.WMGetDlgCode(var Message: TWMGetDlgCode);
  905. begin
  906. Message.Result := DLGC_WANTARROWS or DLGC_WANTTAB;
  907. end;
  908. procedure TCnHexEditor.WMHScroll(var Message: TWMHScroll);
  909. var
  910. NewLeftLine: Integer;
  911. LinesMoved: Integer;
  912. Rect: TRect;
  913. begin
  914. inherited;
  915. if not Focused then SetFocus;
  916. NewLeftLine := FLeftLine;
  917. case Message.ScrollCode of
  918. SB_LINEDOWN: Inc(NewLeftLine);
  919. SB_LINEUP: Dec(NewLeftLine);
  920. SB_PAGEDOWN: Inc(NewLeftLine, FVisibleLines - 1);
  921. SB_PAGEUP: Dec(NewLeftLine, FVisibleLines - 1);
  922. SB_THUMBPOSITION, SB_THUMBTRACK: NewLeftLine := Message.Pos;
  923. end;
  924. if NewLeftLine >= 76 - FVisibleChars + 1 then
  925. NewLeftLine := 76 - FVisibleChars + 1;
  926. if NewLeftLine < 0 then NewLeftLine := 0;
  927. if NewLeftLine <> FLeftLine then
  928. begin
  929. LinesMoved := FLeftLine - NewLeftLine;
  930. FLeftLine := NewLeftLine;
  931. SetScrollPos(Handle, SB_HORZ, FLeftLine, True);
  932. if Abs(LinesMoved) = 1 then
  933. begin
  934. Rect := Bounds(0, 0, ClientWidth - FItemWidth, ClientHeight);
  935. if LinesMoved = 1 then OffsetRect(Rect, FItemWidth, 0);
  936. ScrollWindow(Handle, FItemWidth * LinesMoved, 0, @Rect, nil);
  937. if LinesMoved = -1 then
  938. begin
  939. Rect.Left := ClientWidth;
  940. Rect.Right := ClientWidth - FItemWidth;
  941. end else
  942. begin
  943. Rect.Left := 0;
  944. Rect.Right := FItemWidth;
  945. end;
  946. Windows.InvalidateRect(Handle, @Rect, False);
  947. end else Invalidate;
  948. UpdateCaret;
  949. end;
  950. end;
  951. procedure TCnHexEditor.WMSize(var Message: TWMSize);
  952. begin
  953. inherited;
  954. DoChange;
  955. end;
  956. procedure TCnHexEditor.WMVScroll(var Message: TWMVScroll);
  957. {$J+}
  958. const
  959. vPos: Integer = 0;
  960. vTracking: Boolean = False;
  961. vMouseY: Integer = 0;
  962. {$J-}
  963. var
  964. NewTopLine: Integer;
  965. LinesMoved: Integer;
  966. I: Integer;
  967. vRect: TRect;
  968. begin
  969. inherited;
  970. if not Focused then SetFocus;
  971. NewTopLine := FTopLine;
  972. case Message.ScrollCode of
  973. SB_LINEDOWN: Inc(NewTopLine);
  974. SB_LINEUP: Dec(NewTopLine);
  975. SB_PAGEDOWN: Inc(NewTopLine, FVisibleLines div 2);
  976. SB_PAGEUP: Dec(NewTopLine, FVisibleLines div 2);
  977. SB_THUMBPOSITION: vTracking := False;
  978. SB_THUMBTRACK:
  979. begin
  980. if not vTracking then
  981. begin
  982. vPos := Message.Pos;
  983. vMouseY := Mouse.CursorPos.Y;
  984. end;
  985. vTracking := True;
  986. I := Message.Pos - vPos;
  987. if (I > 0) and (vMouseY > Mouse.CursorPos.Y) then
  988. I := (Message.Pos) - (High(Smallint) * 2 + vPos);
  989. if (I < 0) and (vMouseY < Mouse.CursorPos.Y) then
  990. I := (High(Smallint) * 2 + Message.Pos) - vPos;
  991. NewTopLine := GetScrollPos(Handle, SB_VERT) + I;
  992. vPos := Message.Pos;
  993. vMouseY := Mouse.CursorPos.Y;
  994. end;
  995. end;
  996. if NewTopLine >= FLineCount - FVisibleLines + 1 then
  997. NewTopLine := FLineCount - FVisibleLines + 1;
  998. if NewTopLine < 0 then NewTopLine := 0;
  999. if NewTopLine <> FTopLine then
  1000. begin
  1001. LinesMoved := FTopLine - NewTopLine;
  1002. FTopLine := NewTopLine;
  1003. SetScrollPos(Handle, SB_VERT, FTopLine, True);
  1004. if Abs(LinesMoved) = 1 then
  1005. begin
  1006. vRect := Bounds(0, 0, ClientWidth, ClientHeight - FItemHeight);
  1007. if LinesMoved = 1 then OffsetRect(vRect, 0, FItemHeight);
  1008. ScrollWindow(Handle, 0, FItemHeight * LinesMoved, @vRect, nil);
  1009. if LinesMoved = -1 then
  1010. begin
  1011. vRect.Top := ClientHeight - FItemHeight;
  1012. vRect.Bottom := ClientHeight;
  1013. end else
  1014. begin
  1015. vRect.Top := 0;
  1016. vRect.Bottom := FItemHeight;
  1017. end;
  1018. Windows.InvalidateRect(Handle, @vRect, False);
  1019. end else Invalidate;
  1020. UpdateCaret;
  1021. end;
  1022. end;
  1023. procedure TCnHexEditor.SetColIndex(Value: Integer);
  1024. var
  1025. R: TRect;
  1026. vCharIndex: Integer;
  1027. begin
  1028. if Value <> FColIndex then
  1029. begin
  1030. if Value < 0 then Value := 0;
  1031. if Value > RowMaxIndex(FRowIndex) then Value := RowMaxIndex(FRowIndex);
  1032. FColIndex := Value;
  1033. vCharIndex := ColToChar(FColType, FColIndex);
  1034. if (vCharIndex >= FLeftLine) and (vCharIndex < FLeftLine + 76 - 1) then
  1035. begin
  1036. R := Bounds(0, 0, 1, FItemHeight);
  1037. OffsetRect(R, (vCharIndex - FLeftLine) * FItemWidth, 0);
  1038. Windows.InvalidateRect(Handle, @R, True);
  1039. end;
  1040. FColIndex := Value;
  1041. vCharIndex := ColToChar(FColType, FColIndex);
  1042. R := Bounds(0, 0, 1, FItemHeight);
  1043. OffsetRect(R, (vCharIndex - FLeftLine) * FItemWidth, 0);
  1044. Windows.InvalidateRect(Handle, @R, True);
  1045. ScrollIntoView;
  1046. UpdateCaret;
  1047. end;
  1048. end;
  1049. procedure TCnHexEditor.SetColType(const Value: TCnMouseObject);
  1050. begin
  1051. if FColType = Value then Exit;
  1052. FColType := Value;
  1053. ScrollIntoView;
  1054. UpdateCaret;
  1055. Invalidate;
  1056. end;
  1057. function TCnHexEditor.RowMaxIndex(mRowIndex: Integer): Integer;
  1058. begin
  1059. if mRowIndex < 0 then
  1060. Result := 0
  1061. else if mRowIndex >= FLineCount then
  1062. Result := 0
  1063. else if mRowIndex = FLineCount - 1 then
  1064. Result := FMemoryStream.Size mod 16
  1065. else Result := 15;
  1066. end;
  1067. function TCnHexEditor.ColToChar(mColType: TCnMouseObject;
  1068. mCol: Integer): Integer;
  1069. begin
  1070. Result := 0;
  1071. case mColType of
  1072. moChar: Result := 60 + mCol;
  1073. moHex:
  1074. begin
  1075. case mCol of
  1076. 0..7: Result := 10 + mCol * 3;
  1077. 8..15: Result := 11 + mCol * 3;
  1078. end;
  1079. end;
  1080. end;
  1081. end;
  1082. procedure TCnHexEditor.SetReadOnly(const Value: Boolean);
  1083. begin
  1084. if FReadOnly = Value then Exit;
  1085. FReadOnly := Value;
  1086. if FReadOnly then Cursor := crDefault;
  1087. end;
  1088. procedure TCnHexEditor.SetSelLength(const Value: Integer);
  1089. var
  1090. vCaretPoint: TPoint;
  1091. begin
  1092. if FSelLength = Value then Exit;
  1093. FSelLength := Max(Min(Value, FMemoryStream.Size - FSelStart), 0);
  1094. if Assigned(FOnSelectionChange) then FOnSelectionChange(Self);
  1095. vCaretPoint := PositionToCoordinate(FSelStart + FSelLength);
  1096. FColIndex := vCaretPoint.X;
  1097. FRowIndex := vCaretPoint.Y;
  1098. Invalidate;
  1099. end;
  1100. procedure TCnHexEditor.SetSelStart(Value: Integer);
  1101. var
  1102. vCaretPoint: TPoint;
  1103. begin
  1104. if FSelStart = Value then Exit;
  1105. FSelStart := Max(Min(Value, FMemoryStream.Size), 0);
  1106. FSelLength := Max(Min(FSelLength, FMemoryStream.Size - FSelStart), 0);
  1107. if Assigned(FOnSelectionChange) then FOnSelectionChange(Self);
  1108. vCaretPoint := PositionToCoordinate(FSelStart + FSelLength);
  1109. FColIndex := vCaretPoint.X;
  1110. FRowIndex := vCaretPoint.Y;
  1111. Invalidate;
  1112. end;
  1113. procedure TCnHexEditor.SelectionChange;
  1114. var
  1115. vSelLength: Integer;
  1116. begin
  1117. vSelLength := FSelLength;
  1118. FSelStart := Max(Min(FRowIndex * 16 + FColIndex, FMemoryStream.Size), 0);
  1119. FSelLength := 0;
  1120. FHexChar := #0;
  1121. if vSelLength > 0 then Invalidate;
  1122. UpdateCaret;
  1123. if Assigned(FOnSelectionChange) then FOnSelectionChange(Self);
  1124. end;
  1125. function TCnHexEditor.PositionToCoordinate(mPosition: Integer): TPoint;
  1126. begin
  1127. Result := Point(-1, -1);
  1128. if mPosition < 0 then Exit;
  1129. if mPosition > FMemoryStream.Size then Exit;
  1130. Result.X := mPosition mod 16;
  1131. Result.Y := mPosition div 16;
  1132. end;
  1133. function TCnHexEditor.SelectionViewText(mColType: TCnMouseObject;
  1134. mLineIndex: Integer; mStart, mEnd: Integer): string;
  1135. const
  1136. cHexDigits : array[0..15] of Char = '0123456789ABCDEF';
  1137. var
  1138. I, L: Integer;
  1139. vBytes: array[0..15] of Byte;
  1140. S: string;
  1141. begin
  1142. Result := '';
  1143. if mLineIndex < 0 then Exit;
  1144. FMemoryStream.Position := mLineIndex * 16;
  1145. L := FMemoryStream.Read(vBytes, 16);
  1146. S := '';
  1147. for I := Max(0, mStart) to Min(15, mEnd) do
  1148. begin
  1149. case mColType of
  1150. moHex: if I = 8 then Result := Result + ' ';
  1151. moChar: ;
  1152. end;
  1153. if I < L then
  1154. begin
  1155. case mColType of
  1156. moHex:
  1157. Result := Result + cHexDigits[vBytes[I] shr $04] +
  1158. cHexDigits[vBytes[I] and $0F] + ' ';
  1159. moChar:
  1160. if vBytes[I] in [32..126] then
  1161. Result := Result + Chr(vBytes[I])
  1162. else Result := Result + '.';
  1163. end;
  1164. end;
  1165. end;
  1166. if mColType = moHex then Result := Trim(Result);
  1167. end;
  1168. procedure TCnHexEditor.SetAnchorOffset(Value: Integer);
  1169. var
  1170. vCaretPoint: TPoint;
  1171. begin
  1172. if FAnchorStart = Value then Exit;
  1173. if FAnchorStart + Value < 0 then Exit;
  1174. if FAnchorStart + Value > FMemoryStream.Size then Exit;
  1175. FAnchorOffset := Value;
  1176. FSelLength := Abs(FAnchorOffset);
  1177. if FAnchorOffset < 0 then
  1178. begin
  1179. FSelStart := FAnchorStart + FAnchorOffset;
  1180. vCaretPoint := PositionToCoordinate(FSelStart);
  1181. end else
  1182. begin
  1183. FSelStart := FAnchorStart;
  1184. vCaretPoint := PositionToCoordinate(FSelStart + FSelLength);
  1185. end;
  1186. FColIndex := vCaretPoint.X;
  1187. FRowIndex := vCaretPoint.Y;
  1188. ScrollIntoView;
  1189. UpdateCaret;
  1190. Invalidate;
  1191. if Assigned(FOnSelectionChange) then FOnSelectionChange(Self);
  1192. end;
  1193. procedure TCnHexEditor.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
  1194. Y: Integer);
  1195. var
  1196. Coordinate: TPoint;
  1197. begin
  1198. inherited;
  1199. case MouseObject(Point(X, Y), Coordinate) of
  1200. moAddress: Cursor := crDefault;
  1201. moHex: Cursor := crIBeam;
  1202. moChar: Cursor := crIBeam;
  1203. moNone: Cursor := crDefault;
  1204. end;
  1205. end;
  1206. function TCnHexEditor.CoordinatePosition(mCoordinate: TPoint): Integer;
  1207. begin
  1208. Result :=
  1209. Max(Min(mCoordinate.Y * 16 + mCoordinate.X, FMemoryStream.Size), 0);
  1210. end;
  1211. procedure TCnHexEditor.WMCHAR(var Msg: TWMChar);
  1212. var
  1213. vChar: Char;
  1214. vCoordinate: TPoint;
  1215. vRect: TRect;
  1216. vSelStart: Integer;
  1217. begin
  1218. inherited;
  1219. if FReadOnly then Exit;
  1220. if not FChangeDataSize and (FSelStart >= FMemoryStream.Size) then Exit;
  1221. case Msg.CharCode of
  1222. 0..27, 128..255: Exit;
  1223. end;
  1224. FMemoryStream.Position := FSelStart;
  1225. vSelStart := FSelStart;
  1226. if FColType = moHex then
  1227. begin
  1228. case Msg.CharCode of
  1229. Ord('0')..Ord('9'): ;
  1230. Ord('A')..Ord('F'): ;
  1231. Ord('a')..Ord('f'): ;
  1232. else Exit;
  1233. end;
  1234. if FHexChar = #0 then
  1235. begin
  1236. FHexChar := Char(Msg.CharCode);
  1237. vChar := Char(StrToIntDef('$' + FHexChar, 0));
  1238. end else
  1239. begin
  1240. vChar := Char(StrToIntDef('$' + FHexChar + Char(Msg.CharCode), 0));
  1241. FSelStart := FSelStart + 1;
  1242. FHexChar := #0;
  1243. end;
  1244. end else if FColType = moChar then
  1245. begin
  1246. vChar := Char(Msg.CharCode);
  1247. FSelStart := FSelStart + 1;
  1248. end;
  1249. FMemoryStream.Position := vSelStart;
  1250. FMemoryStream.Write(vChar, SizeOf(vChar));
  1251. vCoordinate := PositionToCoordinate(FSelStart);
  1252. FRowIndex := vCoordinate.Y;
  1253. FColIndex := vCoordinate.X;
  1254. if FSelStart = FMemoryStream.Size then
  1255. DoChange;
  1256. if FSelLength > 0 then
  1257. begin
  1258. FSelLength := 0;
  1259. Invalidate;
  1260. end else
  1261. begin
  1262. vCoordinate := PositionToCoordinate(vSelStart);
  1263. vRect.TopLeft := CoordinateToPoint(moChar, vCoordinate);
  1264. vRect.BottomRight.X := vRect.TopLeft.X + FItemWidth;
  1265. vRect.BottomRight.Y := vRect.TopLeft.Y + FItemHeight;
  1266. Windows.InvalidateRect(Handle, @vRect, True);
  1267. vRect.TopLeft := CoordinateToPoint(moHex, vCoordinate);
  1268. vRect.BottomRight.X := vRect.TopLeft.X + FItemWidth * 3;
  1269. vRect.BottomRight.Y := vRect.TopLeft.Y + FItemHeight;
  1270. Windows.InvalidateRect(Handle, @vRect, True);
  1271. end;
  1272. UpdateCaret;
  1273. end;
  1274. procedure TCnHexEditor.WMIMECHAR(var Msg: TCnWMImeChar);
  1275. var
  1276. vCoordinate: TPoint;
  1277. vRect: TRect;
  1278. begin
  1279. inherited;
  1280. if FReadOnly then Exit;
  1281. FMemoryStream.Position := FSelStart;
  1282. if FColType = moChar then
  1283. begin
  1284. { TODO -c2006.11.17 -oZswangY37 : 考虑采用插入模式输入 }
  1285. FMemoryStream.Write(Msg.CharCode, 2);
  1286. FSelStart := FSelStart + 2;
  1287. vCoordinate := PositionToCoordinate(FSelStart);
  1288. FRowIndex := vCoordinate.Y;
  1289. FColIndex := vCoordinate.X;
  1290. if FSelStart = FMemoryStream.Size then
  1291. DoChange;
  1292. if FSelLength > 0 then
  1293. begin
  1294. FSelLength := 0;
  1295. Invalidate;
  1296. end else
  1297. begin
  1298. vCoordinate := PositionToCoordinate(FSelStart - 2);
  1299. vRect.TopLeft := CoordinateToPoint(moChar, vCoordinate);
  1300. vRect.BottomRight.X := vRect.TopLeft.X + FItemWidth * 2;
  1301. vRect.BottomRight.Y := vRect.TopLeft.Y + FItemHeight;
  1302. Windows.InvalidateRect(Handle, @vRect, True);
  1303. vRect.TopLeft := CoordinateToPoint(moHex, vCoordinate);
  1304. vRect.BottomRight.X := vRect.TopLeft.X + FItemWidth * 4 * 2;
  1305. vRect.BottomRight.Y := vRect.TopLeft.Y + FItemHeight;
  1306. Windows.InvalidateRect(Handle, @vRect, True);
  1307. end;
  1308. UpdateCaret;
  1309. end;
  1310. end;
  1311. function TCnHexEditor.GetSelText: string;
  1312. begin
  1313. Result := '';
  1314. if FSelLength <= 0 then Exit;
  1315. SetLength(Result, FSelLength);
  1316. FMemoryStream.Position := FSelStart;
  1317. FMemoryStream.Read(Result[1], FSelLength);
  1318. end;
  1319. procedure TCnHexEditor.SetSelText(const Value: string);
  1320. var
  1321. vCaretPoint: TPoint;
  1322. L: Integer;
  1323. begin
  1324. L := Length(Value);
  1325. if (L <= 0) and (FSelLength <= 0) then Exit;
  1326. if FSelLength > 0 then
  1327. CnDeleteStream(FMemoryStream, FSelStart, FSelLength);
  1328. if L > 0 then
  1329. CnInsertStream(FMemoryStream, FSelStart, Value[1], L);
  1330. FSelLength := 0;
  1331. FSelStart := FSelStart + L;
  1332. vCaretPoint := PositionToCoordinate(FSelStart + FSelLength);
  1333. FColIndex := vCaretPoint.X;
  1334. FRowIndex := vCaretPoint.Y;
  1335. DoChange;
  1336. end;
  1337. procedure TCnHexEditor.DoEnter;
  1338. begin
  1339. inherited;
  1340. UpdateCaret;
  1341. end;
  1342. procedure TCnHexEditor.DoExit;
  1343. begin
  1344. inherited;
  1345. UpdateCaret;
  1346. end;
  1347. procedure TCnHexEditor.LoadFromBuffer(const Buffer; Size: Integer);
  1348. begin
  1349. FMemoryStream.Clear;
  1350. FMemoryStream.Write(Buffer, Size);
  1351. FSelLength := 0;
  1352. FSelStart := 0;
  1353. FColIndex := 0;
  1354. FRowIndex := 0;
  1355. DoChange;
  1356. end;
  1357. procedure TCnHexEditor.SaveToBuffer(var Buffer; Size: Integer);
  1358. begin
  1359. FMemoryStream.Position := 0;
  1360. FMemoryStream.Read(Buffer, Size);
  1361. end;
  1362. end.