/VirtualTrees.pas

http://pyscripter.googlecode.com/ · Pascal · 38678 lines · 28742 code · 5604 blank · 4332 comment · 3337 complexity · 7598510213ddb13ae478fb0531c4edf8 MD5 · raw file

  1. unit VirtualTrees;
  2. // The contents of this file are subject to the Mozilla Public License
  3. // Version 1.1 (the "License"); you may not use this file except in compliance
  4. // with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
  5. //
  6. // Alternatively, you may redistribute this library, use and/or modify it under the terms of the
  7. // GNU Lesser General Public License as published by the Free Software Foundation;
  8. // either version 2.1 of the License, or (at your option) any later version.
  9. // You may obtain a copy of the LGPL at http://www.gnu.org/copyleft/.
  10. //
  11. // Software distributed under the License is distributed on an "AS IS" basis,
  12. // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
  13. // specific language governing rights and limitations under the License.
  14. //
  15. // The original code is VirtualTrees.pas, released September 30, 2000.
  16. //
  17. // The initial developer of the original code is digital publishing AG (Munich, Germany, www.digitalpublishing.de),
  18. // written by Mike Lischke (public@soft-gems.net, www.soft-gems.net).
  19. //
  20. // Portions created by digital publishing AG are Copyright
  21. // (C) 1999-2001 digital publishing AG. All Rights Reserved.
  22. //----------------------------------------------------------------------------------------------------------------------
  23. //
  24. // For a list of recent changes please see file CHANGES.TXT
  25. //
  26. // Credits for their valuable assistance and code donations go to:
  27. // Freddy Ertl, Marian Aldenhövel, Thomas Bogenrieder, Jim Kuenemann, Werner Lehmann, Jens Treichler,
  28. // Paul Gallagher (IBO tree), Ondrej Kelle, Ronaldo Melo Ferraz, Heri Bender, Roland Bedürftig (BCB)
  29. // Anthony Mills, Alexander Egorushkin (BCB), Mathias Torell (BCB), Frank van den Bergh, Vadim Sedulin, Peter Evans,
  30. // Milan Vandrovec (BCB), Steve Moss, Joe White, David Clark, Anders Thomsen, Igor Afanasyev, Eugene Programmer,
  31. // Corbin Dunn, Richard Pringle, Uli Gerhardt, Azza, Igor Savkic, Daniel Bauten, Timo Tegtmeier, Dmitry Zegebart,
  32. // Andreas Hausladen, Joachim Marder
  33. // Beta testers:
  34. // Freddy Ertl, Hans-Jürgen Schnorrenberg, Werner Lehmann, Jim Kueneman, Vadim Sedulin, Moritz Franckenstein,
  35. // Wim van der Vegt, Franc v/d Westelaken
  36. // Indirect contribution (via publicly accessible work of those persons):
  37. // Alex Denissov, Hiroyuki Hori (MMXAsm expert)
  38. // Documentation:
  39. // Markus Spoettl and toolsfactory GbR (http://www.doc-o-matic.com/, sponsoring Soft Gems development
  40. // with a free copy of the Doc-O-Matic help authoring system), Sven H. (Step by step tutorial)
  41. // CLX:
  42. // Dmitri Dmitrienko (initial developer)
  43. // Source repository:
  44. // https://code.google.com/p/virtual-treeview/source/
  45. // Accessability implementation:
  46. // Marco Zehe (with help from Sebastian Modersohn)
  47. //----------------------------------------------------------------------------------------------------------------------
  48. interface
  49. {$booleval off} // Use fastest possible boolean evaluation
  50. {.$define TntSupport} // Added by Igor Afanasyev to support unicode-aware inplace editors. This implementation uses
  51. // Troy Wolbrink's TNT controls, meanwhile available as TMS Unicode components
  52. // For some things to work we need code, which is classified as being unsafe for .NET.
  53. {$WARN UNSAFE_TYPE OFF}
  54. {$WARN UNSAFE_CAST OFF}
  55. {$WARN UNSAFE_CODE OFF}
  56. {$IF CompilerVersion >= 24}
  57. {$LEGACYIFEND ON}
  58. {$IFEND}
  59. {$if CompilerVersion >= 20}
  60. {$WARN IMPLICIT_STRING_CAST OFF}
  61. {$WARN IMPLICIT_STRING_CAST_LOSS OFF}
  62. {$ifend}
  63. {$HPPEMIT '#include <objidl.h>'}
  64. {$HPPEMIT '#include <oleidl.h>'}
  65. {$HPPEMIT '#include <oleacc.h>'}
  66. {$HPPEMIT '#include <ShlObj.hpp>'}
  67. {$HPPEMIT '#pragma link "VirtualTreesR.lib"'}
  68. uses
  69. Windows,
  70. {$if CompilerVersion >= 18}
  71. oleacc, // MSAA support in Delphi 2006 or higher
  72. {$ifend}
  73. Messages, SysUtils, Graphics, Controls, Forms, ImgList, ActiveX, StdCtrls, Classes, Menus, Printers, Types,
  74. CommCtrl, // image lists, common controls tree structures
  75. Themes, UxTheme, ShlObj
  76. {$ifdef TntSupport}
  77. , TntStdCtrls // Unicode aware inplace editor.
  78. {$endif TntSupport}
  79. {$IF CompilerVersion >= 24}
  80. ,UITypes
  81. {$IFEND}
  82. ;
  83. const
  84. VTVersion = '5.5.3';
  85. {$if CompilerVersion < 20}
  86. type
  87. UnicodeString = WideString;
  88. RawByteString = AnsiString;
  89. PByte = PAnsiChar;
  90. {$ifend}
  91. {$if CompilerVersion < 18}
  92. //MSAA interfaces not included in Delphi 7
  93. {$WARN BOUNDS_ERROR OFF}
  94. IAccessible = interface(IDispatch)
  95. ['{618736E0-3C3D-11CF-810C-00AA00389B71}']
  96. function Get_accParent(out ppdispParent: IDispatch): HResult; stdcall;
  97. function Get_accChildCount(out pcountChildren: Integer): HResult; stdcall;
  98. function Get_accChild(varChild: OleVariant; out ppdispChild: IDispatch): HResult; stdcall;
  99. function Get_accName(varChild: OleVariant; out pszName: WideString): HResult; stdcall;
  100. function Get_accValue(varChild: OleVariant; out pszValue: WideString): HResult; stdcall;
  101. function Get_accDescription(varChild: OleVariant; out pszDescription: WideString): HResult; stdcall;
  102. function Get_accRole(varChild: OleVariant; out pvarRole: OleVariant): HResult; stdcall;
  103. function Get_accState(varChild: OleVariant; out pvarState: OleVariant): HResult; stdcall;
  104. function Get_accHelp(varChild: OleVariant; out pszHelp: WideString): HResult; stdcall;
  105. function Get_accHelpTopic(out pszHelpFile: WideString; varChild: OleVariant;
  106. out pidTopic: Integer): HResult; stdcall;
  107. function Get_accKeyboardShortcut(varChild: OleVariant; out pszKeyboardShortcut: WideString): HResult; stdcall;
  108. function Get_accFocus(out pvarChild: OleVariant): HResult; stdcall;
  109. function Get_accSelection(out pvarChildren: OleVariant): HResult; stdcall;
  110. function Get_accDefaultAction(varChild: OleVariant; out pszDefaultAction: WideString): HResult; stdcall;
  111. function accSelect(flagsSelect: Integer; varChild: OleVariant): HResult; stdcall;
  112. function accLocation(out pxLeft: Integer; out pyTop: Integer; out pcxWidth: Integer;
  113. out pcyHeight: Integer; varChild: OleVariant): HResult; stdcall;
  114. function accNavigate(navDir: Integer; varStart: OleVariant; out pvarEndUpAt: OleVariant): HResult; stdcall;
  115. function accHitTest(xLeft: Integer; yTop: Integer; out pvarChild: OleVariant): HResult; stdcall;
  116. function accDoDefaultAction(varChild: OleVariant): HResult; stdcall;
  117. function Set_accName(varChild: OleVariant; const pszName: WideString): HResult; stdcall;
  118. function Set_accValue(varChild: OleVariant; const pszValue: WideString): HResult; stdcall;
  119. end;
  120. {$ifend}
  121. const
  122. VTTreeStreamVersion = 2;
  123. VTHeaderStreamVersion = 6; // The header needs an own stream version to indicate changes only relevant to the header.
  124. CacheThreshold = 2000; // Number of nodes a tree must at least have to start caching and at the same
  125. // time the maximum number of nodes between two cache entries.
  126. FadeAnimationStepCount = 255; // Number of animation steps for hint fading (0..255).
  127. ShadowSize = 5; // Size in pixels of the hint shadow. This value has no influence on Win2K and XP systems
  128. // as those OSes have native shadow support.
  129. // Special identifiers for columns.
  130. NoColumn = -1;
  131. InvalidColumn = -2;
  132. // Indices for check state images used for checking.
  133. ckEmpty = 0; // an empty image used as place holder
  134. // radio buttons
  135. ckRadioUncheckedNormal = 1;
  136. ckRadioUncheckedHot = 2;
  137. ckRadioUncheckedPressed = 3;
  138. ckRadioUncheckedDisabled = 4;
  139. ckRadioCheckedNormal = 5;
  140. ckRadioCheckedHot = 6;
  141. ckRadioCheckedPressed = 7;
  142. ckRadioCheckedDisabled = 8;
  143. // check boxes
  144. ckCheckUncheckedNormal = 9;
  145. ckCheckUncheckedHot = 10;
  146. ckCheckUncheckedPressed = 11;
  147. ckCheckUncheckedDisabled = 12;
  148. ckCheckCheckedNormal = 13;
  149. ckCheckCheckedHot = 14;
  150. ckCheckCheckedPressed = 15;
  151. ckCheckCheckedDisabled = 16;
  152. ckCheckMixedNormal = 17;
  153. ckCheckMixedHot = 18;
  154. ckCheckMixedPressed = 19;
  155. ckCheckMixedDisabled = 20;
  156. // simple button
  157. ckButtonNormal = 21;
  158. ckButtonHot = 22;
  159. ckButtonPressed = 23;
  160. ckButtonDisabled = 24;
  161. // Instead using a TTimer class for each of the various events I use Windows timers with messages
  162. // as this is more economical.
  163. ExpandTimer = 1;
  164. EditTimer = 2;
  165. HeaderTimer = 3;
  166. ScrollTimer = 4;
  167. ChangeTimer = 5;
  168. StructureChangeTimer = 6;
  169. SearchTimer = 7;
  170. ThemeChangedTimer = 8;
  171. ThemeChangedTimerDelay = 500;
  172. // Need to use this message to release the edit link interface asynchronously.
  173. WM_CHANGESTATE = WM_APP + 32;
  174. // Virtual Treeview does not need to be subclassed by an eventual Theme Manager instance as it handles
  175. // Windows XP theme painting itself. Hence the special message is used to prevent subclassing.
  176. CM_DENYSUBCLASSING = CM_BASE + 2000;
  177. // Decoupling message for auto-adjusting the internal edit window.
  178. CM_AUTOADJUST = CM_BASE + 2005;
  179. CM_UPDATE_VCLSTYLE_SCROLLBARS = CM_BASE + 2050;
  180. // VT's own clipboard formats,
  181. // Note: The reference format is used internally to allow to link to a tree reference
  182. // to implement optimized moves and other back references.
  183. CFSTR_VIRTUALTREE = 'Virtual Tree Data';
  184. CFSTR_VTREFERENCE = 'Virtual Tree Reference';
  185. CFSTR_HTML = 'HTML Format';
  186. CFSTR_RTF = 'Rich Text Format';
  187. CFSTR_RTFNOOBJS = 'Rich Text Format Without Objects';
  188. CFSTR_CSV = 'CSV';
  189. // Drag image helpers for Windows 2000 and up.
  190. IID_IDropTargetHelper: TGUID = (D1: $4657278B; D2: $411B; D3: $11D2; D4: ($83, $9A, $00, $C0, $4F, $D9, $18, $D0));
  191. IID_IDragSourceHelper: TGUID = (D1: $DE5BF786; D2: $477A; D3: $11D2; D4: ($83, $9D, $00, $C0, $4F, $D9, $18, $D0));
  192. IID_IDropTarget: TGUID = (D1: $00000122; D2: $0000; D3: $0000; D4: ($C0, $00, $00, $00, $00, $00, $00, $46));
  193. {$if CompilerVersion < 21}
  194. CLSID_DragDropHelper: TGUID = (D1: $4657278A; D2: $411B; D3: $11D2; D4: ($83, $9A, $00, $C0, $4F, $D9, $18, $D0));
  195. DSH_ALLOWDROPDESCRIPTIONTEXT = $1;
  196. SID_IDropTargetHelper = '{4657278B-411B-11D2-839A-00C04FD918D0}';
  197. SID_IDragSourceHelper = '{DE5BF786-477A-11D2-839D-00C04FD918D0}';
  198. SID_IDragSourceHelper2 = '{83E07D0D-0C5F-4163-BF1A-60B274051E40}';
  199. SID_IDropTarget = '{00000122-0000-0000-C000-000000000046}';
  200. {$ifend}
  201. // Help identifiers for exceptions. Application developers are responsible to link them with actual help topics.
  202. hcTFEditLinkIsNil = 2000;
  203. hcTFWrongMoveError = 2001;
  204. hcTFWrongStreamFormat = 2002;
  205. hcTFWrongStreamVersion = 2003;
  206. hcTFStreamTooSmall = 2004;
  207. hcTFCorruptStream1 = 2005;
  208. hcTFCorruptStream2 = 2006;
  209. hcTFClipboardFailed = 2007;
  210. hcTFCannotSetUserData = 2008;
  211. // Header standard split cursor.
  212. crHeaderSplit = TCursor(63);
  213. // Height changing cursor.
  214. crVertSplit = TCursor(62);
  215. UtilityImageSize = 16; // Needed by descendants for hittests.
  216. var // Clipboard format IDs used in OLE drag'n drop and clipboard transfers.
  217. CF_VIRTUALTREE,
  218. CF_VTREFERENCE,
  219. CF_VRTF,
  220. CF_VRTFNOOBJS, // Unfortunately CF_RTF* is already defined as being
  221. // registration strings so I have to use different identifiers.
  222. CF_HTML,
  223. CF_CSV: Word;
  224. MMXAvailable: Boolean; // necessary to know because the blend code uses MMX instructions
  225. IsWinVistaOrAbove: Boolean;
  226. {$MinEnumSize 1, make enumerations as small as possible}
  227. type
  228. // The exception used by the trees.
  229. EVirtualTreeError = class(Exception);
  230. PCardinal = ^Cardinal;
  231. // Limits the speed interval which can be used for auto scrolling (milliseconds).
  232. TAutoScrollInterval = 1..1000;
  233. // Need to declare the correct WMNCPaint record as the VCL (D5-) doesn't.
  234. {$if CompilerVersion >= 23}
  235. TRealWMNCPaint = TWMNCPaint;
  236. {$else}
  237. TRealWMNCPaint = packed record
  238. Msg: UINT;
  239. Rgn: HRGN;
  240. lParam: LPARAM;
  241. Result: LRESULT;
  242. end;
  243. // The next two message records are not declared in Delphi 6 and lower.
  244. TWMPrint = packed record
  245. Msg: UINT;
  246. DC: HDC;
  247. Flags: LPARAM;
  248. Result: LRESULT;
  249. end;
  250. TWMPrintClient = TWMPrint;
  251. {$ifend}
  252. // Be careful when adding new states as this might change the size of the type which in turn
  253. // changes the alignment in the node record as well as the stream chunks.
  254. // Do not reorder the states and always add new states at the end of this enumeration in order to avoid
  255. // breaking existing code.
  256. TVirtualNodeState = (
  257. vsInitialized, // Set after the node has been initialized.
  258. vsChecking, // Node's check state is changing, avoid propagation.
  259. vsCutOrCopy, // Node is selected as cut or copy and paste source.
  260. vsDisabled, // Set if node is disabled.
  261. vsDeleting, // Set when the node is about to be freed.
  262. vsExpanded, // Set if the node is expanded.
  263. vsHasChildren, // Indicates the presence of child nodes without actually setting them.
  264. vsVisible, // Indicate whether the node is visible or not (independant of the expand states of its parents).
  265. vsSelected, // Set if the node is in the current selection.
  266. vsOnFreeNodeCallRequired, // Set if user data has been set which requires OnFreeNode.
  267. vsAllChildrenHidden, // Set if vsHasChildren is set and no child node has the vsVisible flag set.
  268. vsClearing, // A node's children are being deleted. Don't register structure change event.
  269. vsMultiline, // Node text is wrapped at the cell boundaries instead of being shorted.
  270. vsHeightMeasured, // Node height has been determined and does not need a recalculation.
  271. vsToggling, // Set when a node is expanded/collapsed to prevent recursive calls.
  272. vsFiltered // Indicates that the node should not be painted (without effecting its children).
  273. );
  274. TVirtualNodeStates = set of TVirtualNodeState;
  275. // States used in InitNode to indicate states a node shall initially have.
  276. TVirtualNodeInitState = (
  277. ivsDisabled,
  278. ivsExpanded,
  279. ivsHasChildren,
  280. ivsMultiline,
  281. ivsSelected,
  282. ivsFiltered,
  283. ivsReInit
  284. );
  285. TVirtualNodeInitStates = set of TVirtualNodeInitState;
  286. TScrollBarStyle = (
  287. sbmRegular,
  288. sbm3D
  289. );
  290. // Options per column.
  291. TVTColumnOption = (
  292. coAllowClick, // Column can be clicked (must be enabled too).
  293. coDraggable, // Column can be dragged.
  294. coEnabled, // Column is enabled.
  295. coParentBidiMode, // Column uses the parent's bidi mode.
  296. coParentColor, // Column uses the parent's background color.
  297. coResizable, // Column can be resized.
  298. coShowDropMark, // Column shows the drop mark if it is currently the drop target.
  299. coVisible, // Column is shown.
  300. coAutoSpring, // Column takes part in the auto spring feature of the header (must be resizable too).
  301. coFixed, // Column is fixed and can not be selected or scrolled etc.
  302. coSmartResize, // Column is resized to its largest entry which is in view (instead of its largest
  303. // visible entry).
  304. coAllowFocus, // Column can be focused.
  305. coDisableAnimatedResize, // Column resizing is not animated.
  306. coWrapCaption, // Caption could be wrapped across several header lines to fit columns width.
  307. coUseCaptionAlignment, // Column's caption has its own aligment.
  308. coEditable // Column can be edited
  309. );
  310. TVTColumnOptions = set of TVTColumnOption;
  311. // These flags are used to indicate where a click in the header happened.
  312. TVTHeaderHitPosition = (
  313. hhiNoWhere, // No column is involved (possible only if the tree is smaller than the client area).
  314. hhiOnColumn, // On a column.
  315. hhiOnIcon, // On the bitmap associated with a column.
  316. hhiOnCheckbox // On the checkbox if enabled.
  317. );
  318. TVTHeaderHitPositions = set of TVTHeaderHitPosition;
  319. // These flags are returned by the hit test method.
  320. THitPosition = (
  321. hiAbove, // above the client area (if relative) or the absolute tree area
  322. hiBelow, // below the client area (if relative) or the absolute tree area
  323. hiNowhere, // no node is involved (possible only if the tree is not as tall as the client area)
  324. hiOnItem, // on the bitmaps/buttons or label associated with an item
  325. hiOnItemButton, // on the button associated with an item
  326. hiOnItemButtonExact, // exactly on the button associated with an item
  327. hiOnItemCheckbox, // on the checkbox if enabled
  328. hiOnItemIndent, // in the indentation area in front of a node
  329. hiOnItemLabel, // on the normal text area associated with an item
  330. hiOnItemLeft, // in the area to the left of a node's text area (e.g. when right aligned or centered)
  331. hiOnItemRight, // in the area to the right of a node's text area (e.g. if left aligned or centered)
  332. hiOnNormalIcon, // on the "normal" image
  333. hiOnStateIcon, // on the state image
  334. hiToLeft, // to the left of the client area (if relative) or the absolute tree area
  335. hiToRight, // to the right of the client area (if relative) or the absolute tree area
  336. hiUpperSplitter, // in the upper splitter area of a node
  337. hiLowerSplitter // in the lower splitter area of a node
  338. );
  339. THitPositions = set of THitPosition;
  340. TCheckType = (
  341. ctNone,
  342. ctTriStateCheckBox,
  343. ctCheckBox,
  344. ctRadioButton,
  345. ctButton
  346. );
  347. // The check states include both, transient and fluent (temporary) states. The only temporary state defined so
  348. // far is the pressed state.
  349. TCheckState = (
  350. csUncheckedNormal, // unchecked and not pressed
  351. csUncheckedPressed, // unchecked and pressed
  352. csCheckedNormal, // checked and not pressed
  353. csCheckedPressed, // checked and pressed
  354. csMixedNormal, // 3-state check box and not pressed
  355. csMixedPressed // 3-state check box and pressed
  356. );
  357. TCheckImageKind = (
  358. ckLightCheck, // gray cross
  359. ckDarkCheck, // black cross
  360. ckLightTick, // gray tick mark
  361. ckDarkTick, // black tick mark
  362. ckFlat, // flat images (no 3D border)
  363. ckXP, // Windows XP style
  364. ckCustom, // application defined check images
  365. ckSystemFlat, // Flat system defined check images.
  366. ckSystemDefault // Uses the system check images, theme aware.
  367. );
  368. // mode to describe a move action
  369. TVTNodeAttachMode = (
  370. amNoWhere, // just for simplified tests, means to ignore the Add/Insert command
  371. amInsertBefore, // insert node just before destination (as sibling of destination)
  372. amInsertAfter, // insert node just after destionation (as sibling of destination)
  373. amAddChildFirst, // add node as first child of destination
  374. amAddChildLast // add node as last child of destination
  375. );
  376. // modes to determine drop position further
  377. TDropMode = (
  378. dmNowhere,
  379. dmAbove,
  380. dmOnNode,
  381. dmBelow
  382. );
  383. // operations basically allowed during drag'n drop
  384. TDragOperation = (
  385. doCopy,
  386. doMove,
  387. doLink
  388. );
  389. TDragOperations = set of TDragOperation;
  390. TVTImageKind = (
  391. ikNormal,
  392. ikSelected,
  393. ikState,
  394. ikOverlay
  395. );
  396. TVTHintMode = (
  397. hmDefault, // show the hint of the control
  398. hmHint, // show node specific hint string returned by the application
  399. hmHintAndDefault, // same as hmHint but show the control's hint if no node is concerned
  400. hmTooltip // show the text of the node if it isn't already fully shown
  401. );
  402. // Indicates how to format a tooltip.
  403. TVTTooltipLineBreakStyle = (
  404. hlbDefault, // Use multi-line style of the node.
  405. hlbForceSingleLine, // Use single line hint.
  406. hlbForceMultiLine // Use multi line hint.
  407. );
  408. TMouseButtons = set of TMouseButton;
  409. // Used to describe the action to do when using the OnBeforeItemErase event.
  410. TItemEraseAction = (
  411. eaColor, // Use the provided color to erase the background instead the one of the tree.
  412. eaDefault, // The tree should erase the item's background (bitmap or solid).
  413. eaNone // Do nothing. Let the application paint the background.
  414. );
  415. // There is a heap of switchable behavior in the tree. Since published properties may never exceed 4 bytes,
  416. // which limits sets to at most 32 members, and because for better overview tree options are splitted
  417. // in various sub-options and are held in a commom options class.
  418. //
  419. // Options to customize tree appearance:
  420. TVTPaintOption = (
  421. toHideFocusRect, // Avoid drawing the dotted rectangle around the currently focused node.
  422. toHideSelection, // Selected nodes are drawn as unselected nodes if the tree is unfocused.
  423. toHotTrack, // Track which node is under the mouse cursor.
  424. toPopupMode, // Paint tree as would it always have the focus (useful for tree combo boxes etc.)
  425. toShowBackground, // Use the background image if there's one.
  426. toShowButtons, // Display collapse/expand buttons left to a node.
  427. toShowDropmark, // Show the dropmark during drag'n drop operations.
  428. toShowHorzGridLines, // Display horizontal lines to simulate a grid.
  429. toShowRoot, // Show lines also at top level (does not show the hidden/internal root node).
  430. toShowTreeLines, // Display tree lines to show hierarchy of nodes.
  431. toShowVertGridLines, // Display vertical lines (depending on columns) to simulate a grid.
  432. toThemeAware, // Draw UI elements (header, tree buttons etc.) according to the current theme if
  433. // enabled (Windows XP+ only, application must be themed).
  434. toUseBlendedImages, // Enable alpha blending for ghosted nodes or those which are being cut/copied.
  435. toGhostedIfUnfocused, // Ghosted images are still shown as ghosted if unfocused (otherwise the become non-ghosted
  436. // images).
  437. toFullVertGridLines, // Display vertical lines over the full client area, not only the space occupied by nodes.
  438. // This option only has an effect if toShowVertGridLines is enabled too.
  439. toAlwaysHideSelection, // Do not draw node selection, regardless of focused state.
  440. toUseBlendedSelection, // Enable alpha blending for node selections.
  441. toStaticBackground, // Show simple static background instead of a tiled one.
  442. toChildrenAbove, // Display child nodes above their parent.
  443. toFixedIndent, // Draw the tree with a fixed indent.
  444. toUseExplorerTheme, // Use the explorer theme if run under Windows Vista (or above).
  445. toHideTreeLinesIfThemed, // Do not show tree lines if theming is used.
  446. toShowFilteredNodes // Draw nodes even if they are filtered out.
  447. );
  448. TVTPaintOptions = set of TVTPaintOption;
  449. // Options to toggle animation support:
  450. TVTAnimationOption = (
  451. toAnimatedToggle, // Expanding and collapsing a node is animated (quick window scroll).
  452. toAdvancedAnimatedToggle // Do some advanced animation effects when toggling a node.
  453. );
  454. TVTAnimationOptions = set of TVTAnimationOption;
  455. // Options which toggle automatic handling of certain situations:
  456. TVTAutoOption = (
  457. toAutoDropExpand, // Expand node if it is the drop target for more than a certain time.
  458. toAutoExpand, // Nodes are expanded (collapsed) when getting (losing) the focus.
  459. toAutoScroll, // Scroll if mouse is near the border while dragging or selecting.
  460. toAutoScrollOnExpand, // Scroll as many child nodes in view as possible after expanding a node.
  461. toAutoSort, // Sort tree when Header.SortColumn or Header.SortDirection change or sort node if
  462. // child nodes are added.
  463. toAutoSpanColumns, // Large entries continue into next column(s) if there's no text in them (no clipping).
  464. toAutoTristateTracking, // Checkstates are automatically propagated for tri state check boxes.
  465. toAutoHideButtons, // Node buttons are hidden when there are child nodes, but all are invisible.
  466. toAutoDeleteMovedNodes, // Delete nodes which where moved in a drag operation (if not directed otherwise).
  467. toDisableAutoscrollOnFocus, // Disable scrolling a node or column into view if it gets focused.
  468. toAutoChangeScale, // Change default node height automatically if the system's font scale is set to big fonts.
  469. toAutoFreeOnCollapse, // Frees any child node after a node has been collapsed (HasChildren flag stays there).
  470. toDisableAutoscrollOnEdit, // Do not center a node horizontally when it is edited.
  471. toAutoBidiColumnOrdering // When set then columns (if any exist) will be reordered from lowest index to highest index
  472. // and vice versa when the tree's bidi mode is changed.
  473. );
  474. TVTAutoOptions = set of TVTAutoOption;
  475. // Options which determine the tree's behavior when selecting nodes:
  476. TVTSelectionOption = (
  477. toDisableDrawSelection, // Prevent user from selecting with the selection rectangle in multiselect mode.
  478. toExtendedFocus, // Entries other than in the main column can be selected, edited etc.
  479. toFullRowSelect, // Hit test as well as selection highlight are not constrained to the text of a node.
  480. toLevelSelectConstraint, // Constrain selection to the same level as the selection anchor.
  481. toMiddleClickSelect, // Allow selection, dragging etc. with the middle mouse button. This and toWheelPanning
  482. // are mutual exclusive.
  483. toMultiSelect, // Allow more than one node to be selected.
  484. toRightClickSelect, // Allow selection, dragging etc. with the right mouse button.
  485. toSiblingSelectConstraint, // Constrain selection to nodes with same parent.
  486. toCenterScrollIntoView, // Center nodes vertically in the client area when scrolling into view.
  487. toSimpleDrawSelection, // Simplifies draw selection, so a node's caption does not need to intersect with the
  488. // selection rectangle.
  489. toAlwaysSelectNode, // If this flag is set to true, the tree view tries to always have a node selected.
  490. // This behavior is closer to the Windows TreeView and useful in Windows Explorer style applications.
  491. toRestoreSelection // Set to true if upon refill the previously selected nodes should be selected again.
  492. // The nodes will be identified by its caption only.
  493. );
  494. TVTSelectionOptions = set of TVTSelectionOption;
  495. // Options which do not fit into any of the other groups:
  496. TVTMiscOption = (
  497. toAcceptOLEDrop, // Register tree as OLE accepting drop target
  498. toCheckSupport, // Show checkboxes/radio buttons.
  499. toEditable, // Node captions can be edited.
  500. toFullRepaintOnResize, // Fully invalidate the tree when its window is resized (CS_HREDRAW/CS_VREDRAW).
  501. toGridExtensions, // Use some special enhancements to simulate and support grid behavior.
  502. toInitOnSave, // Initialize nodes when saving a tree to a stream.
  503. toReportMode, // Tree behaves like TListView in report mode.
  504. toToggleOnDblClick, // Toggle node expansion state when it is double clicked.
  505. toWheelPanning, // Support for mouse panning (wheel mice only). This option and toMiddleClickSelect are
  506. // mutal exclusive, where panning has precedence.
  507. toReadOnly, // The tree does not allow to be modified in any way. No action is executed and
  508. // node editing is not possible.
  509. toVariableNodeHeight, // When set then GetNodeHeight will trigger OnMeasureItem to allow variable node heights.
  510. toFullRowDrag, // Start node dragging by clicking anywhere in it instead only on the caption or image.
  511. // Must be used together with toDisableDrawSelection.
  512. toNodeHeightResize, // Allows changing a node's height via mouse.
  513. toNodeHeightDblClickResize, // Allows to reset a node's height to FDefaultNodeHeight via a double click.
  514. toEditOnClick, // Editing mode can be entered with a single click
  515. toEditOnDblClick, // Editing mode can be entered with a double click
  516. toReverseFullExpandHotKey // Used to define Ctrl+'+' instead of Ctrl+Shift+'+' for full expand (and similar for collapsing)
  517. );
  518. TVTMiscOptions = set of TVTMiscOption;
  519. // Options to control data export
  520. TVTExportMode = (
  521. emAll, // export all records (regardless checked state)
  522. emChecked, // export checked records only
  523. emUnchecked, // export unchecked records only
  524. emVisibleDueToExpansion, //Do not export nodes that are not visible because their parent is not expanded
  525. emSelected // export selected nodes only
  526. );
  527. // Kinds of operations
  528. TVTOperationKind = (
  529. okAutoFitColumns,
  530. okGetMaxColumnWidth,
  531. okSortNode,
  532. okSortTree
  533. );
  534. TVTOperationKinds = set of TVTOperationKind;
  535. const
  536. DefaultPaintOptions = [toShowButtons, toShowDropmark, toShowTreeLines, toShowRoot, toThemeAware, toUseBlendedImages];
  537. DefaultAnimationOptions = [];
  538. DefaultAutoOptions = [toAutoDropExpand, toAutoTristateTracking, toAutoScrollOnExpand, toAutoDeleteMovedNodes, toAutoChangeScale, toAutoSort];
  539. DefaultSelectionOptions = [];
  540. DefaultMiscOptions = [toAcceptOLEDrop, toFullRepaintOnResize, toInitOnSave, toToggleOnDblClick, toWheelPanning,
  541. toEditOnClick];
  542. DefaultColumnOptions = [coAllowClick, coDraggable, coEnabled, coParentColor, coParentBidiMode, coResizable,
  543. coShowDropmark, coVisible, coAllowFocus, coEditable];
  544. type
  545. TBaseVirtualTree = class;
  546. TVirtualTreeClass = class of TBaseVirtualTree;
  547. PVirtualNode = ^TVirtualNode;
  548. TColumnIndex = type Integer;
  549. TColumnPosition = type Cardinal;
  550. // This record must already be defined here and not later because otherwise BCB users will not be able
  551. // to compile (conversion done by BCB is wrong).
  552. TCacheEntry = record
  553. Node: PVirtualNode;
  554. AbsoluteTop: Cardinal;
  555. end;
  556. TCache = array of TCacheEntry;
  557. TNodeArray = array of PVirtualNode;
  558. TCustomVirtualTreeOptions = class(TPersistent)
  559. private
  560. FOwner: TBaseVirtualTree;
  561. FPaintOptions: TVTPaintOptions;
  562. FAnimationOptions: TVTAnimationOptions;
  563. FAutoOptions: TVTAutoOptions;
  564. FSelectionOptions: TVTSelectionOptions;
  565. FMiscOptions: TVTMiscOptions;
  566. FExportMode: TVTExportMode;
  567. procedure SetAnimationOptions(const Value: TVTAnimationOptions);
  568. procedure SetAutoOptions(const Value: TVTAutoOptions);
  569. procedure SetMiscOptions(const Value: TVTMiscOptions);
  570. procedure SetPaintOptions(const Value: TVTPaintOptions);
  571. procedure SetSelectionOptions(const Value: TVTSelectionOptions);
  572. protected
  573. property AnimationOptions: TVTAnimationOptions read FAnimationOptions write SetAnimationOptions
  574. default DefaultAnimationOptions;
  575. property AutoOptions: TVTAutoOptions read FAutoOptions write SetAutoOptions default DefaultAutoOptions;
  576. property ExportMode: TVTExportMode read FExportMode write FExportMode default emAll;
  577. property MiscOptions: TVTMiscOptions read FMiscOptions write SetMiscOptions default DefaultMiscOptions;
  578. property PaintOptions: TVTPaintOptions read FPaintOptions write SetPaintOptions default DefaultPaintOptions;
  579. property SelectionOptions: TVTSelectionOptions read FSelectionOptions write SetSelectionOptions
  580. default DefaultSelectionOptions;
  581. public
  582. constructor Create(AOwner: TBaseVirtualTree); virtual;
  583. procedure AssignTo(Dest: TPersistent); override;
  584. property Owner: TBaseVirtualTree read FOwner;
  585. end;
  586. TTreeOptionsClass = class of TCustomVirtualTreeOptions;
  587. TVirtualTreeOptions = class(TCustomVirtualTreeOptions)
  588. published
  589. property AnimationOptions;
  590. property AutoOptions;
  591. property ExportMode;
  592. property MiscOptions;
  593. property PaintOptions;
  594. property SelectionOptions;
  595. end;
  596. // Used in the CF_VTREFERENCE clipboard format.
  597. PVTReference = ^TVTReference;
  598. TVTReference = record
  599. Process: Cardinal;
  600. Tree: TBaseVirtualTree;
  601. end;
  602. TVirtualNode = packed record
  603. Index, // index of node with regard to its parent
  604. ChildCount: Cardinal; // number of child nodes
  605. NodeHeight: Word; // height in pixels
  606. States: TVirtualNodeStates; // states describing various properties of the node (expanded, initialized etc.)
  607. Align: Byte; // line/button alignment
  608. CheckState: TCheckState; // indicates the current check state (e.g. checked, pressed etc.)
  609. CheckType: TCheckType; // indicates which check type shall be used for this node
  610. Dummy: Byte; // dummy value to fill DWORD boundary
  611. TotalCount, // sum of this node, all of its child nodes and their child nodes etc.
  612. TotalHeight: Cardinal; // height in pixels this node covers on screen including the height of all of its
  613. // children
  614. // Note: Some copy routines require that all pointers (as well as the data area) in a node are
  615. // located at the end of the node! Hence if you want to add new member fields (except pointers to internal
  616. // data) then put them before field Parent.
  617. Parent, // reference to the node's parent (for the root this contains the treeview)
  618. PrevSibling, // link to the node's previous sibling or nil if it is the first node
  619. NextSibling, // link to the node's next sibling or nil if it is the last node
  620. FirstChild, // link to the node's first child...
  621. LastChild: PVirtualNode; // link to the node's last child...
  622. Data: record end; // this is a placeholder, each node gets extra data determined by NodeDataSize
  623. end;
  624. // Structure used when info about a certain position in the header is needed.
  625. TVTHeaderHitInfo = record
  626. X,
  627. Y: Integer;
  628. Button: TMouseButton;
  629. Shift: TShiftState;
  630. Column: TColumnIndex;
  631. HitPosition: TVTHeaderHitPositions;
  632. end;
  633. // Structure used when info about a certain position in the tree is needed.
  634. THitInfo = record
  635. HitNode: PVirtualNode;
  636. HitPositions: THitPositions;
  637. HitColumn: TColumnIndex;
  638. HitPoint: TPoint;
  639. end;
  640. // auto scroll directions
  641. TScrollDirections = set of (
  642. sdLeft,
  643. sdUp,
  644. sdRight,
  645. sdDown
  646. );
  647. // OLE drag'n drop support
  648. TFormatEtcArray = array of TFormatEtc;
  649. TFormatArray = array of Word;
  650. // IDataObject.SetData support
  651. TInternalStgMedium = packed record
  652. Format: TClipFormat;
  653. Medium: TStgMedium;
  654. end;
  655. TInternalStgMediumArray = array of TInternalStgMedium;
  656. TEnumFormatEtc = class(TInterfacedObject, IEnumFormatEtc)
  657. private
  658. FTree: TBaseVirtualTree;
  659. FFormatEtcArray: TFormatEtcArray;
  660. FCurrentIndex: Integer;
  661. public
  662. constructor Create(Tree: TBaseVirtualTree; AFormatEtcArray: TFormatEtcArray);
  663. function Clone(out Enum: IEnumFormatEtc): HResult; stdcall;
  664. function Next(celt: Integer; out elt; pceltFetched: PLongint): HResult; stdcall;
  665. function Reset: HResult; stdcall;
  666. function Skip(celt: Integer): HResult; stdcall;
  667. end;
  668. // ----- OLE drag'n drop handling
  669. {$if CompilerVersion < 21}
  670. {$EXTERNALSYM IDropTargetHelper}
  671. IDropTargetHelper = interface(IUnknown)
  672. [SID_IDropTargetHelper]
  673. function DragEnter(hwndTarget: HWND; pDataObject: IDataObject; var ppt: TPoint; dwEffect: Integer): HRESULT; stdcall;
  674. function DragLeave: HRESULT; stdcall;
  675. function DragOver(var ppt: TPoint; dwEffect: Integer): HRESULT; stdcall;
  676. function Drop(pDataObject: IDataObject; var ppt: TPoint; dwEffect: Integer): HRESULT; stdcall;
  677. function Show(fShow: Boolean): HRESULT; stdcall;
  678. end;
  679. PSHDragImage = ^TSHDragImage;
  680. TSHDragImage = packed record
  681. sizeDragImage: TSize;
  682. ptOffset: TPoint;
  683. hbmpDragImage: HBITMAP;
  684. crColorKey: TColorRef;
  685. end;
  686. IDragSourceHelper = interface(IUnknown)
  687. [SID_IDragSourceHelper]
  688. function InitializeFromBitmap(SHDragImage: PSHDragImage; pDataObject: IDataObject): HRESULT; stdcall;
  689. function InitializeFromWindow(Window: HWND; var ppt: TPoint; pDataObject: IDataObject): HRESULT; stdcall;
  690. end;
  691. {$EXTERNALSYM IDragSourceHelper}
  692. IDragSourceHelper2 = interface(IDragSourceHelper)
  693. [SID_IDragSourceHelper2]
  694. function SetFlags(dwFlags: DWORD): HRESULT; stdcall;
  695. end;
  696. {$EXTERNALSYM IDragSourceHelper2}
  697. {$ifend}
  698. IVTDragManager = interface(IUnknown)
  699. ['{C4B25559-14DA-446B-8901-0C879000EB16}']
  700. procedure ForceDragLeave; stdcall;
  701. function GetDataObject: IDataObject; stdcall;
  702. function GetDragSource: TBaseVirtualTree; stdcall;
  703. function GetDropTargetHelperSupported: Boolean; stdcall;
  704. function GetIsDropTarget: Boolean; stdcall;
  705. property DataObject: IDataObject read GetDataObject;
  706. property DragSource: TBaseVirtualTree read GetDragSource;
  707. property DropTargetHelperSupported: Boolean read GetDropTargetHelperSupported;
  708. property IsDropTarget: Boolean read GetIsDropTarget;
  709. end;
  710. // This data object is used in two different places. One is for clipboard operations and the other while dragging.
  711. TVTDataObject = class(TInterfacedObject, IDataObject)
  712. private
  713. FOwner: TBaseVirtualTree; // The tree which provides clipboard or drag data.
  714. FForClipboard: Boolean; // Determines which data to render with GetData.
  715. FFormatEtcArray: TFormatEtcArray;
  716. FInternalStgMediumArray: TInternalStgMediumArray; // The available formats in the DataObject
  717. FAdviseHolder: IDataAdviseHolder; // Reference to an OLE supplied implementation for advising.
  718. protected
  719. function CanonicalIUnknown(TestUnknown: IUnknown): IUnknown;
  720. function EqualFormatEtc(FormatEtc1, FormatEtc2: TFormatEtc): Boolean;
  721. function FindFormatEtc(TestFormatEtc: TFormatEtc; const FormatEtcArray: TFormatEtcArray): integer;
  722. function FindInternalStgMedium(Format: TClipFormat): PStgMedium;
  723. function HGlobalClone(HGlobal: THandle): THandle;
  724. function RenderInternalOLEData(const FormatEtcIn: TFormatEtc; var Medium: TStgMedium; var OLEResult: HResult): Boolean;
  725. function StgMediumIncRef(const InStgMedium: TStgMedium; var OutStgMedium: TStgMedium;
  726. CopyInMedium: Boolean; DataObject: IDataObject): HRESULT;
  727. property ForClipboard: Boolean read FForClipboard;
  728. property FormatEtcArray: TFormatEtcArray read FFormatEtcArray write FFormatEtcArray;
  729. property InternalStgMediumArray: TInternalStgMediumArray read FInternalStgMediumArray write FInternalStgMediumArray;
  730. property Owner: TBaseVirtualTree read FOwner;
  731. public
  732. constructor Create(AOwner: TBaseVirtualTree; ForClipboard: Boolean); virtual;
  733. destructor Destroy; override;
  734. function DAdvise(const FormatEtc: TFormatEtc; advf: Integer; const advSink: IAdviseSink; out dwConnection: Integer):
  735. HResult; virtual; stdcall;
  736. function DUnadvise(dwConnection: Integer): HResult; virtual; stdcall;
  737. function EnumDAdvise(out enumAdvise: IEnumStatData): HResult; virtual; stdcall;
  738. function EnumFormatEtc(Direction: Integer; out EnumFormatEtc: IEnumFormatEtc): HResult; virtual; stdcall;
  739. function GetCanonicalFormatEtc(const FormatEtc: TFormatEtc; out FormatEtcOut: TFormatEtc): HResult; virtual; stdcall;
  740. function GetData(const FormatEtcIn: TFormatEtc; out Medium: TStgMedium): HResult; virtual; stdcall;
  741. function GetDataHere(const FormatEtc: TFormatEtc; out Medium: TStgMedium): HResult; virtual; stdcall;
  742. function QueryGetData(const FormatEtc: TFormatEtc): HResult; virtual; stdcall;
  743. function SetData(const FormatEtc: TFormatEtc; var Medium: TStgMedium; DoRelease: BOOL): HResult; virtual; stdcall;
  744. end;
  745. // TVTDragManager is a class to manage drag and drop in a Virtual Treeview.
  746. TVTDragManager = class(TInterfacedObject, IVTDragManager, IDropSource, IDropTarget)
  747. private
  748. FOwner, // The tree which is responsible for drag management.
  749. FDragSource: TBaseVirtualTree; // Reference to the source tree if the source was a VT, might be different than
  750. // the owner tree.
  751. FIsDropTarget: Boolean; // True if the owner is currently the drop target.
  752. FDataObject: IDataObject; // A reference to the data object passed in by DragEnter (only used when the owner
  753. // tree is the current drop target).
  754. FDropTargetHelper: IDropTargetHelper; // Win2k > Drag image support
  755. FFullDragging: BOOL; // True, if full dragging is currently enabled in the system.
  756. function GetDataObject: IDataObject; stdcall;
  757. function GetDragSource: TBaseVirtualTree; stdcall;
  758. function GetDropTargetHelperSupported: Boolean; stdcall;
  759. function GetIsDropTarget: Boolean; stdcall;
  760. public
  761. constructor Create(AOwner: TBaseVirtualTree); virtual;
  762. destructor Destroy; override;
  763. function DragEnter(const DataObject: IDataObject; KeyState: Integer; Pt: TPoint;
  764. var Effect: Longint): HResult; stdcall;
  765. function DragLeave: HResult; stdcall;
  766. function DragOver(KeyState: Integer; Pt: TPoint; var Effect: LongInt): HResult; stdcall;
  767. function Drop(const DataObject: IDataObject; KeyState: Integer; Pt: TPoint; var Effect: Integer): HResult; stdcall;
  768. procedure ForceDragLeave; stdcall;
  769. function GiveFeedback(Effect: Integer): HResult; stdcall;
  770. function QueryContinueDrag(EscapePressed: BOOL; KeyState: Integer): HResult; stdcall;
  771. end;
  772. PVTHintData = ^TVTHintData;
  773. TVTHintData = record
  774. Tree: TBaseVirtualTree;
  775. Node: PVirtualNode;
  776. Column: TColumnIndex;
  777. HintRect: TRect; // used for draw trees only, string trees get the size from the hint string
  778. DefaultHint: UnicodeString; // used only if there is no node specific hint string available
  779. // or a header hint is about to appear
  780. HintText: UnicodeString; // set when size of the hint window is calculated
  781. BidiMode: TBidiMode;
  782. Alignment: TAlignment;
  783. LineBreakStyle: TVTToolTipLineBreakStyle;
  784. end;
  785. // Determines the kind of animation when a hint is activated.
  786. THintAnimationType = (
  787. hatNone, // no animation at all, just display hint/tooltip
  788. hatFade, // fade in the hint/tooltip, like in Windows 2000
  789. hatSlide, // slide in the hint/tooltip, like in Windows 98
  790. hatSystemDefault // use what the system is using (slide for Win9x, slide/fade for Win2K+, depends on settings)
  791. );
  792. // The trees need an own hint window class because of Unicode output and adjusted font.
  793. TVirtualTreeHintWindow = class(THintWindow)
  794. private
  795. FHintData: TVTHintData;
  796. FBackground,
  797. FDrawBuffer,
  798. FTarget: TBitmap;
  799. FTextHeight: Integer;
  800. function AnimationCallback(Step, StepSize: Integer; Data: Pointer): Boolean;
  801. procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
  802. function GetHintWindowDestroyed: Boolean;
  803. procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
  804. procedure WMNCPaint(var Message: TMessage); message WM_NCPAINT;
  805. procedure WMShowWindow(var Message: TWMShowWindow); message WM_SHOWWINDOW;
  806. protected
  807. procedure CreateParams(var Params: TCreateParams); override;
  808. procedure InternalPaint(Step, StepSize: Integer);
  809. procedure Paint; override;
  810. property Background: TBitmap read FBackground;
  811. property DrawBuffer: TBitmap read FDrawBuffer;
  812. property HintData: TVTHintData read FHintData;
  813. property HintWindowDestroyed: Boolean read GetHintWindowDestroyed;
  814. property Target: TBitmap read FTarget;
  815. property TextHeight: Integer read FTextHeight;
  816. public
  817. constructor Create(AOwner: TComponent); override;
  818. destructor Destroy; override;
  819. procedure ActivateHint(Rect: TRect; const AHint: string); override;
  820. function CalcHintRect(MaxWidth: Integer; const AHint: string; AData: Pointer): TRect; override;
  821. function IsHintMsg(var Msg: TMsg): Boolean; override;
  822. end;
  823. // Drag image support for the tree.
  824. TVTTransparency = 0..255;
  825. TVTBias = -128..127;
  826. // Simple move limitation for the drag image.
  827. TVTDragMoveRestriction = (
  828. dmrNone,
  829. dmrHorizontalOnly,
  830. dmrVerticalOnly
  831. );
  832. TVTDragImageStates = set of (
  833. disHidden, // Internal drag image is currently hidden (always hidden if drag image helper interfaces are used).
  834. disInDrag, // Drag image class is currently being used.
  835. disPrepared, // Drag image class is prepared.
  836. disSystemSupport // Running on Windows 2000 or higher. System supports drag images natively.
  837. );
  838. // Class to manage header and tree drag image during a drag'n drop operation.
  839. TVTDragImage = class
  840. private
  841. FOwner: TBaseVirtualTree;
  842. FBackImage, // backup of overwritten screen area
  843. FAlphaImage, // target for alpha blending
  844. FDragImage: TBitmap; // the actual drag image to blend to screen
  845. FImagePosition, // position of image (upper left corner) in screen coordinates
  846. FLastPosition: TPoint; // last mouse position in screen coordinates
  847. FTransparency: TVTTransparency; // alpha value of the drag image (0 - fully transparent, 255 - fully opaque)
  848. FPreBlendBias, // value to darken or lighten the drag image before it is blended
  849. FPostBlendBias: TVTBias; // value to darken or lighten the alpha blend result
  850. FFade: Boolean; // determines whether to fade the drag image from center to borders or not
  851. FRestriction: TVTDragMoveRestriction; // determines in which directions the drag image can be moved
  852. FColorKey: TColor; // color to make fully transparent regardless of any other setting
  853. FStates: TVTDragImageStates; // Determines the states of the drag image class.
  854. function GetVisible: Boolean; // True if the drag image is currently hidden (used only when dragging)
  855. protected
  856. procedure InternalShowDragImage(ScreenDC: HDC);
  857. procedure MakeAlphaChannel(Source, Target: TBitmap);
  858. public
  859. constructor Create(AOwner: TBaseVirtualTree);
  860. destructor Destroy; override;
  861. function DragTo(P: TPoint; ForceRepaint: Boolean): Boolean;
  862. procedure EndDrag;
  863. function GetDragImageRect: TRect;
  864. procedure HideDragImage;
  865. procedure PrepareDrag(DragImage: TBitmap; ImagePosition, HotSpot: TPoint; const DataObject: IDataObject);
  866. procedure RecaptureBackground(Tree: TBaseVirtualTree; R: TRect; VisibleRegion: HRGN; CaptureNCArea,
  867. ReshowDragImage: Boolean);
  868. procedure ShowDragImage;
  869. function WillMove(P: TPoint): Boolean;
  870. property ColorKey: TColor read FColorKey write FColorKey default clWindow;
  871. property Fade: Boolean read FFade write FFade default False;
  872. property MoveRestriction: TVTDragMoveRestriction read FRestriction write FRestriction default dmrNone;
  873. property PostBlendBias: TVTBias read FPostBlendBias write FPostBlendBias default 0;
  874. property PreBlendBias: TVTBias read FPreBlendBias write FPreBlendBias default 0;
  875. property Transparency: TVTTransparency read FTransparency write FTransparency default 128;
  876. property Visible: Boolean read GetVisible;
  877. end;
  878. // tree columns implementation
  879. TVirtualTreeColumns = class;
  880. TVTHeader = class;
  881. TVirtualTreeColumnStyle = (
  882. vsText,
  883. vsOwnerDraw
  884. );
  885. TVTHeaderColumnLayout = (
  886. blGlyphLeft,
  887. blGlyphRight,
  888. blGlyphTop,
  889. blGlyphBottom
  890. );
  891. TSortDirection = (
  892. sdAscending,
  893. sdDescending
  894. );
  895. TVirtualTreeColumn = class(TCollectionItem)
  896. private
  897. FText,
  898. FHint: UnicodeString;
  899. FLeft,
  900. FWidth: Integer;
  901. FPosition: TColumnPosition;
  902. FMinWidth: Integer;
  903. FMaxWidth: Integer;
  904. FStyle: TVirtualTreeColumnStyle;
  905. FImageIndex: TImageIndex;
  906. FBiDiMode: TBiDiMode;
  907. FLayout: TVTHeaderColumnLayout;
  908. FMargin,
  909. FSpacing: Integer;
  910. FOptions: TVTColumnOptions;
  911. FTag: NativeInt;
  912. FAlignment: TAlignment;
  913. FCaptionAlignment: TAlignment; // Alignment of the caption.
  914. FLastWidth: Integer;
  915. FColor: TColor;
  916. FBonusPixel: Boolean;
  917. FSpringRest: Single; // Accumulator for width adjustment when auto spring option is enabled.
  918. FCaptionText: UnicodeString;
  919. FCheckBox: Boolean;
  920. FCheckType: TCheckType;
  921. FCheckState: TCheckState;
  922. FImageRect: TRect;
  923. FHasImage: Boolean;
  924. FDefaultSortDirection: TSortDirection;
  925. function GetCaptionAlignment: TAlignment;
  926. function GetLeft: Integer;
  927. function IsBiDiModeStored: Boolean;
  928. function IsCaptionAlignmentStored: Boolean;
  929. function IsColorStored: Boolean;
  930. procedure SetAlignment(const Value: TAlignment);
  931. procedure SetBiDiMode(Value: TBiDiMode);
  932. procedure SetCaptionAlignment(const Value: TAlignment);
  933. procedure SetCheckBox(Value: Boolean);
  934. procedure SetCheckState(Value: TCheckState);
  935. procedure SetCheckType(Value: TCheckType);
  936. procedure SetColor(const Value: TColor);
  937. procedure SetImageIndex(Value: TImageIndex);
  938. procedure SetLayout(Value: TVTHeaderColumnLayout);
  939. procedure SetMargin(Value: Integer);
  940. procedure SetMaxWidth(Value: Integer);
  941. procedure SetMinWidth(Value: Integer);
  942. procedure SetOptions(Value: TVTColumnOptions);
  943. procedure SetPosition(Value: TColumnPosition);
  944. procedure SetSpacing(Value: Integer);
  945. procedure SetStyle(Value: TVirtualTreeColumnStyle);
  946. procedure SetWidth(Value: Integer);
  947. protected
  948. procedure ComputeHeaderLayout(DC: HDC; Client: TRect; UseHeaderGlyph, UseSortGlyph: Boolean;
  949. var HeaderGlyphPos, SortGlyphPos: TPoint; var SortGlyphSize: TSize; var TextBounds: TRect; DrawFormat: Cardinal;
  950. CalculateTextRect: Boolean = False);
  951. procedure DefineProperties(Filer: TFiler); override;
  952. procedure GetAbsoluteBounds(var Left, Right: Integer);
  953. function GetDisplayName: string; override;
  954. function GetText: UnicodeString; virtual; // [IPK]
  955. procedure SetText(const Value: UnicodeString); virtual; // [IPK] private to protected & virtual
  956. function GetOwner: TVirtualTreeColumns; reintroduce;
  957. procedure ReadHint(Reader: TReader);
  958. procedure ReadText(Reader: TReader);
  959. procedure WriteHint(Writer: TWriter);
  960. procedure WriteText(Writer: TWriter);
  961. property HasImage: Boolean read FHasImage;
  962. property ImageRect: TRect read FImageRect;
  963. public
  964. constructor Create(Collection: TCollection); override;
  965. destructor Destroy; override;
  966. procedure Assign(Source: TPersistent); override;
  967. {$if CompilerVersion >= 20}
  968. function Equals(OtherColumnObj: TObject): Boolean; override;
  969. {$else}
  970. function Equals(OtherColumnObj: TObject): Boolean; virtual;
  971. {$ifend}
  972. function GetRect: TRect; virtual;
  973. procedure LoadFromStream(const Stream: TStream; Version: Integer);
  974. procedure ParentBiDiModeChanged;
  975. procedure ParentColorChanged;
  976. procedure RestoreLastWidth;
  977. procedure SaveToStream(const Stream: TStream);
  978. function UseRightToLeftReading: Boolean;
  979. property Left: Integer read GetLeft;
  980. property Owner: TVirtualTreeColumns read GetOwner;
  981. published
  982. property Alignment: TAlignment read FAlignment write SetAlignment default taLeftJustify;
  983. property BiDiMode: TBiDiMode read FBiDiMode write SetBiDiMode stored IsBiDiModeStored;
  984. property CaptionAlignment: TAlignment read GetCaptionAlignment write SetCaptionAlignment
  985. stored IsCaptionAlignmentStored default taLeftJustify;
  986. property CaptionText: UnicodeString read FCaptionText stored False;
  987. property CheckType: TCheckType read FCheckType write SetCheckType default ctCheckBox;
  988. property CheckState: TCheckState read FCheckState write SetCheckState default csUncheckedNormal;
  989. property CheckBox: Boolean read FCheckBox write SetCheckBox default False;
  990. property Color: TColor read FColor write SetColor stored IsColorStored;
  991. property DefaultSortDirection: TSortDirection read FDefaultSortDirection write FDefaultSortDirection default sdAscending;
  992. property Hint: UnicodeString read FHint write FHint stored False;
  993. property ImageIndex: TImageIndex read FImageIndex write SetImageIndex default -1;
  994. property Layout: TVTHeaderColumnLayout read FLayout write SetLayout default blGlyphLeft;
  995. property Margin: Integer read FMargin write SetMargin default 4;
  996. property MaxWidth: Integer read FMaxWidth write SetMaxWidth default 10000;
  997. property MinWidth: Integer read FMinWidth write SetMinWidth default 10;
  998. property Options: TVTColumnOptions read FOptions write SetOptions default DefaultColumnOptions;
  999. property Position: TColumnPosition read FPosition write SetPosition;
  1000. property Spacing: Integer read FSpacing write SetSpacing default 3;
  1001. property Style: TVirtualTreeColumnStyle read FStyle write SetStyle default vsText;
  1002. property Tag: NativeInt read FTag write FTag default 0;
  1003. property Text: UnicodeString read GetText write SetText stored False; // Never let the VCL store the wide string, // [IPK] FText changed to GetText
  1004. // it is simply unable to write it correctly.
  1005. // We use DefineProperties here.
  1006. property Width: Integer read FWidth write SetWidth default 50;
  1007. end;
  1008. TVirtualTreeColumnClass = class of TVirtualTreeColumn;
  1009. TColumnsArray = array of TVirtualTreeColumn;
  1010. TCardinalArray = array of Cardinal;
  1011. TIndexArray = array of TColumnIndex;
  1012. TVirtualTreeColumns = class(TCollection)
  1013. private
  1014. FHeader: TVTHeader;
  1015. FHeaderBitmap: TBitmap; // backbuffer for drawing
  1016. FHoverIndex, // currently "hot" column
  1017. FDownIndex, // Column on which a mouse button is held down.
  1018. FTrackIndex: TColumnIndex; // Index of column which is currently being resized.
  1019. FClickIndex: TColumnIndex; // Index of the last clicked column.
  1020. FCheckBoxHit: Boolean; // True if the last click was on a header checkbox.
  1021. FPositionToIndex: TIndexArray;
  1022. FDefaultWidth: Integer; // the width columns are created with
  1023. FNeedPositionsFix: Boolean; // True if FixPositions must still be called after DFM loading or Bidi mode change.
  1024. FClearing: Boolean; // True if columns are being deleted entirely.
  1025. function GetCount: Integer;
  1026. function GetItem(Index: TColumnIndex): TVirtualTreeColumn;
  1027. function GetNewIndex(P: TPoint; var OldIndex: TColumnIndex): Boolean;
  1028. procedure SetDefaultWidth(Value: Integer);
  1029. procedure SetItem(Index: TColumnIndex; Value: TVirtualTreeColumn);
  1030. protected
  1031. // drag support
  1032. FDragIndex: TColumnIndex; // index of column currently being dragged
  1033. FDropTarget: TColumnIndex; // current target column (index) while dragging
  1034. FDropBefore: Boolean; // True if drop position is in the left half of a column, False for the right
  1035. // side to drop the dragged column to
  1036. procedure AdjustAutoSize(CurrentIndex: TColumnIndex; Force: Boolean = False);
  1037. function AdjustDownColumn(P: TPoint): TColumnIndex;
  1038. function AdjustHoverColumn(P: TPoint): Boolean;
  1039. procedure AdjustPosition(Column: TVirtualTreeColumn; Position: Cardinal);
  1040. function CanSplitterResize(P: TPoint; Column: TColumnIndex): Boolean;
  1041. procedure DoCanSplitterResize(P: TPoint; Column: TColumnIndex; var Allowed: Boolean); virtual;
  1042. procedure DrawButtonText(DC: HDC; Caption: UnicodeString; Bounds: TRect; Enabled, Hot: Boolean; DrawFormat: Cardinal;
  1043. WrapCaption: Boolean);
  1044. procedure FixPositions;
  1045. function GetColumnAndBounds(P: TPoint; var ColumnLeft, ColumnRight: Integer; Relative: Boolean = True): Integer;
  1046. function GetOwner: TPersistent; override;
  1047. procedure HandleClick(P: TPoint; Button: TMouseButton; Force, DblClick: Boolean); virtual;
  1048. procedure IndexChanged(OldIndex, NewIndex: Integer);
  1049. procedure InitializePositionArray;
  1050. procedure Notify(Item: TCollectionItem; Action: TCollectionNotification); override;
  1051. procedure ReorderColumns(RTL: Boolean);
  1052. procedure Update(Item: TCollectionItem); override;
  1053. procedure UpdatePositions(Force: Boolean = False);
  1054. property HeaderBitmap: TBitmap read FHeaderBitmap;
  1055. property PositionToIndex: TIndexArray read FPositionToIndex;
  1056. property HoverIndex: TColumnIndex read FHoverIndex;
  1057. property DownIndex: TColumnIndex read FDownIndex;
  1058. property CheckBoxHit: Boolean read FCheckBoxHit;
  1059. public
  1060. constructor Create(AOwner: TVTHeader); virtual;
  1061. destructor Destroy; override;
  1062. function Add: TVirtualTreeColumn; virtual;
  1063. procedure AnimatedResize(Column: TColumnIndex; NewWidth: Integer);
  1064. procedure Assign(Source: TPersistent); override;
  1065. procedure Clear; virtual;
  1066. function ColumnFromPosition(P: TPoint; Relative: Boolean = True): TColumnIndex; overload; virtual;
  1067. function ColumnFromPosition(PositionIndex: TColumnPosition): TColumnIndex; overload; virtual;
  1068. {$if CompilerVersion >= 20}
  1069. function Equals(OtherColumnsObj: TObject): Boolean; override;
  1070. {$else}
  1071. function Equals(OtherColumnsObj: TObject): Boolean;
  1072. {$ifend}
  1073. procedure GetColumnBounds(Column: TColumnIndex; var Left, Right: Integer);
  1074. function GetFirstVisibleColumn(ConsiderAllowFocus: Boolean = False): TColumnIndex;
  1075. function GetLastVisibleColumn(ConsiderAllowFocus: Boolean = False): TColumnIndex;
  1076. function GetFirstColumn: TColumnIndex;
  1077. function GetNextColumn(Column: TColumnIndex): TColumnIndex;
  1078. function GetNextVisibleColumn(Column: TColumnIndex; ConsiderAllowFocus: Boolean = False): TColumnIndex;
  1079. function GetPreviousColumn(Column: TColumnIndex): TColumnIndex;
  1080. function GetPreviousVisibleColumn(Column: TColumnIndex; ConsiderAllowFocus: Boolean = False): TColumnIndex;
  1081. function GetScrollWidth: Integer;
  1082. function GetVisibleColumns: TColumnsArray;
  1083. function GetVisibleFixedWidth: Integer;
  1084. function IsValidColumn(Column: TColumnIndex): Boolean;
  1085. procedure LoadFromStream(const Stream: TStream; Version: Integer);
  1086. procedure PaintHeader(DC: HDC; R: TRect; HOffset: Integer); overload; virtual;
  1087. procedure PaintHeader(TargetCanvas: TCanvas; R: TRect; const Target: TPoint;
  1088. RTLOffset: Integer = 0); overload; virtual;
  1089. procedure SaveToStream(const Stream: TStream);
  1090. function TotalWidth: Integer;
  1091. property Count: Integer read GetCount;
  1092. property ClickIndex: TColumnIndex read FClickIndex;
  1093. property DefaultWidth: Integer read FDefaultWidth write SetDefaultWidth default 50;
  1094. property Items[Index: TColumnIndex]: TVirtualTreeColumn read GetItem write SetItem; default;
  1095. property Header: TVTHeader read FHeader;
  1096. property TrackIndex: TColumnIndex read FTrackIndex;
  1097. end;
  1098. TVirtualTreeColumnsClass = class of TVirtualTreeColumns;
  1099. TVTConstraintPercent = 0..100;
  1100. TVTFixedAreaConstraints = class(TPersistent)
  1101. private
  1102. FHeader: TVTHeader;
  1103. FMaxHeightPercent,
  1104. FMaxWidthPercent,
  1105. FMinHeightPercent,
  1106. FMinWidthPercent: TVTConstraintPercent;
  1107. FOnChange: TNotifyEvent;
  1108. procedure SetConstraints(Index: Integer; Value: TVTConstraintPercent);
  1109. protected
  1110. procedure Change;
  1111. property Header: TVTHeader read FHeader;
  1112. public
  1113. constructor Create(AOwner: TVTHeader);
  1114. procedure Assign(Source: TPersistent); override;
  1115. property OnChange: TNotifyEvent read FOnChange write FOnChange;
  1116. published
  1117. property MaxHeightPercent: TVTConstraintPercent index 0 read FMaxHeightPercent write SetConstraints default 0;
  1118. property MaxWidthPercent: TVTConstraintPercent index 1 read FMaxWidthPercent write SetConstraints default 0;
  1119. property MinHeightPercent: TVTConstraintPercent index 2 read FMinHeightPercent write SetConstraints default 0;
  1120. property MinWidthPercent: TVTConstraintPercent index 3 read FMinWidthPercent write SetConstraints default 0;
  1121. end;
  1122. TVTHeaderStyle = (
  1123. hsThickButtons, // TButton look and feel
  1124. hsFlatButtons, // flatter look than hsThickButton, like an always raised flat TToolButton
  1125. hsPlates // flat TToolButton look and feel (raise on hover etc.)
  1126. );
  1127. TVTHeaderOption = (
  1128. hoAutoResize, // Adjust a column so that the header never exceeds the client width of the owner control.
  1129. hoColumnResize, // Resizing columns with the mouse is allowed.
  1130. hoDblClickResize, // Allows a column to resize itself to its largest entry.
  1131. hoDrag, // Dragging columns is allowed.
  1132. hoHotTrack, // Header captions are highlighted when mouse is over a particular column.
  1133. hoOwnerDraw, // Header items with the owner draw style can be drawn by the application via event.
  1134. hoRestrictDrag, // Header can only be dragged horizontally.
  1135. hoShowHint, // Show application defined header hint.
  1136. hoShowImages, // Show header images.
  1137. hoShowSortGlyphs, // Allow visible sort glyphs.
  1138. hoVisible, // Header is visible.
  1139. hoAutoSpring, // Distribute size changes of the header to all columns, which are sizable and have the
  1140. // coAutoSpring option enabled.
  1141. hoFullRepaintOnResize, // Fully invalidate the header (instead of subsequent columns only) when a column is resized.
  1142. hoDisableAnimatedResize, // Disable animated resize for all columns.
  1143. hoHeightResize, // Allow resizing header height via mouse.
  1144. hoHeightDblClickResize, // Allow the header to resize itself to its default height.
  1145. hoHeaderClickAutoSort // Clicks on the header will make the clicked column the SortColumn or toggle sort direction if
  1146. // it already was the sort column
  1147. );
  1148. TVTHeaderOptions = set of TVTHeaderOption;
  1149. THeaderState = (
  1150. hsAutoSizing, // auto size chain is in progess, do not trigger again on WM_SIZE
  1151. hsDragging, // header dragging is in progress (only if enabled)
  1152. hsDragPending, // left button is down, user might want to start dragging a column
  1153. hsLoading, // The header currently loads from stream, so updates are not necessary.
  1154. hsColumnWidthTracking, // column resizing is in progress
  1155. hsColumnWidthTrackPending, // left button is down, user might want to start resize a column
  1156. hsHeightTracking, // height resizing is in progress
  1157. hsHeightTrackPending, // left button is down, user might want to start changing height
  1158. hsResizing, // multi column resizing in progress
  1159. hsScaling, // the header is scaled after a change of FixedAreaConstraints or client size
  1160. hsNeedScaling // the header needs to be scaled
  1161. );
  1162. THeaderStates = set of THeaderState;
  1163. TSmartAutoFitType = (
  1164. smaAllColumns, // consider nodes in view only for all columns
  1165. smaNoColumn, // consider nodes in view only for no column
  1166. smaUseColumnOption // use coSmartResize of the corresponding column
  1167. ); // describes the used column resize behaviour for AutoFitColumns
  1168. TChangeReason = (
  1169. crIgnore, // used as placeholder
  1170. crAccumulated, // used for delayed changes
  1171. crChildAdded, // one or more child nodes have been added
  1172. crChildDeleted, // one or more child nodes have been deleted
  1173. crNodeAdded, // a node has been added
  1174. crNodeCopied, // a node has been duplicated
  1175. crNodeMoved // a node has been moved to a new place
  1176. ); // desribes what made a structure change event happen
  1177. TVTHeader = class(TPersistent)
  1178. private
  1179. FOwner: TBaseVirtualTree;
  1180. FColumns: TVirtualTreeColumns;
  1181. FHeight: Integer;
  1182. FFont: TFont;
  1183. FParentFont: Boolean;
  1184. FOptions: TVTHeaderOptions;
  1185. FStyle: TVTHeaderStyle; // button style
  1186. FBackground: TColor;
  1187. FAutoSizeIndex: TColumnIndex;
  1188. FPopupMenu: TPopupMenu;
  1189. FMainColumn: TColumnIndex; // the column which holds the tree
  1190. FMaxHeight: Integer;
  1191. FMinHeight: Integer;
  1192. FDefaultHeight: Integer;
  1193. FFixedAreaConstraints: TVTFixedAreaConstraints; // Percentages for the fixed area (header, fixed columns).
  1194. FImages: TCustomImageList;
  1195. FImageChangeLink: TChangeLink; // connections to the image list to get notified about changes
  1196. FSortColumn: TColumnIndex;
  1197. FSortDirection: TSortDirection;
  1198. FDragImage: TVTDragImage; // drag image management during header drag
  1199. FLastWidth: Integer; // Used to adjust spring columns. This is the width of all visible columns,
  1200. // not the header rectangle.
  1201. procedure FontChanged(Sender: TObject);
  1202. function GetMainColumn: TColumnIndex;
  1203. function GetUseColumns: Boolean;
  1204. function IsFontStored: Boolean;
  1205. procedure SetAutoSizeIndex(Value: TColumnIndex);
  1206. procedure SetBackground(Value: TColor);
  1207. procedure SetColumns(Value: TVirtualTreeColumns);
  1208. procedure SetDefaultHeight(Value: Integer);
  1209. procedure SetFont(const Value: TFont);
  1210. procedure SetHeight(Value: Integer);
  1211. procedure SetImages(const Value: TCustomImageList);
  1212. procedure SetMainColumn(Value: TColumnIndex);
  1213. procedure SetMaxHeight(Value: Integer);
  1214. procedure SetMinHeight(Value: Integer);
  1215. procedure SetOptions(Value: TVTHeaderOptions);
  1216. procedure SetParentFont(Value: Boolean);
  1217. procedure SetSortColumn(Value: TColumnIndex);
  1218. procedure SetSortDirection(const Value: TSortDirection);
  1219. procedure SetStyle(Value: TVTHeaderStyle);
  1220. protected
  1221. FStates: THeaderStates; // Used to keep track of internal states the header can enter.
  1222. FDragStart: TPoint; // initial mouse drag position
  1223. FTrackStart: TPoint; // client coordinates of the tracking start point
  1224. FTrackPoint: TPoint; // Client coordinate where the tracking started.
  1225. function CanSplitterResize(P: TPoint): Boolean;
  1226. function CanWriteColumns: Boolean; virtual;
  1227. procedure ChangeScale(M, D: Integer); virtual;
  1228. function DetermineSplitterIndex(P: TPoint): Boolean; virtual;
  1229. procedure DoAfterAutoFitColumn(Column: TColumnIndex); virtual;
  1230. procedure DoAfterColumnWidthTracking(Column: TColumnIndex); virtual;
  1231. procedure DoAfterHeightTracking; virtual;
  1232. function DoBeforeAutoFitColumn(Column: TColumnIndex; SmartAutoFitType: TSmartAutoFitType): Boolean; virtual;
  1233. procedure DoBeforeColumnWidthTracking(Column: TColumnIndex; Shift: TShiftState); virtual;
  1234. procedure DoBeforeHeightTracking(Shift: TShiftState); virtual;
  1235. procedure DoCanSplitterResize(P: TPoint; var Allowed: Boolean); virtual;
  1236. function DoColumnWidthDblClickResize(Column: TColumnIndex; P: TPoint; Shift: TShiftState): Boolean; virtual;
  1237. function DoColumnWidthTracking(Column: TColumnIndex; Shift: TShiftState; var TrackPoint: TPoint; P: TPoint): Boolean; virtual;
  1238. function DoGetPopupMenu(Column: TColumnIndex; Position: TPoint): TPopupMenu; virtual;
  1239. function DoHeightTracking(var P: TPoint; Shift: TShiftState): Boolean; virtual;
  1240. function DoHeightDblClickResize(var P: TPoint; Shift: TShiftState): Boolean; virtual;
  1241. procedure DoSetSortColumn(Value: TColumnIndex); virtual;
  1242. procedure DragTo(P: TPoint);
  1243. procedure FixedAreaConstraintsChanged(Sender: TObject);
  1244. function GetColumnsClass: TVirtualTreeColumnsClass; virtual;
  1245. function GetOwner: TPersistent; override;
  1246. function GetShiftState: TShiftState;
  1247. function HandleHeaderMouseMove(var Message: TWMMouseMove): Boolean;
  1248. function HandleMessage(var Message: TMessage): Boolean; virtual;
  1249. procedure ImageListChange(Sender: TObject);
  1250. procedure PrepareDrag(P, Start: TPoint);
  1251. procedure ReadColumns(Reader: TReader);
  1252. procedure RecalculateHeader; virtual;
  1253. procedure RescaleHeader;
  1254. procedure UpdateMainColumn;
  1255. procedure UpdateSpringColumns;
  1256. procedure WriteColumns(Writer: TWriter);
  1257. public
  1258. constructor Create(AOwner: TBaseVirtualTree); virtual;
  1259. destructor Destroy; override;
  1260. function AllowFocus(ColumnIndex: TColumnIndex): Boolean;
  1261. procedure Assign(Source: TPersistent); override;
  1262. procedure AutoFitColumns(Animated: Boolean = True; SmartAutoFitType: TSmartAutoFitType = smaUseColumnOption;
  1263. RangeStartCol: Integer = NoColumn; RangeEndCol: Integer = NoColumn); virtual;
  1264. function InHeader(P: TPoint): Boolean; virtual;
  1265. function InHeaderSplitterArea(P: TPoint): Boolean; virtual;
  1266. procedure Invalidate(Column: TVirtualTreeColumn; ExpandToBorder: Boolean = False);
  1267. procedure LoadFromStream(const Stream: TStream); virtual;
  1268. function ResizeColumns(ChangeBy: Integer; RangeStartCol: TColumnIndex; RangeEndCol: TColumnIndex;
  1269. Options: TVTColumnOptions = [coVisible]): Integer;
  1270. procedure RestoreColumns;
  1271. procedure SaveToStream(const Stream: TStream); virtual;
  1272. property DragImage: TVTDragImage read FDragImage;
  1273. property States: THeaderStates read FStates;
  1274. property Treeview: TBaseVirtualTree read FOwner;
  1275. property UseColumns: Boolean read GetUseColumns;
  1276. published
  1277. property AutoSizeIndex: TColumnIndex read FAutoSizeIndex write SetAutoSizeIndex;
  1278. property Background: TColor read FBackground write SetBackground default clBtnFace;
  1279. property Columns: TVirtualTreeColumns read FColumns write SetColumns stored False; // Stored by the owner tree to support VFI.
  1280. property DefaultHeight: Integer read FDefaultHeight write SetDefaultHeight default 19;
  1281. property Font: TFont read FFont write SetFont stored IsFontStored;
  1282. property FixedAreaConstraints: TVTFixedAreaConstraints read FFixedAreaConstraints write FFixedAreaConstraints;
  1283. property Height: Integer read FHeight write SetHeight default 19;
  1284. property Images: TCustomImageList read FImages write SetImages;
  1285. property MainColumn: TColumnIndex read GetMainColumn write SetMainColumn default 0;
  1286. property MaxHeight: Integer read FMaxHeight write SetMaxHeight default 10000;
  1287. property MinHeight: Integer read FMinHeight write SetMinHeight default 10;
  1288. property Options: TVTHeaderOptions read FOptions write SetOptions default [hoColumnResize, hoDrag, hoShowSortGlyphs];
  1289. property ParentFont: Boolean read FParentFont write SetParentFont default False;
  1290. property PopupMenu: TPopupMenu read FPopupMenu write FPopupMenu;
  1291. property SortColumn: TColumnIndex read FSortColumn write SetSortColumn default NoColumn;
  1292. property SortDirection: TSortDirection read FSortDirection write SetSortDirection default sdAscending;
  1293. property Style: TVTHeaderStyle read FStyle write SetStyle default hsThickButtons;
  1294. end;
  1295. TVTHeaderClass = class of TVTHeader;
  1296. // Communication interface between a tree editor and the tree itself (declared as using stdcall in case it
  1297. // is implemented in a (C/C++) DLL). The GUID is not nessecary in Delphi but important for BCB users
  1298. // to allow QueryInterface and _uuidof calls.
  1299. IVTEditLink = interface
  1300. ['{2BE3EAFA-5ACB-45B4-9D9A-B58BCC496E17}']
  1301. function BeginEdit: Boolean; stdcall; // Called when editing actually starts.
  1302. function CancelEdit: Boolean; stdcall; // Called when editing has been cancelled by the tree.
  1303. function EndEdit: Boolean; stdcall; // Called when editing has been finished by the tree.
  1304. function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
  1305. // Called after creation to allow a setup.
  1306. function GetBounds: TRect; stdcall; // Called to get the current size of the edit window
  1307. // (only important if the edit resizes itself).
  1308. procedure ProcessMessage(var Message: TMessage); stdcall;
  1309. // Used to forward messages to the edit window(s)-
  1310. procedure SetBounds(R: TRect); stdcall; // Called to place the editor.
  1311. end;
  1312. // Indicates in the OnUpdating event what state the tree is currently in.
  1313. TVTUpdateState = (
  1314. usBegin, // The tree just entered the update state (BeginUpdate call for the first time).
  1315. usBeginSynch, // The tree just entered the synch update state (BeginSynch call for the first time).
  1316. usSynch, // Begin/EndSynch has been called but the tree did not change the update state.
  1317. usUpdate, // Begin/EndUpdate has been called but the tree did not change the update state.
  1318. usEnd, // The tree just left the update state (EndUpdate called for the last level).
  1319. usEndSynch // The tree just left the synch update state (EndSynch called for the last level).
  1320. );
  1321. // Used during owner draw of the header to indicate which drop mark for the column must be drawn.
  1322. TVTDropMarkMode = (
  1323. dmmNone,
  1324. dmmLeft,
  1325. dmmRight
  1326. );
  1327. // This structure carries all important information about header painting and is used in the advanced header painting.
  1328. THeaderPaintInfo = record
  1329. TargetCanvas: TCanvas;
  1330. Column: TVirtualTreeColumn;
  1331. PaintRectangle: TRect;
  1332. TextRectangle: TRect;
  1333. IsHoverIndex,
  1334. IsDownIndex,
  1335. IsEnabled,
  1336. ShowHeaderGlyph,
  1337. ShowSortGlyph,
  1338. ShowRightBorder: Boolean;
  1339. DropMark: TVTDropMarkMode;
  1340. GlyphPos,
  1341. SortGlyphPos: TPoint;
  1342. end;
  1343. // These elements are used both to query the application, which of them it wants to draw itself and to tell it during
  1344. // painting, which elements must be drawn during the advanced custom draw events.
  1345. THeaderPaintElements = set of (
  1346. hpeBackground,
  1347. hpeDropMark,
  1348. hpeHeaderGlyph,
  1349. hpeSortGlyph,
  1350. hpeText
  1351. );
  1352. // Various events must be handled at different places than they were initiated or need
  1353. // a persistent storage until they are reset.
  1354. TVirtualTreeStates = set of (
  1355. tsCancelHintAnimation, // Set when a new hint is about to show but an old hint is still being animated.
  1356. tsChangePending, // A selection change is pending.
  1357. tsCheckPropagation, // Set during automatic check state propagation.
  1358. tsCollapsing, // A full collapse operation is in progress.
  1359. tsToggleFocusedSelection, // Node selection was modifed using Ctrl-click. Change selection state on next mouse up.
  1360. tsClearPending, // Need to clear the current selection on next mouse move.
  1361. tsClipboardFlushing, // Set during flushing the clipboard to avoid freeing the content.
  1362. tsCopyPending, // Indicates a pending copy operation which needs to be finished.
  1363. tsCutPending, // Indicates a pending cut operation which needs to be finished.
  1364. tsDrawSelPending, // Multiselection only. User held down the left mouse button on a free
  1365. // area and might want to start draw selection.
  1366. tsDrawSelecting, // Multiselection only. Draw selection has actually started.
  1367. tsEditing, // Indicates that an edit operation is currently in progress.
  1368. tsEditPending, // An mouse up start edit if dragging has not started.
  1369. tsExpanding, // A full expand operation is in progress.
  1370. tsNodeHeightTracking, // A node height changing operation is in progress.
  1371. tsNodeHeightTrackPending, // left button is down, user might want to start changing a node's height.
  1372. tsHint, // Set when our hint is visible or soon will be.
  1373. tsInAnimation, // Set if the tree is currently in an animation loop.
  1374. tsIncrementalSearching, // Set when the user starts incremental search.
  1375. tsIncrementalSearchPending, // Set in WM_KEYDOWN to tell to use the char in WM_CHAR for incremental search.
  1376. tsIterating, // Set when IterateSubtree is currently in progress.
  1377. tsKeyCheckPending, // A check operation is under way, initiated by a key press (space key). Ignore mouse.
  1378. tsLeftButtonDown, // Set when the left mouse button is down.
  1379. tsLeftDblClick, // Set when the left mouse button was doubly clicked.
  1380. tsMouseCheckPending, // A check operation is under way, initiated by a mouse click. Ignore space key.
  1381. tsMiddleButtonDown, // Set when the middle mouse button is down.
  1382. tsMiddleDblClick, // Set when the middle mouse button was doubly clicked.
  1383. tsNeedRootCountUpdate, // Set if while loading a root node count is set.
  1384. tsOLEDragging, // OLE dragging in progress.
  1385. tsOLEDragPending, // User has requested to start delayed dragging.
  1386. tsPainting, // The tree is currently painting itself.
  1387. tsRightButtonDown, // Set when the right mouse button is down.
  1388. tsRightDblClick, // Set when the right mouse button was doubly clicked.
  1389. tsPopupMenuShown, // The user clicked the right mouse button, which might cause a popup menu to appear.
  1390. tsScrolling, // Set when autoscrolling is active.
  1391. tsScrollPending, // Set when waiting for the scroll delay time to elapse.
  1392. tsSizing, // Set when the tree window is being resized. This is used to prevent recursive calls
  1393. // due to setting the scrollbars when sizing.
  1394. tsStopValidation, // Cache validation can be stopped (usually because a change has occured meanwhile).
  1395. tsStructureChangePending, // The structure of the tree has been changed while the update was locked.
  1396. tsSynchMode, // Set when the tree is in synch mode, where no timer events are triggered.
  1397. tsThumbTracking, // Stop updating the horizontal scroll bar while dragging the vertical thumb and vice versa.
  1398. tsToggling, // A toggle operation (for some node) is in progress.
  1399. tsUpdateHiddenChildrenNeeded, // Pending update for the hidden children flag after massive visibility changes.
  1400. tsUpdating, // The tree does currently not update its window because a BeginUpdate has not yet ended.
  1401. tsUseCache, // The tree's node caches are validated and non-empty.
  1402. tsUserDragObject, // Signals that the application created an own drag object in OnStartDrag.
  1403. tsUseThemes, // The tree runs under WinXP+, is theme aware and themes are enabled.
  1404. tsValidating, // The tree's node caches are currently validated.
  1405. tsPreviouslySelectedLocked,// The member FPreviouslySelected should not be changed
  1406. tsValidationNeeded, // Something in the structure of the tree has changed. The cache needs validation.
  1407. tsVCLDragging, // VCL drag'n drop in progress.
  1408. tsVCLDragPending, // One-shot flag to avoid clearing the current selection on implicit mouse up for VCL drag.
  1409. tsVCLDragFinished, // Flag to avoid triggering the OnColumnClick event twice
  1410. tsWheelPanning, // Wheel mouse panning is active or soon will be.
  1411. tsWheelScrolling, // Wheel mouse scrolling is active or soon will be.
  1412. tsWindowCreating, // Set during window handle creation to avoid frequent unnecessary updates.
  1413. tsUseExplorerTheme // The tree runs under WinVista+ and is using the explorer theme
  1414. );
  1415. TChangeStates = set of (
  1416. csStopValidation, // Cache validation can be stopped (usually because a change has occured meanwhile).
  1417. csUseCache, // The tree's node caches are validated and non-empty.
  1418. csValidating, // The tree's node caches are currently validated.
  1419. csValidationNeeded // Something in the structure of the tree has changed. The cache needs validation.
  1420. );
  1421. // determines whether and how the drag image is to show
  1422. TVTDragImageKind = (
  1423. diComplete, // show a complete drag image with all columns, only visible columns are shown
  1424. diMainColumnOnly, // show only the main column (the tree column)
  1425. diNoImage // don't show a drag image at all
  1426. );
  1427. // Switch for OLE and VCL drag'n drop. Because it is not possible to have both simultanously.
  1428. TVTDragType = (
  1429. dtOLE,
  1430. dtVCL
  1431. );
  1432. // options which determine what to draw in PaintTree
  1433. TVTInternalPaintOption = (
  1434. poBackground, // draw background image if there is any and it is enabled
  1435. poColumnColor, // erase node's background with the column's color
  1436. poDrawFocusRect, // draw focus rectangle around the focused node
  1437. poDrawSelection, // draw selected nodes with the normal selection color
  1438. poDrawDropMark, // draw drop mark if a node is currently the drop target
  1439. poGridLines, // draw grid lines if enabled
  1440. poMainOnly, // draw only the main column
  1441. poSelectedOnly, // draw only selected nodes
  1442. poUnbuffered // draw directly onto the target canvas; especially useful when printing
  1443. );
  1444. TVTInternalPaintOptions = set of TVTInternalPaintOption;
  1445. // Determines the look of a tree's lines.
  1446. TVTLineStyle = (
  1447. lsCustomStyle, // application provides a line pattern
  1448. lsDotted, // usual dotted lines (default)
  1449. lsSolid // simple solid lines
  1450. );
  1451. // TVTLineType is used during painting a tree
  1452. TVTLineType = (
  1453. ltNone, // no line at all
  1454. ltBottomRight, // a line from bottom to the center and from there to the right
  1455. ltTopDown, // a line from top to bottom
  1456. ltTopDownRight, // a line from top to bottom and from center to the right
  1457. ltRight, // a line from center to the right
  1458. ltTopRight, // a line from bottom to center and from there to the right
  1459. // special styles for alternative drawings of tree lines
  1460. ltLeft, // a line from top to bottom at the left
  1461. ltLeftBottom // a combination of ltLeft and a line at the bottom from left to right
  1462. );
  1463. // Determines how to draw tree lines.
  1464. TVTLineMode = (
  1465. lmNormal, // usual tree lines (as in TTreeview)
  1466. lmBands // looks similar to a Nassi-Schneidermann diagram
  1467. );
  1468. // A collection of line type IDs which is used while painting a node.
  1469. TLineImage = array of TVTLineType;
  1470. TVTScrollIncrement = 1..10000;
  1471. // Export type
  1472. TVTExportType = (
  1473. etRTF, // contentToRTF
  1474. etHTML, // contentToHTML
  1475. etText, // contentToText
  1476. etExcel, // supported by external tools
  1477. etWord, // supported by external tools
  1478. etCustom // supported by external tools
  1479. );
  1480. TVTNodeExportEvent = function (Sender: TBaseVirtualTree; aExportType: TVTExportType; Node: PVirtualNode): Boolean of object;
  1481. TVTColumnExportEvent = procedure (Sender: TBaseVirtualTree; aExportType: TVTExportType; Column: TVirtualTreeColumn) of object;
  1482. TVTTreeExportEvent = procedure(Sender: TBaseVirtualTree; aExportType: TVTExportType) of object;
  1483. // A class to manage scroll bar aspects.
  1484. TScrollBarOptions = class(TPersistent)
  1485. private
  1486. FAlwaysVisible: Boolean;
  1487. FOwner: TBaseVirtualTree;
  1488. FScrollBars: TScrollStyle; // used to hide or show vertical and/or horizontal scrollbar
  1489. FScrollBarStyle: TScrollBarStyle; // kind of scrollbars to use
  1490. FIncrementX,
  1491. FIncrementY: TVTScrollIncrement; // number of pixels to scroll in one step (when auto scrolling)
  1492. procedure SetAlwaysVisible(Value: Boolean);
  1493. procedure SetScrollBars(Value: TScrollStyle);
  1494. procedure SetScrollBarStyle(Value: TScrollBarStyle);
  1495. protected
  1496. function GetOwner: TPersistent; override;
  1497. public
  1498. constructor Create(AOwner: TBaseVirtualTree);
  1499. procedure Assign(Source: TPersistent); override;
  1500. published
  1501. property AlwaysVisible: Boolean read FAlwaysVisible write SetAlwaysVisible default False;
  1502. property HorizontalIncrement: TVTScrollIncrement read FIncrementX write FIncrementX default 20;
  1503. property ScrollBars: TScrollStyle read FScrollBars write SetScrollBars default ssBoth;
  1504. property ScrollBarStyle: TScrollBarStyle read FScrollBarStyle write SetScrollBarStyle default sbmRegular;
  1505. property VerticalIncrement: TVTScrollIncrement read FIncrementY write FIncrementY default 20;
  1506. end;
  1507. // class to collect all switchable colors into one place
  1508. TVTColors = class(TPersistent)
  1509. private
  1510. FOwner: TBaseVirtualTree;
  1511. FColors: array[0..16] of TColor; // [IPK] 15 -> 16
  1512. function GetColor(const Index: Integer): TColor;
  1513. procedure SetColor(const Index: Integer; const Value: TColor);
  1514. function GetBackgroundColor: TColor;
  1515. function GetHeaderFontColor: TColor;
  1516. function GetNodeFontColor: TColor;
  1517. public
  1518. constructor Create(AOwner: TBaseVirtualTree);
  1519. procedure Assign(Source: TPersistent); override;
  1520. property BackGroundColor: TColor read GetBackgroundColor;
  1521. property HeaderFontColor: TColor read GetHeaderFontColor;
  1522. property NodeFontColor: TColor read GetNodeFontColor;
  1523. published
  1524. property BorderColor: TColor index 7 read GetColor write SetColor default clBtnFace;
  1525. property DisabledColor: TColor index 0 read GetColor write SetColor default clBtnShadow;
  1526. property DropMarkColor: TColor index 1 read GetColor write SetColor default clHighlight;
  1527. property DropTargetColor: TColor index 2 read GetColor write SetColor default clHighLight;
  1528. property DropTargetBorderColor: TColor index 11 read GetColor write SetColor default clHighLight;
  1529. property FocusedSelectionColor: TColor index 3 read GetColor write SetColor default clHighLight;
  1530. property FocusedSelectionBorderColor: TColor index 9 read GetColor write SetColor default clHighLight;
  1531. property GridLineColor: TColor index 4 read GetColor write SetColor default clBtnFace;
  1532. property HeaderHotColor: TColor index 14 read GetColor write SetColor default clBtnShadow;
  1533. property HotColor: TColor index 8 read GetColor write SetColor default clWindowText;
  1534. property SelectionRectangleBlendColor: TColor index 12 read GetColor write SetColor default clHighlight;
  1535. property SelectionRectangleBorderColor: TColor index 13 read GetColor write SetColor default clHighlight;
  1536. property SelectionTextColor: TColor index 15 read GetColor write SetColor default clHighlightText;
  1537. property TreeLineColor: TColor index 5 read GetColor write SetColor default clBtnShadow;
  1538. property UnfocusedColor: TColor index 16 read GetColor write SetColor default clBtnFace; // [IPK] Added
  1539. property UnfocusedSelectionColor: TColor index 6 read GetColor write SetColor default clBtnFace;
  1540. property UnfocusedSelectionBorderColor: TColor index 10 read GetColor write SetColor default clBtnFace;
  1541. end;
  1542. // For painting a node and its columns/cells a lot of information must be passed frequently around.
  1543. TVTImageInfo = record
  1544. Index: Integer; // Index in the associated image list.
  1545. XPos, // Horizontal position in the current target canvas.
  1546. YPos: Integer; // Vertical position in the current target canvas.
  1547. Ghosted: Boolean; // Flag to indicate that the image must be drawn slightly lighter.
  1548. Images: TCustomImageList; // The image list to be used for painting.
  1549. end;
  1550. TVTImageInfoIndex = (
  1551. iiNormal,
  1552. iiState,
  1553. iiCheck,
  1554. iiOverlay
  1555. );
  1556. // Options which are used when modifying the scroll offsets.
  1557. TScrollUpdateOptions = set of (
  1558. suoRepaintHeader, // if suoUpdateNCArea is also set then invalidate the header
  1559. suoRepaintScrollBars, // if suoUpdateNCArea is also set then repaint both scrollbars after updating them
  1560. suoScrollClientArea, // scroll and invalidate the proper part of the client area
  1561. suoUpdateNCArea // update non-client area (scrollbars, header)
  1562. );
  1563. // Determines the look of a tree's buttons.
  1564. TVTButtonStyle = (
  1565. bsRectangle, // traditional Windows look (plus/minus buttons)
  1566. bsTriangle // traditional Macintosh look
  1567. );
  1568. // TButtonFillMode is only used when the button style is bsRectangle and determines how to fill the interior.
  1569. TVTButtonFillMode = (
  1570. fmTreeColor, // solid color, uses the tree's background color
  1571. fmWindowColor, // solid color, uses clWindow
  1572. fmShaded, // color gradient, Windows XP style (legacy code, use toThemeAware on Windows XP instead)
  1573. fmTransparent // transparent color, use the item's background color
  1574. );
  1575. TVTPaintInfo = record
  1576. Canvas: TCanvas; // the canvas to paint on
  1577. PaintOptions: TVTInternalPaintOptions; // a copy of the paint options passed to PaintTree
  1578. Node: PVirtualNode; // the node to paint
  1579. Column: TColumnIndex; // the node's column index to paint
  1580. Position: TColumnPosition; // the column position of the node
  1581. CellRect, // the node cell
  1582. ContentRect: TRect; // the area of the cell used for the node's content
  1583. NodeWidth: Integer; // the actual node width
  1584. Alignment: TAlignment; // how to align within the node rectangle
  1585. CaptionAlignment: TAlignment; // how to align text within the caption rectangle
  1586. BidiMode: TBidiMode; // directionality to be used for painting
  1587. BrushOrigin: TPoint; // the alignment for the brush used to draw dotted lines
  1588. ImageInfo: array[TVTImageInfoIndex] of TVTImageInfo; // info about each possible node image
  1589. end;
  1590. // Method called by the Animate routine for each animation step.
  1591. TVTAnimationCallback = function(Step, StepSize: Integer; Data: Pointer): Boolean of object;
  1592. TVTIncrementalSearch = (
  1593. isAll, // search every node in tree, initialize if necessary
  1594. isNone, // disable incremental search
  1595. isInitializedOnly, // search only initialized nodes, skip others
  1596. isVisibleOnly // search only visible nodes, initialize if necessary
  1597. );
  1598. // Determines which direction to use when advancing nodes during an incremental search.
  1599. TVTSearchDirection = (
  1600. sdForward,
  1601. sdBackward
  1602. );
  1603. // Determines where to start incremental searching for each key press.
  1604. TVTSearchStart = (
  1605. ssAlwaysStartOver, // always use the first/last node (depending on direction) to search from
  1606. ssLastHit, // use the last found node
  1607. ssFocusedNode // use the currently focused node
  1608. );
  1609. // Determines how to use the align member of a node.
  1610. TVTNodeAlignment = (
  1611. naFromBottom, // the align member specifies amount of units (usually pixels) from top border of the node
  1612. naFromTop, // align is to be measured from bottom
  1613. naProportional // align is to be measure in percent of the entire node height and relative to top
  1614. );
  1615. // Determines how to draw the selection rectangle used for draw selection.
  1616. TVTDrawSelectionMode = (
  1617. smDottedRectangle, // same as DrawFocusRect
  1618. smBlendedRectangle // alpha blending, uses special colors (see TVTColors)
  1619. );
  1620. // Determines for which purpose the cell paint event is called.
  1621. TVTCellPaintMode = (
  1622. cpmPaint, // painting the cell
  1623. cpmGetContentMargin // getting cell content margin
  1624. );
  1625. // Determines which sides of the cell content margin should be considered.
  1626. TVTCellContentMarginType = (
  1627. ccmtAllSides, // consider all sides
  1628. ccmtTopLeftOnly, // consider top margin and left margin only
  1629. ccmtBottomRightOnly // consider bottom margin and right margin only
  1630. );
  1631. TClipboardFormats = class(TStringList)
  1632. private
  1633. FOwner: TBaseVirtualTree;
  1634. public
  1635. constructor Create(AOwner: TBaseVirtualTree); virtual;
  1636. function Add(const S: string): Integer; override;
  1637. procedure Insert(Index: Integer; const S: string); override;
  1638. property Owner: TBaseVirtualTree read FOwner;
  1639. end;
  1640. // ----- Event prototypes:
  1641. // node enumeration
  1642. {$if CompilerVersion >= 20}
  1643. TVTGetNodeProc = reference to procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Data: Pointer; var Abort: Boolean);
  1644. {$else}
  1645. TVTGetNodeProc = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Data: Pointer; var Abort: Boolean) of object;
  1646. {$ifend}
  1647. // node events
  1648. TVTChangingEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; var Allowed: Boolean) of object;
  1649. TVTCheckChangingEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; var NewState: TCheckState;
  1650. var Allowed: Boolean) of object;
  1651. TVTChangeEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode) of object;
  1652. TVTStructureChangeEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Reason: TChangeReason) of object;
  1653. TVTEditCancelEvent = procedure(Sender: TBaseVirtualTree; Column: TColumnIndex) of object;
  1654. TVTEditChangingEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
  1655. var Allowed: Boolean) of object;
  1656. TVTEditChangeEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex) of object;
  1657. TVTFreeNodeEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode) of object;
  1658. TVTFocusChangingEvent = procedure(Sender: TBaseVirtualTree; OldNode, NewNode: PVirtualNode; OldColumn,
  1659. NewColumn: TColumnIndex; var Allowed: Boolean) of object;
  1660. TVTFocusChangeEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex) of object;
  1661. TVTAddToSelectionEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode) of object;
  1662. TVTRemoveFromSelectionEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode) of object;
  1663. TVTGetImageEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
  1664. var Ghosted: Boolean; var ImageIndex: Integer) of object;
  1665. TVTGetImageExEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
  1666. var Ghosted: Boolean; var ImageIndex: Integer; var ImageList: TCustomImageList) of object;
  1667. TVTGetImageTextEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
  1668. var ImageText: UnicodeString) of object;
  1669. TVTHotNodeChangeEvent = procedure(Sender: TBaseVirtualTree; OldNode, NewNode: PVirtualNode) of object;
  1670. TVTInitChildrenEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; var ChildCount: Cardinal) of object;
  1671. TVTInitNodeEvent = procedure(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode;
  1672. var InitialStates: TVirtualNodeInitStates) of object;
  1673. TVTPopupEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; const P: TPoint;
  1674. var AskParent: Boolean; var PopupMenu: TPopupMenu) of object;
  1675. TVTHelpContextEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
  1676. var HelpContext: Integer) of object;
  1677. TVTCreateEditorEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
  1678. out EditLink: IVTEditLink) of object;
  1679. TVTSaveTreeEvent = procedure(Sender: TBaseVirtualTree; Stream: TStream) of object;
  1680. TVTSaveNodeEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Stream: TStream) of object;
  1681. // header/column events
  1682. TVTHeaderClickEvent = procedure(Sender: TVTHeader; HitInfo: TVTHeaderHitInfo) of object;
  1683. TVTHeaderMouseEvent = procedure(Sender: TVTHeader; Button: TMouseButton; Shift: TShiftState; X, Y: Integer) of object;
  1684. TVTHeaderMouseMoveEvent = procedure(Sender: TVTHeader; Shift: TShiftState; X, Y: Integer) of object;
  1685. TVTBeforeHeaderHeightTrackingEvent = procedure(Sender: TVTHeader; Shift: TShiftState) of object;
  1686. TVTAfterHeaderHeightTrackingEvent = procedure(Sender: TVTHeader) of object;
  1687. TVTHeaderHeightTrackingEvent = procedure(Sender: TVTHeader; var P: TPoint; Shift: TShiftState; var Allowed: Boolean) of object;
  1688. TVTHeaderHeightDblClickResizeEvent = procedure(Sender: TVTHeader; var P: TPoint; Shift: TShiftState; var Allowed: Boolean) of object;
  1689. TVTHeaderNotifyEvent = procedure(Sender: TVTHeader; Column: TColumnIndex) of object;
  1690. TVTHeaderDraggingEvent = procedure(Sender: TVTHeader; Column: TColumnIndex; var Allowed: Boolean) of object;
  1691. TVTHeaderDraggedEvent = procedure(Sender: TVTHeader; Column: TColumnIndex; OldPosition: Integer) of object;
  1692. TVTHeaderDraggedOutEvent = procedure(Sender: TVTHeader; Column: TColumnIndex; DropPosition: TPoint) of object;
  1693. TVTHeaderPaintEvent = procedure(Sender: TVTHeader; HeaderCanvas: TCanvas; Column: TVirtualTreeColumn; R: TRect; Hover,
  1694. Pressed: Boolean; DropMark: TVTDropMarkMode) of object;
  1695. TVTHeaderPaintQueryElementsEvent = procedure(Sender: TVTHeader; var PaintInfo: THeaderPaintInfo;
  1696. var Elements: THeaderPaintElements) of object;
  1697. TVTAdvancedHeaderPaintEvent = procedure(Sender: TVTHeader; var PaintInfo: THeaderPaintInfo;
  1698. const Elements: THeaderPaintElements) of object;
  1699. TVTBeforeAutoFitColumnsEvent = procedure(Sender: TVTHeader; var SmartAutoFitType: TSmartAutoFitType) of object;
  1700. TVTBeforeAutoFitColumnEvent = procedure(Sender: TVTHeader; Column: TColumnIndex; var SmartAutoFitType: TSmartAutoFitType;
  1701. var Allowed: Boolean) of object;
  1702. TVTAfterAutoFitColumnEvent = procedure(Sender: TVTHeader; Column: TColumnIndex) of object;
  1703. TVTAfterAutoFitColumnsEvent = procedure(Sender: TVTHeader) of object;
  1704. TVTColumnClickEvent = procedure (Sender: TBaseVirtualTree; Column: TColumnIndex; Shift: TShiftState) of object;
  1705. TVTColumnDblClickEvent = procedure (Sender: TBaseVirtualTree; Column: TColumnIndex; Shift: TShiftState) of object;
  1706. TVTColumnWidthDblClickResizeEvent = procedure(Sender: TVTHeader; Column: TColumnIndex; Shift: TShiftState; P: TPoint;
  1707. var Allowed: Boolean) of object;
  1708. TVTBeforeColumnWidthTrackingEvent = procedure(Sender: TVTHeader; Column: TColumnIndex; Shift: TShiftState) of object;
  1709. TVTAfterColumnWidthTrackingEvent = procedure(Sender: TVTHeader; Column: TColumnIndex) of object;
  1710. TVTColumnWidthTrackingEvent = procedure(Sender: TVTHeader; Column: TColumnIndex; Shift: TShiftState; var TrackPoint: TPoint; P: TPoint;
  1711. var Allowed: Boolean) of object;
  1712. TVTGetHeaderCursorEvent = procedure(Sender: TVTHeader; var Cursor: HCURSOR) of object;
  1713. TVTBeforeGetMaxColumnWidthEvent = procedure(Sender: TVTHeader; Column: TColumnIndex; var UseSmartColumnWidth: Boolean) of object;
  1714. TVTAfterGetMaxColumnWidthEvent = procedure(Sender: TVTHeader; Column: TColumnIndex; var MaxWidth: Integer) of object;
  1715. TVTCanSplitterResizeColumnEvent = procedure(Sender: TVTHeader; P: TPoint; Column: TColumnIndex; var Allowed: Boolean) of object;
  1716. TVTCanSplitterResizeHeaderEvent = procedure(Sender: TVTHeader; P: TPoint; var Allowed: Boolean) of object;
  1717. // move, copy and node tracking events
  1718. TVTNodeMovedEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode) of object;
  1719. TVTNodeMovingEvent = procedure(Sender: TBaseVirtualTree; Node, Target: PVirtualNode;
  1720. var Allowed: Boolean) of object;
  1721. TVTNodeCopiedEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode) of object;
  1722. TVTNodeCopyingEvent = procedure(Sender: TBaseVirtualTree; Node, Target: PVirtualNode;
  1723. var Allowed: Boolean) of object;
  1724. TVTNodeClickEvent = procedure(Sender: TBaseVirtualTree; const HitInfo: THitInfo) of object;
  1725. TVTNodeHeightTrackingEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; Shift: TShiftState;
  1726. var TrackPoint: TPoint; P: TPoint; var Allowed: Boolean) of object;
  1727. TVTNodeHeightDblClickResizeEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
  1728. Shift: TShiftState; P: TPoint; var Allowed: Boolean) of object;
  1729. TVTCanSplitterResizeNodeEvent = procedure(Sender: TBaseVirtualTree; P: TPoint; Node: PVirtualNode;
  1730. Column: TColumnIndex; var Allowed: Boolean) of object;
  1731. // drag'n drop/OLE events
  1732. TVTCreateDragManagerEvent = procedure(Sender: TBaseVirtualTree; out DragManager: IVTDragManager) of object;
  1733. TVTCreateDataObjectEvent = procedure(Sender: TBaseVirtualTree; out IDataObject: IDataObject) of object;
  1734. TVTDragAllowedEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
  1735. var Allowed: Boolean) of object;
  1736. TVTDragOverEvent = procedure(Sender: TBaseVirtualTree; Source: TObject; Shift: TShiftState; State: TDragState;
  1737. Pt: TPoint; Mode: TDropMode; var Effect: Integer; var Accept: Boolean) of object;
  1738. TVTDragDropEvent = procedure(Sender: TBaseVirtualTree; Source: TObject; DataObject: IDataObject;
  1739. Formats: TFormatArray; Shift: TShiftState; Pt: TPoint; var Effect: Integer; Mode: TDropMode) of object;
  1740. TVTRenderOLEDataEvent = procedure(Sender: TBaseVirtualTree; const FormatEtcIn: TFormatEtc; out Medium: TStgMedium;
  1741. ForClipboard: Boolean; var Result: HRESULT) of object;
  1742. TVTGetUserClipboardFormatsEvent = procedure(Sender: TBaseVirtualTree; var Formats: TFormatEtcArray) of object;
  1743. // paint events
  1744. TVTBeforeItemEraseEvent = procedure(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; Node: PVirtualNode; ItemRect: TRect;
  1745. var ItemColor: TColor; var EraseAction: TItemEraseAction) of object;
  1746. TVTAfterItemEraseEvent = procedure(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; Node: PVirtualNode;
  1747. ItemRect: TRect) of object;
  1748. TVTBeforeItemPaintEvent = procedure(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; Node: PVirtualNode;
  1749. ItemRect: TRect; var CustomDraw: Boolean) of object;
  1750. TVTAfterItemPaintEvent = procedure(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; Node: PVirtualNode;
  1751. ItemRect: TRect) of object;
  1752. TVTBeforeCellPaintEvent = procedure(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; Node: PVirtualNode;
  1753. Column: TColumnIndex; CellPaintMode: TVTCellPaintMode; CellRect: TRect; var ContentRect: TRect) of object;
  1754. TVTAfterCellPaintEvent = procedure(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; Node: PVirtualNode;
  1755. Column: TColumnIndex; CellRect: TRect) of object;
  1756. TVTPaintEvent = procedure(Sender: TBaseVirtualTree; TargetCanvas: TCanvas) of object;
  1757. TVTBackgroundPaintEvent = procedure(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; R: TRect;
  1758. var Handled: Boolean) of object;
  1759. TVTGetLineStyleEvent = procedure(Sender: TBaseVirtualTree; var Bits: Pointer) of object;
  1760. TVTMeasureItemEvent = procedure(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; Node: PVirtualNode;
  1761. var NodeHeight: Integer) of object;
  1762. // search, sort
  1763. TVTCompareEvent = procedure(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; Column: TColumnIndex;
  1764. var Result: Integer) of object;
  1765. TVTIncrementalSearchEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; const SearchText: UnicodeString;
  1766. var Result: Integer) of object;
  1767. // operations
  1768. TVTOperationEvent = procedure(Sender: TBaseVirtualTree; OperationKind: TVTOperationKind) of object;
  1769. TVTHintKind = (vhkText, vhkOwnerDraw);
  1770. TVTHintKindEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; var Kind: TVTHintKind) of object;
  1771. TVTDrawHintEvent = procedure(Sender: TBaseVirtualTree; HintCanvas: TCanvas; Node: PVirtualNode; R: TRect; Column: TColumnIndex) of object;
  1772. TVTGetHintSizeEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; var R: TRect) of object;
  1773. // miscellaneous
  1774. TVTBeforeDrawLineImageEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Level: Integer; var PosX: Integer) of object;
  1775. TVTGetNodeDataSizeEvent = procedure(Sender: TBaseVirtualTree; var NodeDataSize: Integer) of object;
  1776. TVTKeyActionEvent = procedure(Sender: TBaseVirtualTree; var CharCode: Word; var Shift: TShiftState;
  1777. var DoDefault: Boolean) of object;
  1778. TVTScrollEvent = procedure(Sender: TBaseVirtualTree; DeltaX, DeltaY: Integer) of object;
  1779. TVTUpdatingEvent = procedure(Sender: TBaseVirtualTree; State: TVTUpdateState) of object;
  1780. TVTGetCursorEvent = procedure(Sender: TBaseVirtualTree; var Cursor: TCursor) of object;
  1781. TVTStateChangeEvent = procedure(Sender: TBaseVirtualTree; Enter, Leave: TVirtualTreeStates) of object;
  1782. TVTGetCellIsEmptyEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
  1783. var IsEmpty: Boolean) of object;
  1784. TVTScrollBarShowEvent = procedure(Sender: TBaseVirtualTree; Bar: Integer; Show: Boolean) of object;
  1785. // Helper types for node iterations.
  1786. TGetFirstNodeProc = function: PVirtualNode of object;
  1787. TGetNextNodeProc = function(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode of object;
  1788. TVZVirtualNodeEnumerationMode = (
  1789. vneAll,
  1790. vneChecked,
  1791. vneChild,
  1792. vneCutCopy,
  1793. vneInitialized,
  1794. vneLeaf,
  1795. vneLevel,
  1796. vneNoInit,
  1797. vneSelected,
  1798. vneVisible,
  1799. vneVisibleChild,
  1800. vneVisibleNoInitChild,
  1801. vneVisibleNoInit
  1802. );
  1803. PVTVirtualNodeEnumeration = ^TVTVirtualNodeEnumeration;
  1804. TVTVirtualNodeEnumerator = {$if CompilerVersion >= 18}record{$else}class{$ifend}
  1805. private
  1806. FNode: PVirtualNode;
  1807. FCanModeNext: Boolean;
  1808. FEnumeration: PVTVirtualNodeEnumeration;
  1809. function GetCurrent: PVirtualNode; {$if CompilerVersion >= 18}inline;{$ifend}
  1810. public
  1811. function MoveNext: Boolean; {$if CompilerVersion >= 18}inline;{$ifend}
  1812. property Current: PVirtualNode read GetCurrent;
  1813. end;
  1814. TVTVirtualNodeEnumeration = {$if CompilerVersion >= 18}record{$else}object{$ifend}
  1815. private
  1816. FMode: TVZVirtualNodeEnumerationMode;
  1817. FTree: TBaseVirtualTree;
  1818. // GetNextXxx parameters:
  1819. FConsiderChildrenAbove: Boolean;
  1820. FNode: PVirtualNode;
  1821. FNodeLevel: Cardinal;
  1822. FState: TCheckState;
  1823. FIncludeFiltered: Boolean;
  1824. public
  1825. function GetEnumerator: TVTVirtualNodeEnumerator;
  1826. private
  1827. function GetNext(Node: PVirtualNode): PVirtualNode;
  1828. end;
  1829. // XE2+ VCL Style
  1830. {$if CompilerVersion >= 23 }
  1831. TVclStyleScrollBarsHook = class(TMouseTrackControlStyleHook)
  1832. strict private type
  1833. {$REGION 'TVclStyleScrollBarWindow'}
  1834. TVclStyleScrollBarWindow = class(TWinControl)strict private FScrollBarWindowOwner: TVclStyleScrollBarsHook;
  1835. FScrollBarVertical: Boolean;
  1836. FScrollBarVisible: Boolean;
  1837. FScrollBarEnabled: Boolean;
  1838. procedure WMNCHitTest(var Msg: TWMNCHitTest); message WM_NCHITTEST;
  1839. procedure WMEraseBkgnd(var Msg: TMessage); message WM_ERASEBKGND;
  1840. procedure WMPaint(var Msg: TWMPaint); message WM_PAINT;
  1841. strict protected
  1842. procedure CreateParams(var Params: TCreateParams);
  1843. override;
  1844. public
  1845. constructor Create(AOwner: TComponent);
  1846. override;
  1847. property ScrollBarWindowOwner: TVclStyleScrollBarsHook read FScrollBarWindowOwner write FScrollBarWindowOwner;
  1848. property ScrollBarVertical: Boolean read FScrollBarVertical write FScrollBarVertical;
  1849. property ScrollBarVisible: Boolean read FScrollBarVisible write FScrollBarVisible;
  1850. property ScrollBarEnabled: Boolean read FScrollBarEnabled write FScrollBarEnabled;
  1851. end;
  1852. {$ENDREGION}
  1853. private
  1854. FHorzScrollBarDownButtonRect: TRect;
  1855. FHorzScrollBarDownButtonState: TThemedScrollBar;
  1856. FHorzScrollBarRect: TRect;
  1857. FHorzScrollBarSliderState: TThemedScrollBar;
  1858. FHorzScrollBarSliderTrackRect: TRect;
  1859. FHorzScrollBarUpButtonRect: TRect;
  1860. FHorzScrollBarUpButtonState: TThemedScrollBar;
  1861. FHorzScrollBarWindow: TVclStyleScrollBarWindow;
  1862. FLeftMouseButtonDown: Boolean;
  1863. FPrevScrollPos: Integer;
  1864. FScrollPos: Single;
  1865. FVertScrollBarDownButtonRect: TRect;
  1866. FVertScrollBarDownButtonState: TThemedScrollBar;
  1867. FVertScrollBarRect: TRect;
  1868. FVertScrollBarSliderState: TThemedScrollBar;
  1869. FVertScrollBarSliderTrackRect: TRect;
  1870. FVertScrollBarUpButtonRect: TRect;
  1871. FVertScrollBarUpButtonState: TThemedScrollBar;
  1872. FVertScrollBarWindow: TVclStyleScrollBarWindow;
  1873. procedure CMUpdateVclStyleScrollBars(var Message: TMessage); message CM_UPDATE_VCLSTYLE_SCROLLBARS;
  1874. procedure WMKeyDown(var Msg: TMessage); message WM_KEYDOWN;
  1875. procedure WMKeyUp(var Msg: TMessage); message WM_KEYUP;
  1876. procedure WMLButtonDown(var Msg: TWMMouse); message WM_LBUTTONDOWN;
  1877. procedure WMLButtonUp(var Msg: TWMMouse); message WM_LBUTTONUP;
  1878. procedure WMNCLButtonDown(var Msg: TWMMouse); message WM_NCLBUTTONDOWN;
  1879. procedure WMNCMouseMove(var Msg: TWMMouse); message WM_NCMOUSEMOVE;
  1880. procedure WMNCLButtonUp(var Msg: TWMMouse); message WM_NCLBUTTONUP;
  1881. procedure WMNCPaint(var Msg: TMessage); message WM_NCPAINT;
  1882. procedure WMMouseMove(var Msg: TWMMouse); message WM_MOUSEMOVE;
  1883. procedure WMMouseWheel(var Msg: TMessage); message WM_MOUSEWHEEL;
  1884. procedure WMVScroll(var Msg: TMessage); message WM_VSCROLL;
  1885. procedure WMHScroll(var Msg: TMessage); message WM_HSCROLL;
  1886. procedure WMCaptureChanged(var Msg: TMessage); message WM_CAPTURECHANGED;
  1887. procedure WMNCLButtonDblClk(var Msg: TWMMouse); message WM_NCLBUTTONDBLCLK;
  1888. procedure WMSize(var Msg: TMessage); message WM_SIZE;
  1889. procedure WMMove(var Msg: TMessage); message WM_MOVE;
  1890. procedure WMPosChanged(var Msg: TMessage); message WM_WINDOWPOSCHANGED;
  1891. protected
  1892. procedure CalcScrollBarsRect; virtual;
  1893. procedure DrawHorzScrollBar(DC: HDC); virtual;
  1894. procedure DrawVertScrollBar(DC: HDC); virtual;
  1895. function GetHorzScrollBarSliderRect: TRect;
  1896. function GetVertScrollBarSliderRect: TRect;
  1897. procedure MouseLeave; override;
  1898. procedure PaintScrollBars; virtual;
  1899. function PointInTreeHeader(const P: TPoint): Boolean;
  1900. procedure UpdateScrollBarWindow;
  1901. public
  1902. constructor Create(AControl: TWinControl); override;
  1903. destructor Destroy; override;
  1904. end;
  1905. {$ifend}
  1906. // ----- TBaseVirtualTree
  1907. TBaseVirtualTree = class(TCustomControl)
  1908. private
  1909. FBorderStyle: TBorderStyle;
  1910. FHeader: TVTHeader;
  1911. FRoot: PVirtualNode;
  1912. FDefaultNodeHeight,
  1913. FIndent: Cardinal;
  1914. FOptions: TCustomVirtualTreeOptions;
  1915. FUpdateCount: Cardinal; // update stopper, updates of the tree control are only done if = 0
  1916. FSynchUpdateCount: Cardinal; // synchronizer, causes all events which are usually done via timers
  1917. // to happen immediately, regardless of the normal update state
  1918. FNodeDataSize: Integer; // number of bytes to allocate with each node (in addition to its base
  1919. // structure and the internal data), if -1 then do callback
  1920. FStates: TVirtualTreeStates; // various active/pending states the tree needs to consider
  1921. FLastSelected,
  1922. FFocusedNode: PVirtualNode;
  1923. FEditColumn, // column to be edited (focused node)
  1924. FFocusedColumn: TColumnIndex; // NoColumn if no columns are active otherwise the last hit column of
  1925. // the currently focused node
  1926. FHeightTrackPoint: TPoint; // Starting point of a node's height changing operation.
  1927. FHeightTrackNode: PVirtualNode; // Node which height is being changed.
  1928. FHeightTrackColumn: TColumnIndex; // Initial column where the height changing operation takes place.
  1929. FScrollDirections: TScrollDirections; // directions to scroll client area into depending on mouse position
  1930. FLastStructureChangeReason: TChangeReason; // Used for delayed structure change event.
  1931. FLastStructureChangeNode, // dito
  1932. FLastChangedNode, // used for delayed change event
  1933. FCurrentHotNode: PVirtualNode; // Node over which the mouse is hovering.
  1934. FCurrentHotColumn: TColumnIndex; // Column over which the mouse is hovering.
  1935. FHotNodeButtonHit: Boolean; // Indicates wether the mouse is hovering over the hot node's button.
  1936. FLastSelRect,
  1937. FNewSelRect: TRect; // used while doing draw selection
  1938. FHotCursor: TCursor; // can be set to additionally indicate the current hot node
  1939. FAnimationType: THintAnimationType; // none, fade in, slide in animation (just like those animations used
  1940. // in Win98 (slide) and Windows 2000 (fade))
  1941. FHintMode: TVTHintMode; // determines the kind of the hint window
  1942. FHintData: TVTHintData; // used while preparing the hint window
  1943. FChangeDelay: Cardinal; // used to delay OnChange event
  1944. FEditDelay: Cardinal; // determines time to elapse before a node goes into edit mode
  1945. FPositionCache: TCache; // array which stores node references ordered by vertical positions
  1946. // (see also DoValidateCache for more information)
  1947. FVisibleCount: Cardinal; // number of currently visible nodes
  1948. FStartIndex: Cardinal; // index to start validating cache from
  1949. FSelection: TNodeArray; // list of currently selected nodes
  1950. FSelectionCount: Integer; // number of currently selected nodes (size of FSelection might differ)
  1951. FSelectionLocked: Boolean; // prevents the tree from changing the selection
  1952. FRangeAnchor: PVirtualNode; // anchor node for selection with the keyboard, determines start of a
  1953. // selection range
  1954. FCheckNode: PVirtualNode; // node which "captures" a check event
  1955. FPendingCheckState: TCheckState; // the new state the check node will get if all went fine
  1956. FCheckPropagationCount: Cardinal; // nesting level of check propagation (WL, 05.02.2004)
  1957. FLastSelectionLevel: Integer; // keeps the last node level for constrained multiselection
  1958. FDrawSelShiftState: TShiftState; // keeps the initial shift state when the user starts selection with
  1959. // the mouse
  1960. FEditLink: IVTEditLink; // used to comunicate with an application defined editor
  1961. FTempNodeCache: TNodeArray; // used at various places to hold temporarily a bunch of node refs.
  1962. FTempNodeCount: Cardinal; // number of nodes in FTempNodeCache
  1963. FBackground: TPicture; // A background image loadable at design and runtime.
  1964. FMargin: Integer; // horizontal border distance
  1965. FTextMargin: Integer; // space between the node's text and its horizontal bounds
  1966. FBackgroundOffsetX,
  1967. FBackgroundOffsetY: Integer; // used to fine tune the position of the background image
  1968. FAnimationDuration: Cardinal; // specifies how long an animation shall take (expanding, hint)
  1969. FWantTabs: Boolean; // If True then the tree also consumes the tab key.
  1970. FNodeAlignment: TVTNodeAlignment; // determines how to interpret the align member of a node
  1971. FHeaderRect: TRect; // Space which the header currently uses in the control (window coords).
  1972. FLastHintRect: TRect; // Area which the mouse must leave to reshow a hint.
  1973. FUpdateRect: TRect;
  1974. FEmptyListMessage: UnicodeString; // Optional message that will be displayed if no nodes exist in the control.
  1975. // paint support and images
  1976. FPlusBM,
  1977. FMinusBM, // small bitmaps used for tree buttons
  1978. FHotPlusBM,
  1979. FHotMinusBM: TBitmap; // small bitmaps used for hot tree buttons
  1980. FImages, // normal images in the tree
  1981. FStateImages, // state images in the tree
  1982. FCustomCheckImages: TCustomImageList; // application defined check images
  1983. FCheckImageKind: TCheckImageKind; // light or dark, cross marks or tick marks
  1984. FCheckImages: TCustomImageList; // Reference to global image list to be used for the check images.
  1985. FImageChangeLink,
  1986. FStateChangeLink,
  1987. FCustomCheckChangeLink: TChangeLink; // connections to the image lists
  1988. FOldFontChange: TNotifyEvent; // helper method pointer for tracking font changes in the off screen buffer
  1989. FColors: TVTColors; // class comprising all customizable colors in the tree
  1990. FButtonStyle: TVTButtonStyle; // style of the tree buttons
  1991. FButtonFillMode: TVTButtonFillMode; // for rectangular tree buttons only: how to fill them
  1992. FLineStyle: TVTLineStyle; // style of the tree lines
  1993. FLineMode: TVTLineMode; // tree lines or bands etc.
  1994. FDottedBrush: HBRUSH; // used to paint dotted lines without special pens
  1995. FSelectionCurveRadius: Cardinal; // radius for rounded selection rectangles
  1996. FSelectionBlendFactor: Byte; // Determines the factor by which the selection rectangle is to be
  1997. // faded if enabled.
  1998. FDrawSelectionMode: TVTDrawSelectionMode; // determines the paint mode for draw selection
  1999. // alignment and directionality support
  2000. FAlignment: TAlignment; // default alignment of the tree if no columns are shown
  2001. // drag'n drop and clipboard support
  2002. FDragImageKind: TVTDragImageKind; // determines whether or not and what to show in the drag image
  2003. FDragOperations: TDragOperations; // determines which operations are allowed during drag'n drop
  2004. FDragThreshold: Integer; // used to determine when to actually start a drag'n drop operation
  2005. FDragManager: IVTDragManager; // drag'n drop, cut'n paste
  2006. FDropTargetNode: PVirtualNode; // node currently selected as drop target
  2007. FLastDropMode: TDropMode; // set while dragging and used to track changes
  2008. FDragSelection: TNodeArray; // temporary copy of FSelection used during drag'n drop
  2009. FLastDragEffect: LongInt; // The last executed drag effect
  2010. FDragType: TVTDragType; // used to switch between OLE and VCL drag'n drop
  2011. FDragImage: TVTDragImage; // drag image management
  2012. FDragWidth,
  2013. FDragHeight: Integer; // size of the drag image, the larger the more CPU power is needed
  2014. FClipboardFormats: TClipboardFormats; // a list of clipboard format descriptions enabled for this tree
  2015. FLastVCLDragTarget: PVirtualNode; // A node cache for VCL drag'n drop (keywords: DragLeave on DragDrop).
  2016. FVCLDragEffect: Integer; // A cache for VCL drag'n drop to keep the current drop effect.
  2017. // scroll support
  2018. FScrollBarOptions: TScrollBarOptions; // common properties of horizontal and vertical scrollbar
  2019. FAutoScrollInterval: TAutoScrollInterval; // determines speed of auto scrolling
  2020. FAutoScrollDelay: Cardinal; // amount of milliseconds to wait until autoscrolling becomes active
  2021. FAutoExpandDelay: Cardinal; // amount of milliseconds to wait until a node is expanded if it is the
  2022. // drop target
  2023. FOffsetX: Integer;
  2024. FOffsetY: Integer; // Determines left and top scroll offset.
  2025. FEffectiveOffsetX: Integer; // Actual position of the horizontal scroll bar (varies depending on bidi mode).
  2026. FRangeX,
  2027. FRangeY: Cardinal; // current virtual width and height of the tree
  2028. FBottomSpace: Cardinal; // Extra space below the last node.
  2029. FDefaultPasteMode: TVTNodeAttachMode; // Used to determine where to add pasted nodes to.
  2030. FSingletonNodeArray: TNodeArray; // Contains only one element for quick addition of single nodes
  2031. // to the selection.
  2032. FDragScrollStart: Cardinal; // Contains the start time when a tree does auto scrolling as drop target.
  2033. // search
  2034. FIncrementalSearch: TVTIncrementalSearch; // Used to determine whether and how incremental search is to be used.
  2035. FSearchTimeout: Cardinal; // Number of milliseconds after which to stop incremental searching.
  2036. FSearchBuffer: UnicodeString; // Collects a sequence of keypresses used to do incremental searching.
  2037. FLastSearchNode: PVirtualNode; // Reference to node which was last found as search fit.
  2038. FSearchDirection: TVTSearchDirection; // Direction to incrementally search the tree.
  2039. FSearchStart: TVTSearchStart; // Where to start iteration on each key press.
  2040. // miscellanous
  2041. FTotalInternalDataSize: Cardinal; // Cache of the sum of the necessary internal data size for all tree
  2042. // classes derived from this base class.
  2043. FPanningWindow: HWND; // Helper window for wheel panning
  2044. FPanningCursor: HCURSOR; // Current wheel panning cursor.
  2045. FPanningImage: TBitmap; // A little 32x32 bitmap to indicate the panning reference point.
  2046. FLastClickPos: TPoint; // Used for retained drag start and wheel mouse scrolling.
  2047. FOperationCount: Cardinal; // Counts how many nested long-running operations are in progress.
  2048. FOperationCanceled: Boolean; // Used to indicate that a long-running operation should be canceled.
  2049. FChangingTheme: Boolean; // Used to indicate that a theme change is goi ng on
  2050. FNextNodeToSelect: PVirtualNode; // Next tree node that we would like to select if the current one gets deleted or looses selection for other reasons.
  2051. // MSAA support
  2052. FAccessible: IAccessible; // The IAccessible interface to the window itself.
  2053. FAccessibleItem: IAccessible; // The IAccessible to the item that currently has focus.
  2054. FAccessibleName: string; // The name the window is given for screen readers.
  2055. // export
  2056. FOnBeforeNodeExport: TVTNodeExportEvent; // called before exporting a node
  2057. FOnNodeExport: TVTNodeExportEvent;
  2058. FOnAfterNodeExport: TVTNodeExportEvent; // called after exporting a node
  2059. FOnBeforeColumnExport: TVTColumnExportEvent; // called before exporting a column
  2060. FOnColumnExport: TVTColumnExportEvent;
  2061. FOnAfterColumnExport: TVTColumnExportEvent; // called after exporting a column
  2062. FOnBeforeTreeExport: TVTTreeExportEvent; // called before starting the export
  2063. FOnAfterTreeExport: TVTTreeExportEvent; // called after finishing the export
  2064. FOnBeforeHeaderExport: TVTTreeExportEvent; // called before exporting the header
  2065. FOnAfterHeaderExport: TVTTreeExportEvent; // called after exporting the header
  2066. // common events
  2067. FOnChange: TVTChangeEvent; // selection change
  2068. FOnStructureChange: TVTStructureChangeEvent; // structural change like adding nodes etc.
  2069. FOnInitChildren: TVTInitChildrenEvent; // called when a node's children are needed (expanding etc.)
  2070. FOnInitNode: TVTInitNodeEvent; // called when a node needs to be initialized (child count etc.)
  2071. FOnFreeNode: TVTFreeNodeEvent; // called when a node is about to be destroyed, user data can and should
  2072. // be freed in this event
  2073. FOnGetImage: TVTGetImageEvent; // Used to retrieve the image index of a given node.
  2074. FOnGetImageEx: TVTGetImageExEvent; // Used to retrieve the image index of a given node along with a custom
  2075. // image list.
  2076. FOnGetImageText: TVTGetImageTextEvent; // Used to retrieve the image alternative text of a given node.
  2077. // Used by the accessibility interface to provide useful text for status images.
  2078. FOnHotChange: TVTHotNodeChangeEvent; // called when the current "hot" node (that is, the node under the mouse)
  2079. // changes and hot tracking is enabled
  2080. FOnExpanding, // called just before a node is expanded
  2081. FOnCollapsing: TVTChangingEvent; // called just before a node is collapsed
  2082. FOnChecking: TVTCheckChangingEvent; // called just before a node's check state is changed
  2083. FOnExpanded, // called after a node has been expanded
  2084. FOnCollapsed, // called after a node has been collapsed
  2085. FOnChecked: TVTChangeEvent; // called after a node's check state has been changed
  2086. FOnResetNode: TVTChangeEvent; // called when a node is set to be uninitialized
  2087. FOnNodeMoving: TVTNodeMovingEvent; // called just before a node is moved from one parent node to another
  2088. // (this can be cancelled)
  2089. FOnNodeMoved: TVTNodeMovedEvent; // called after a node and its children have been moved to another
  2090. // parent node (probably another tree, but within the same application)
  2091. FOnNodeCopying: TVTNodeCopyingEvent; // called when a node is copied to another parent node (probably in
  2092. // another tree, but within the same application, can be cancelled)
  2093. FOnNodeClick: TVTNodeClickEvent; // called when the user clicks on a node
  2094. FOnNodeDblClick: TVTNodeClickEvent; // called when the user double clicks on a node
  2095. FOnCanSplitterResizeNode: TVTCanSplitterResizeNodeEvent; // called to query the application wether resizing a node is allowed
  2096. FOnNodeHeightTracking: TVTNodeHeightTrackingEvent; // called when a node's height is being changed via mouse
  2097. FOnNodeHeightDblClickResize: TVTNodeHeightDblClickResizeEvent; // called when a node's vertical splitter is double clicked
  2098. FOnNodeCopied: TVTNodeCopiedEvent; // call after a node has been copied
  2099. FOnEditing: TVTEditChangingEvent; // called just before a node goes into edit mode
  2100. FOnEditCancelled: TVTEditCancelEvent; // called when editing has been cancelled
  2101. FOnEdited: TVTEditChangeEvent; // called when editing has successfully been finished
  2102. FOnFocusChanging: TVTFocusChangingEvent; // called when the focus is about to go to a new node and/or column
  2103. // (can be cancelled)
  2104. FOnFocusChanged: TVTFocusChangeEvent; // called when the focus goes to a new node and/or column
  2105. FOnAddToSelection: TVTAddToSelectionEvent; // called when a node is added to the selection
  2106. FOnRemoveFromSelection: TVTRemoveFromSelectionEvent; // called when a node is removed from the selection
  2107. FOnGetPopupMenu: TVTPopupEvent; // called when the popup for a node or the header needs to be shown
  2108. FOnGetHelpContext: TVTHelpContextEvent; // called when a node specific help theme should be called
  2109. FOnCreateEditor: TVTCreateEditorEvent; // called when a node goes into edit mode, this allows applications
  2110. // to supply their own editor
  2111. FOnLoadNode, // called after a node has been loaded from a stream (file, clipboard,
  2112. // OLE drag'n drop) to allow an application to load their own data
  2113. // saved in OnSaveNode
  2114. FOnSaveNode: TVTSaveNodeEvent; // called when a node needs to be serialized into a stream
  2115. // (see OnLoadNode) to give the application the opportunity to save
  2116. // their node specific, persistent data (note: never save memory
  2117. // references)
  2118. FOnLoadTree, // called after the tree has been loaded from a stream to allow an
  2119. // application to load their own data saved in OnSaveTree
  2120. FOnSaveTree: TVTSaveTreeEvent; // called after the tree has been saved to a stream to allow an
  2121. // application to save its own data
  2122. // header/column mouse events
  2123. FOnAfterAutoFitColumn: TVTAfterAutoFitColumnEvent;
  2124. FOnAfterAutoFitColumns: TVTAfterAutoFitColumnsEvent;
  2125. FOnBeforeAutoFitColumns: TVTBeforeAutoFitColumnsEvent;
  2126. FOnBeforeAutoFitColumn: TVTBeforeAutoFitColumnEvent;
  2127. FOnHeaderClick: TVTHeaderClickEvent;
  2128. FOnHeaderDblClick: TVTHeaderClickEvent;
  2129. FOnAfterHeaderHeightTracking: TVTAfterHeaderHeightTrackingEvent;
  2130. FOnBeforeHeaderHeightTracking: TVTBeforeHeaderHeightTrackingEvent;
  2131. FOnHeaderHeightTracking: TVTHeaderHeightTrackingEvent;
  2132. FOnHeaderHeightDblClickResize: TVTHeaderHeightDblClickResizeEvent;
  2133. FOnHeaderMouseDown,
  2134. FOnHeaderMouseUp: TVTHeaderMouseEvent;
  2135. FOnHeaderMouseMove: TVTHeaderMouseMoveEvent;
  2136. FOnAfterGetMaxColumnWidth: TVTAfterGetMaxColumnWidthEvent;
  2137. FOnBeforeGetMaxColumnWidth: TVTBeforeGetMaxColumnWidthEvent;
  2138. FOnColumnClick: TVTColumnClickEvent;
  2139. FOnColumnDblClick: TVTColumnDblClickEvent;
  2140. FOnColumnResize: TVTHeaderNotifyEvent;
  2141. FOnColumnWidthDblClickResize: TVTColumnWidthDblClickResizeEvent;
  2142. FOnAfterColumnWidthTracking: TVTAfterColumnWidthTrackingEvent;
  2143. FOnBeforeColumnWidthTracking: TVTBeforeColumnWidthTrackingEvent;
  2144. FOnColumnWidthTracking: TVTColumnWidthTrackingEvent;
  2145. FOnGetHeaderCursor: TVTGetHeaderCursorEvent; // triggered to allow the app. to use customized cursors for the header
  2146. FOnCanSplitterResizeColumn: TVTCanSplitterResizeColumnEvent;
  2147. FOnCanSplitterResizeHeader: TVTCanSplitterResizeHeaderEvent;
  2148. // paint events
  2149. FOnAfterPaint, // triggered when the tree has entirely been painted
  2150. FOnBeforePaint: TVTPaintEvent; // triggered when the tree is about to be painted
  2151. FOnAfterItemPaint: TVTAfterItemPaintEvent; // triggered after an item has been painted
  2152. FOnBeforeItemPaint: TVTBeforeItemPaintEvent; // triggered when an item is about to be painted
  2153. FOnBeforeItemErase: TVTBeforeItemEraseEvent; // triggered when an item's background is about to be erased
  2154. FOnAfterItemErase: TVTAfterItemEraseEvent; // triggered after an item's background has been erased
  2155. FOnAfterCellPaint: TVTAfterCellPaintEvent; // triggered after a column of an item has been painted
  2156. FOnBeforeCellPaint: TVTBeforeCellPaintEvent; // triggered when a column of an item is about to be painted
  2157. FOnHeaderDraw: TVTHeaderPaintEvent; // Used when owner draw is enabled for the header and a column is set
  2158. // to owner draw mode.
  2159. FOnHeaderDrawQueryElements: TVTHeaderPaintQueryElementsEvent; // Used for advanced header painting to query the
  2160. // application for the elements, which are drawn by it and which should
  2161. // be drawn by the tree.
  2162. FOnAdvancedHeaderDraw: TVTAdvancedHeaderPaintEvent; // Used when owner draw is enabled for the header and a column
  2163. // is set to owner draw mode. But only if OnHeaderDrawQueryElements
  2164. // returns at least one element to be drawn by the application.
  2165. // In this case OnHeaderDraw is not used.
  2166. FOnGetLineStyle: TVTGetLineStyleEvent; // triggered when a custom line style is used and the pattern brush
  2167. // needs to be build
  2168. FOnPaintBackground: TVTBackgroundPaintEvent; // triggered if a part of the tree's background must be erased which is
  2169. // not covered by any node
  2170. FOnMeasureItem: TVTMeasureItemEvent; // Triggered when a node is about to be drawn and its height was not yet
  2171. // determined by the application.
  2172. // drag'n drop events
  2173. FOnCreateDragManager: TVTCreateDragManagerEvent; // called to allow for app./descendant defined drag managers
  2174. FOnCreateDataObject: TVTCreateDataObjectEvent; // called to allow for app./descendant defined data objects
  2175. FOnDragAllowed: TVTDragAllowedEvent; // used to get permission for manual drag in mouse down
  2176. FOnDragOver: TVTDragOverEvent; // called for every mouse move
  2177. FOnDragDrop: TVTDragDropEvent; // called on release of mouse button (if drop was allowed)
  2178. FOnHeaderDragged: TVTHeaderDraggedEvent; // header (column) drag'n drop
  2179. FOnHeaderDraggedOut: TVTHeaderDraggedOutEvent; // header (column) drag'n drop, which did not result in a valid drop.
  2180. FOnHeaderDragging: TVTHeaderDraggingEvent; // header (column) drag'n drop
  2181. FOnRenderOLEData: TVTRenderOLEDataEvent; // application/descendant defined clipboard formats
  2182. FOnGetUserClipboardFormats: TVTGetUserClipboardFormatsEvent; // gives application/descendants the opportunity to
  2183. // add own clipboard formats on the fly
  2184. // miscellanous events
  2185. FOnGetNodeDataSize: TVTGetNodeDataSizeEvent; // Called if NodeDataSize is -1.
  2186. FOnBeforeDrawLineImage: TVTBeforeDrawLineImageEvent; // Called to allow adjusting the indention of treelines.
  2187. FOnKeyAction: TVTKeyActionEvent; // Used to selectively prevent key actions (full expand on Ctrl+'+' etc.).
  2188. FOnScroll: TVTScrollEvent; // Called when one or both paint offsets changed.
  2189. FOnUpdating: TVTUpdatingEvent; // Called from BeginUpdate, EndUpdate, BeginSynch and EndSynch.
  2190. FOnGetCursor: TVTGetCursorEvent; // Called to allow the app. to set individual cursors.
  2191. FOnStateChange: TVTStateChangeEvent; // Called whenever a state in the tree changes.
  2192. FOnGetCellIsEmpty: TVTGetCellIsEmptyEvent; // Called when the tree needs to know if a cell is empty.
  2193. FOnShowScrollBar: TVTScrollBarShowEvent; // Called when a scrollbar is changed in its visibility.
  2194. // search, sort
  2195. FOnCompareNodes: TVTCompareEvent; // used during sort
  2196. FOnDrawHint: TVTDrawHintEvent;
  2197. FOnGetHintSize: TVTGetHintSizeEvent;
  2198. FOnGetHintKind: TVTHintKindEvent;
  2199. FOnIncrementalSearch: TVTIncrementalSearchEvent; // triggered on every key press (not key down)
  2200. FOnMouseEnter: TNotifyEvent;
  2201. FOnMouseLeave: TNotifyEvent;
  2202. // operations
  2203. FOnStartOperation: TVTOperationEvent; // Called when an operation starts
  2204. FOnEndOperation: TVTOperationEvent; // Called when an operation ends
  2205. FVclStyleEnabled: Boolean;
  2206. {$if CompilerVersion >= 23 }
  2207. FSavedBevelKind: TBevelKind;
  2208. FSavedBorderWidth: Integer;
  2209. FSetOrRestoreBevelKindAndBevelWidth: Boolean;
  2210. procedure CMStyleChanged(var Message: TMessage); message CM_STYLECHANGED;
  2211. procedure CMBorderChanged(var Message: TMessage); message CM_BORDERCHANGED;
  2212. procedure CMParentDoubleBufferedChange(var Message: TMessage); message CM_PARENTDOUBLEBUFFEREDCHANGED;
  2213. {$ifend}
  2214. procedure AdjustCoordinatesByIndent(var PaintInfo: TVTPaintInfo; Indent: Integer);
  2215. procedure AdjustTotalCount(Node: PVirtualNode; Value: Integer; Relative: Boolean = False);
  2216. procedure AdjustTotalHeight(Node: PVirtualNode; Value: Integer; Relative: Boolean = False);
  2217. function CalculateCacheEntryCount: Integer;
  2218. procedure CalculateVerticalAlignments(ShowImages, ShowStateImages: Boolean; Node: PVirtualNode; var VAlign,
  2219. VButtonAlign: Integer);
  2220. function ChangeCheckState(Node: PVirtualNode; Value: TCheckState): Boolean;
  2221. function CollectSelectedNodesLTR(MainColumn, NodeLeft, NodeRight: Integer; Alignment: TAlignment; OldRect,
  2222. NewRect: TRect): Boolean;
  2223. function CollectSelectedNodesRTL(MainColumn, NodeLeft, NodeRight: Integer; Alignment: TAlignment; OldRect,
  2224. NewRect: TRect): Boolean;
  2225. procedure ClearNodeBackground(const PaintInfo: TVTPaintInfo; UseBackground, Floating: Boolean; R: TRect);
  2226. function CompareNodePositions(Node1, Node2: PVirtualNode; ConsiderChildrenAbove: Boolean = False): Integer;
  2227. procedure DrawLineImage(const PaintInfo: TVTPaintInfo; X, Y, H, VAlign: Integer; Style: TVTLineType; Reverse: Boolean);
  2228. function FindInPositionCache(Node: PVirtualNode; var CurrentPos: Cardinal): PVirtualNode; overload;
  2229. function FindInPositionCache(Position: Cardinal; var CurrentPos: Cardinal): PVirtualNode; overload;
  2230. procedure FixupTotalCount(Node: PVirtualNode);
  2231. procedure FixupTotalHeight(Node: PVirtualNode);
  2232. function GetBottomNode: PVirtualNode;
  2233. function GetCheckedCount: Integer;
  2234. function GetCheckState(Node: PVirtualNode): TCheckState;
  2235. function GetCheckType(Node: PVirtualNode): TCheckType;
  2236. function GetChildCount(Node: PVirtualNode): Cardinal;
  2237. function GetChildrenInitialized(Node: PVirtualNode): Boolean;
  2238. function GetCutCopyCount: Integer;
  2239. function GetDisabled(Node: PVirtualNode): Boolean;
  2240. function GetDragManager: IVTDragManager;
  2241. function GetExpanded(Node: PVirtualNode): Boolean;
  2242. function GetFiltered(Node: PVirtualNode): Boolean;
  2243. function GetFullyVisible(Node: PVirtualNode): Boolean;
  2244. function GetHasChildren(Node: PVirtualNode): Boolean;
  2245. function GetMultiline(Node: PVirtualNode): Boolean;
  2246. function GetNodeHeight(Node: PVirtualNode): Cardinal;
  2247. function GetNodeParent(Node: PVirtualNode): PVirtualNode;
  2248. function GetOffsetXY: TPoint;
  2249. function GetRootNodeCount: Cardinal;
  2250. function GetSelected(Node: PVirtualNode): Boolean;
  2251. function GetTopNode: PVirtualNode;
  2252. function GetTotalCount: Cardinal;
  2253. function GetVerticalAlignment(Node: PVirtualNode): Byte;
  2254. function GetVisible(Node: PVirtualNode): Boolean;
  2255. function GetVisiblePath(Node: PVirtualNode): Boolean;
  2256. procedure HandleClickSelection(LastFocused, NewNode: PVirtualNode; Shift: TShiftState; DragPending: Boolean);
  2257. function HandleDrawSelection(X, Y: Integer): Boolean;
  2258. function HasVisibleNextSibling(Node: PVirtualNode): Boolean;
  2259. function HasVisiblePreviousSibling(Node: PVirtualNode): Boolean;
  2260. procedure ImageListChange(Sender: TObject);
  2261. procedure InitializeFirstColumnValues(var PaintInfo: TVTPaintInfo);
  2262. procedure InitRootNode(OldSize: Cardinal = 0);
  2263. procedure InterruptValidation;
  2264. function IsFirstVisibleChild(Parent, Node: PVirtualNode): Boolean;
  2265. function IsLastVisibleChild(Parent, Node: PVirtualNode): Boolean;
  2266. function MakeNewNode: PVirtualNode;
  2267. function PackArray({*}const TheArray: TNodeArray; Count: Integer): Integer;
  2268. procedure PrepareBitmaps(NeedButtons, NeedLines: Boolean);
  2269. procedure ReadOldOptions(Reader: TReader);
  2270. procedure SetAlignment(const Value: TAlignment);
  2271. procedure SetAnimationDuration(const Value: Cardinal);
  2272. procedure SetBackground(const Value: TPicture);
  2273. procedure SetBackgroundOffset(const Index, Value: Integer);
  2274. procedure SetBorderStyle(Value: TBorderStyle);
  2275. procedure SetBottomNode(Node: PVirtualNode);
  2276. procedure SetBottomSpace(const Value: Cardinal);
  2277. procedure SetButtonFillMode(const Value: TVTButtonFillMode);
  2278. procedure SetButtonStyle(const Value: TVTButtonStyle);
  2279. procedure SetCheckImageKind(Value: TCheckImageKind);
  2280. procedure SetCheckState(Node: PVirtualNode; Value: TCheckState);
  2281. procedure SetCheckType(Node: PVirtualNode; Value: TCheckType);
  2282. procedure SetChildCount(Node: PVirtualNode; NewChildCount: Cardinal);
  2283. procedure SetClipboardFormats(const Value: TClipboardFormats);
  2284. procedure SetColors(const Value: TVTColors);
  2285. procedure SetCustomCheckImages(const Value: TCustomImageList);
  2286. procedure SetDefaultNodeHeight(Value: Cardinal);
  2287. procedure SetDisabled(Node: PVirtualNode; Value: Boolean);
  2288. procedure SetEmptyListMessage(const Value: UnicodeString);
  2289. procedure SetExpanded(Node: PVirtualNode; Value: Boolean);
  2290. procedure SetFocusedColumn(Value: TColumnIndex);
  2291. procedure SetFocusedNode(Value: PVirtualNode);
  2292. procedure SetFullyVisible(Node: PVirtualNode; Value: Boolean);
  2293. procedure SetHasChildren(Node: PVirtualNode; Value: Boolean);
  2294. procedure SetHeader(const Value: TVTHeader);
  2295. procedure SetFiltered(Node: PVirtualNode; Value: Boolean);
  2296. procedure SetImages(const Value: TCustomImageList);
  2297. procedure SetIndent(Value: Cardinal);
  2298. procedure SetLineMode(const Value: TVTLineMode);
  2299. procedure SetLineStyle(const Value: TVTLineStyle);
  2300. procedure SetMargin(Value: Integer);
  2301. procedure SetMultiline(Node: PVirtualNode; const Value: Boolean);
  2302. procedure SetNodeAlignment(const Value: TVTNodeAlignment);
  2303. procedure SetNodeDataSize(Value: Integer);
  2304. procedure SetNodeHeight(Node: PVirtualNode; Value: Cardinal);
  2305. procedure SetNodeParent(Node: PVirtualNode; const Value: PVirtualNode);
  2306. procedure SetOffsetX(const Value: Integer);
  2307. procedure SetOffsetXY(const Value: TPoint);
  2308. procedure SetOffsetY(const Value: Integer);
  2309. procedure SetOptions(const Value: TCustomVirtualTreeOptions);
  2310. procedure SetRootNodeCount(Value: Cardinal);
  2311. procedure SetScrollBarOptions(Value: TScrollBarOptions);
  2312. procedure SetSearchOption(const Value: TVTIncrementalSearch);
  2313. procedure SetSelected(Node: PVirtualNode; Value: Boolean);
  2314. procedure SetSelectionCurveRadius(const Value: Cardinal);
  2315. procedure SetStateImages(const Value: TCustomImageList);
  2316. procedure SetTextMargin(Value: Integer);
  2317. procedure SetTopNode(Node: PVirtualNode);
  2318. procedure SetUpdateState(Updating: Boolean);
  2319. procedure SetVerticalAlignment(Node: PVirtualNode; Value: Byte);
  2320. procedure SetVisible(Node: PVirtualNode; Value: Boolean);
  2321. procedure SetVisiblePath(Node: PVirtualNode; Value: Boolean);
  2322. procedure StaticBackground(Source: TBitmap; Target: TCanvas; OffsetPosition: TPoint; R: TRect);
  2323. procedure StopTimer(ID: Integer);
  2324. procedure SetWindowTheme(Theme: UnicodeString);
  2325. procedure TileBackground(Source: TBitmap; Target: TCanvas; Offset: TPoint; R: TRect);
  2326. function ToggleCallback(Step, StepSize: Integer; Data: Pointer): Boolean;
  2327. procedure CMColorChange(var Message: TMessage); message CM_COLORCHANGED;
  2328. procedure CMCtl3DChanged(var Message: TMessage); message CM_CTL3DCHANGED;
  2329. procedure CMBiDiModeChanged(var Message: TMessage); message CM_BIDIMODECHANGED;
  2330. procedure CMDenySubclassing(var Message: TMessage); message CM_DENYSUBCLASSING;
  2331. procedure CMDrag(var Message: TCMDrag); message CM_DRAG;
  2332. procedure CMEnabledChanged(var Message: TMessage); message CM_ENABLEDCHANGED;
  2333. procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
  2334. procedure CMHintShow(var Message: TCMHintShow); message CM_HINTSHOW;
  2335. procedure CMHintShowPause(var Message: TCMHintShowPause); message CM_HINTSHOWPAUSE;
  2336. procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
  2337. procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
  2338. procedure CMMouseWheel(var Message: TCMMouseWheel); message CM_MOUSEWHEEL;
  2339. procedure CMSysColorChange(var Message: TMessage); message CM_SYSCOLORCHANGE;
  2340. procedure TVMGetItem(var Message: TMessage); message TVM_GETITEM;
  2341. procedure TVMGetItemRect(var Message: TMessage); message TVM_GETITEMRECT;
  2342. procedure TVMGetNextItem(var Message: TMessage); message TVM_GETNEXTITEM;
  2343. procedure WMCancelMode(var Message: TWMCancelMode); message WM_CANCELMODE;
  2344. procedure WMChangeState(var Message: TMessage); message WM_CHANGESTATE;
  2345. procedure WMChar(var Message: TWMChar); message WM_CHAR;
  2346. procedure WMContextMenu(var Message: TWMContextMenu); message WM_CONTEXTMENU;
  2347. procedure WMCopy(var Message: TWMCopy); message WM_COPY;
  2348. procedure WMCut(var Message: TWMCut); message WM_CUT;
  2349. procedure WMEnable(var Message: TWMEnable); message WM_ENABLE;
  2350. procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
  2351. procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
  2352. procedure WMGetObject(var Message: TMessage); message WM_GETOBJECT;
  2353. procedure WMHScroll(var Message: TWMHScroll); message WM_HSCROLL;
  2354. procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN;
  2355. procedure WMKeyUp(var Message: TWMKeyUp); message WM_KEYUP;
  2356. procedure WMKillFocus(var Msg: TWMKillFocus); message WM_KILLFOCUS;
  2357. procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK;
  2358. procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
  2359. procedure WMLButtonUp(var Message: TWMLButtonUp); message WM_LBUTTONUP;
  2360. procedure WMMButtonDblClk(var Message: TWMMButtonDblClk); message WM_MBUTTONDBLCLK;
  2361. procedure WMMButtonDown(var Message: TWMMButtonDown); message WM_MBUTTONDOWN;
  2362. procedure WMMButtonUp(var Message: TWMMButtonUp); message WM_MBUTTONUP;
  2363. procedure WMNCCalcSize(var Message: TWMNCCalcSize); message WM_NCCALCSIZE;
  2364. procedure WMNCDestroy(var Message: TWMNCDestroy); message WM_NCDESTROY;
  2365. procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
  2366. procedure WMNCPaint(var Message: TRealWMNCPaint); message WM_NCPAINT;
  2367. procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
  2368. procedure WMPaste(var Message: TWMPaste); message WM_PASTE;
  2369. procedure WMPrint(var Message: TWMPrint); message WM_PRINT;
  2370. procedure WMPrintClient(var Message: TWMPrintClient); message WM_PRINTCLIENT;
  2371. procedure WMRButtonDblClk(var Message: TWMRButtonDblClk); message WM_RBUTTONDBLCLK;
  2372. procedure WMRButtonDown(var Message: TWMRButtonDown); message WM_RBUTTONDOWN;
  2373. procedure WMRButtonUp(var Message: TWMRButtonUp); message WM_RBUTTONUP;
  2374. procedure WMSetCursor(var Message: TWMSetCursor); message WM_SETCURSOR;
  2375. procedure WMSetFocus(var Msg: TWMSetFocus); message WM_SETFOCUS;
  2376. procedure WMSize(var Message: TWMSize); message WM_SIZE;
  2377. procedure WMTimer(var Message: TWMTimer); message WM_TIMER;
  2378. procedure WMThemeChanged(var Message: TMessage); message WM_THEMECHANGED;
  2379. procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
  2380. function GetRangeX: Cardinal;
  2381. function GetDoubleBuffered: Boolean;
  2382. procedure SetDoubleBuffered(const Value: Boolean);
  2383. procedure ChangeTreeStatesAsync(EnterStates, LeaveStates: TChangeStates);
  2384. protected
  2385. FFontChanged: Boolean; // flag for keeping informed about font changes in the off screen buffer // [IPK] - private to protected
  2386. procedure AutoScale(); virtual;
  2387. procedure AddToSelection(Node: PVirtualNode); overload; virtual;
  2388. procedure AddToSelection(const NewItems: TNodeArray; NewLength: Integer; ForceInsert: Boolean = False); overload; virtual;
  2389. procedure AdjustImageBorder(Images: TCustomImageList; BidiMode: TBidiMode; VAlign: Integer; var R: TRect;
  2390. var ImageInfo: TVTImageInfo); virtual;
  2391. procedure AdjustPaintCellRect(var PaintInfo: TVTPaintInfo; var NextNonEmpty: TColumnIndex); virtual;
  2392. procedure AdjustPanningCursor(X, Y: Integer); virtual;
  2393. procedure AdviseChangeEvent(StructureChange: Boolean; Node: PVirtualNode; Reason: TChangeReason); virtual;
  2394. function AllocateInternalDataArea(Size: Cardinal): Cardinal; virtual;
  2395. procedure Animate(Steps, Duration: Cardinal; Callback: TVTAnimationCallback; Data: Pointer); virtual;
  2396. function CalculateSelectionRect(X, Y: Integer): Boolean; virtual;
  2397. function CanAutoScroll: Boolean; virtual;
  2398. function CanShowDragImage: Boolean; virtual;
  2399. function CanSplitterResizeNode(P: TPoint; Node: PVirtualNode; Column: TColumnIndex): Boolean;
  2400. procedure Change(Node: PVirtualNode); virtual;
  2401. procedure ChangeScale(M, D: Integer); override;
  2402. function CheckParentCheckState(Node: PVirtualNode; NewCheckState: TCheckState): Boolean; virtual;
  2403. procedure ClearTempCache; virtual;
  2404. function ColumnIsEmpty(Node: PVirtualNode; Column: TColumnIndex): Boolean; virtual;
  2405. function ComputeRTLOffset(ExcludeScrollBar: Boolean = False): Integer; virtual;
  2406. function CountLevelDifference(Node1, Node2: PVirtualNode): Integer; virtual;
  2407. function CountVisibleChildren(Node: PVirtualNode): Cardinal; virtual;
  2408. procedure CreateParams(var Params: TCreateParams); override;
  2409. procedure CreateWnd; override;
  2410. procedure DefineProperties(Filer: TFiler); override;
  2411. function DetermineDropMode(const P: TPoint; var HitInfo: THitInfo; var NodeRect: TRect): TDropMode; virtual;
  2412. procedure DetermineHiddenChildrenFlag(Node: PVirtualNode); virtual;
  2413. procedure DetermineHiddenChildrenFlagAllNodes; virtual;
  2414. procedure DetermineHitPositionLTR(var HitInfo: THitInfo; Offset, Right: Integer; Alignment: TAlignment); virtual;
  2415. procedure DetermineHitPositionRTL(var HitInfo: THitInfo; Offset, Right: Integer; Alignment: TAlignment); virtual;
  2416. function DetermineLineImageAndSelectLevel(Node: PVirtualNode; var LineImage: TLineImage): Integer; virtual;
  2417. function DetermineNextCheckState(CheckType: TCheckType; CheckState: TCheckState): TCheckState; virtual;
  2418. function DetermineScrollDirections(X, Y: Integer): TScrollDirections; virtual;
  2419. procedure DoAdvancedHeaderDraw(var PaintInfo: THeaderPaintInfo; const Elements: THeaderPaintElements); virtual;
  2420. procedure DoAfterCellPaint(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; CellRect: TRect); virtual;
  2421. procedure DoAfterItemErase(Canvas: TCanvas; Node: PVirtualNode; ItemRect: TRect); virtual;
  2422. procedure DoAfterItemPaint(Canvas: TCanvas; Node: PVirtualNode; ItemRect: TRect); virtual;
  2423. procedure DoAfterPaint(Canvas: TCanvas); virtual;
  2424. procedure DoAutoScroll(X, Y: Integer); virtual;
  2425. function DoBeforeDrag(Node: PVirtualNode; Column: TColumnIndex): Boolean; virtual;
  2426. procedure DoBeforeCellPaint(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
  2427. CellPaintMode: TVTCellPaintMode; CellRect: TRect; var ContentRect: TRect); virtual;
  2428. procedure DoBeforeItemErase(Canvas: TCanvas; Node: PVirtualNode; ItemRect: TRect; var Color: TColor;
  2429. var EraseAction: TItemEraseAction); virtual;
  2430. function DoBeforeItemPaint(Canvas: TCanvas; Node: PVirtualNode; ItemRect: TRect): Boolean; virtual;
  2431. procedure DoBeforePaint(Canvas: TCanvas); virtual;
  2432. function DoCancelEdit: Boolean; virtual;
  2433. procedure DoCanEdit(Node: PVirtualNode; Column: TColumnIndex; var Allowed: Boolean); virtual;
  2434. procedure DoCanSplitterResizeNode(P: TPoint; Node: PVirtualNode; Column: TColumnIndex;
  2435. var Allowed: Boolean); virtual;
  2436. procedure DoChange(Node: PVirtualNode); virtual;
  2437. procedure DoCheckClick(Node: PVirtualNode; NewCheckState: TCheckState); virtual;
  2438. procedure DoChecked(Node: PVirtualNode); virtual;
  2439. function DoChecking(Node: PVirtualNode; var NewCheckState: TCheckState): Boolean; virtual;
  2440. procedure DoCollapsed(Node: PVirtualNode); virtual;
  2441. function DoCollapsing(Node: PVirtualNode): Boolean; virtual;
  2442. procedure DoColumnClick(Column: TColumnIndex; Shift: TShiftState); virtual;
  2443. procedure DoColumnDblClick(Column: TColumnIndex; Shift: TShiftState); virtual;
  2444. procedure DoColumnResize(Column: TColumnIndex); virtual;
  2445. function DoCompare(Node1, Node2: PVirtualNode; Column: TColumnIndex): Integer; virtual;
  2446. function DoCreateDataObject: IDataObject; virtual;
  2447. function DoCreateDragManager: IVTDragManager; virtual;
  2448. function DoCreateEditor(Node: PVirtualNode; Column: TColumnIndex): IVTEditLink; virtual;
  2449. procedure DoDragging(P: TPoint); virtual;
  2450. procedure DoDragExpand; virtual;
  2451. procedure DoBeforeDrawLineImage(Node: PVirtualNode; Level: Integer; var XPos: Integer); virtual;
  2452. function DoDragOver(Source: TObject; Shift: TShiftState; State: TDragState; Pt: TPoint; Mode: TDropMode;
  2453. var Effect: Integer): Boolean; virtual;
  2454. procedure DoDragDrop(Source: TObject; DataObject: IDataObject; Formats: TFormatArray; Shift: TShiftState; Pt: TPoint;
  2455. var Effect: Integer; Mode: TDropMode); virtual;
  2456. procedure DoDrawHint(Canvas: TCanvas; Node: PVirtualNode; R: TRect; Column:
  2457. TColumnIndex);
  2458. procedure DoEdit; virtual;
  2459. procedure DoEndDrag(Target: TObject; X, Y: Integer); override;
  2460. function DoEndEdit: Boolean; virtual;
  2461. procedure DoEndOperation(OperationKind: TVTOperationKind); virtual;
  2462. procedure DoEnter(); override;
  2463. procedure DoExpanded(Node: PVirtualNode); virtual;
  2464. function DoExpanding(Node: PVirtualNode): Boolean; virtual;
  2465. procedure DoFocusChange(Node: PVirtualNode; Column: TColumnIndex); virtual;
  2466. function DoFocusChanging(OldNode, NewNode: PVirtualNode; OldColumn, NewColumn: TColumnIndex): Boolean; virtual;
  2467. procedure DoFocusNode(Node: PVirtualNode; Ask: Boolean); virtual;
  2468. procedure DoFreeNode(Node: PVirtualNode); virtual;
  2469. function DoGetAnimationType: THintAnimationType; virtual;
  2470. function DoGetCellContentMargin(Node: PVirtualNode; Column: TColumnIndex;
  2471. CellContentMarginType: TVTCellContentMarginType = ccmtAllSides; Canvas: TCanvas = nil): TPoint; virtual;
  2472. procedure DoGetCursor(var Cursor: TCursor); virtual;
  2473. procedure DoGetHeaderCursor(var Cursor: HCURSOR); virtual;
  2474. procedure DoGetHintSize(Node: PVirtualNode; Column: TColumnIndex; var R:
  2475. TRect); virtual;
  2476. procedure DoGetHintKind(Node: PVirtualNode; Column: TColumnIndex; var Kind:
  2477. TVTHintKind);
  2478. function DoGetImageIndex(Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
  2479. var Ghosted: Boolean; var Index: Integer): TCustomImageList; virtual;
  2480. procedure DoGetImageText(Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
  2481. var ImageText: UnicodeString); virtual;
  2482. procedure DoGetLineStyle(var Bits: Pointer); virtual;
  2483. function DoGetNodeHint(Node: PVirtualNode; Column: TColumnIndex; var LineBreakStyle: TVTTooltipLineBreakStyle): UnicodeString; virtual;
  2484. function DoGetNodeTooltip(Node: PVirtualNode; Column: TColumnIndex; var LineBreakStyle: TVTTooltipLineBreakStyle): UnicodeString; virtual;
  2485. function DoGetNodeExtraWidth(Node: PVirtualNode; Column: TColumnIndex; Canvas: TCanvas = nil): Integer; virtual;
  2486. function DoGetNodeWidth(Node: PVirtualNode; Column: TColumnIndex; Canvas: TCanvas = nil): Integer; virtual;
  2487. function DoGetPopupMenu(Node: PVirtualNode; Column: TColumnIndex; Position: TPoint): TPopupMenu; virtual;
  2488. procedure DoGetUserClipboardFormats(var Formats: TFormatEtcArray); virtual;
  2489. procedure DoHeaderClick(HitInfo: TVTHeaderHitInfo); virtual;
  2490. procedure DoHeaderDblClick(HitInfo: TVTHeaderHitInfo); virtual;
  2491. procedure DoHeaderDragged(Column: TColumnIndex; OldPosition: TColumnPosition); virtual;
  2492. procedure DoHeaderDraggedOut(Column: TColumnIndex; DropPosition: TPoint); virtual;
  2493. function DoHeaderDragging(Column: TColumnIndex): Boolean; virtual;
  2494. procedure DoHeaderDraw(Canvas: TCanvas; Column: TVirtualTreeColumn; R: TRect; Hover, Pressed: Boolean;
  2495. DropMark: TVTDropMarkMode); virtual;
  2496. procedure DoHeaderDrawQueryElements(var PaintInfo: THeaderPaintInfo; var Elements: THeaderPaintElements); virtual;
  2497. procedure DoHeaderMouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); virtual;
  2498. procedure DoHeaderMouseMove(Shift: TShiftState; X, Y: Integer); virtual;
  2499. procedure DoHeaderMouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); virtual;
  2500. procedure DoHotChange(Old, New: PVirtualNode); virtual;
  2501. function DoIncrementalSearch(Node: PVirtualNode; const Text: UnicodeString): Integer; virtual;
  2502. function DoInitChildren(Node: PVirtualNode; var ChildCount: Cardinal): Boolean; virtual;
  2503. procedure DoInitNode(Parent, Node: PVirtualNode; var InitStates: TVirtualNodeInitStates); virtual;
  2504. function DoKeyAction(var CharCode: Word; var Shift: TShiftState): Boolean; virtual;
  2505. procedure DoLoadUserData(Node: PVirtualNode; Stream: TStream); virtual;
  2506. procedure DoMeasureItem(TargetCanvas: TCanvas; Node: PVirtualNode; var NodeHeight: Integer); virtual;
  2507. procedure DoMouseEnter(); virtual;
  2508. procedure DoMouseLeave(); virtual;
  2509. procedure DoNodeCopied(Node: PVirtualNode); virtual;
  2510. function DoNodeCopying(Node, NewParent: PVirtualNode): Boolean; virtual;
  2511. procedure DoNodeClick(const HitInfo: THitInfo); virtual;
  2512. procedure DoNodeDblClick(const HitInfo: THitInfo); virtual;
  2513. function DoNodeHeightDblClickResize(Node: PVirtualNode; Column: TColumnIndex; Shift: TShiftState;
  2514. P: TPoint): Boolean; virtual;
  2515. function DoNodeHeightTracking(Node: PVirtualNode; Column: TColumnIndex; Shift: TShiftState;
  2516. var TrackPoint: TPoint; P: TPoint): Boolean; virtual;
  2517. procedure DoNodeMoved(Node: PVirtualNode); virtual;
  2518. function DoNodeMoving(Node, NewParent: PVirtualNode): Boolean; virtual;
  2519. function DoPaintBackground(Canvas: TCanvas; R: TRect): Boolean; virtual;
  2520. procedure DoPaintDropMark(Canvas: TCanvas; Node: PVirtualNode; R: TRect); virtual;
  2521. procedure DoPaintNode(var PaintInfo: TVTPaintInfo); virtual;
  2522. procedure DoPopupMenu(Node: PVirtualNode; Column: TColumnIndex; Position: TPoint); virtual;
  2523. procedure DoRemoveFromSelection(Node: PVirtualNode); virtual;
  2524. function DoRenderOLEData(const FormatEtcIn: TFormatEtc; out Medium: TStgMedium;
  2525. ForClipboard: Boolean): HRESULT; virtual;
  2526. procedure DoReset(Node: PVirtualNode); virtual;
  2527. procedure DoSaveUserData(Node: PVirtualNode; Stream: TStream); virtual;
  2528. procedure DoScroll(DeltaX, DeltaY: Integer); virtual;
  2529. function DoSetOffsetXY(Value: TPoint; Options: TScrollUpdateOptions; ClipRect: PRect = nil): Boolean; virtual;
  2530. procedure DoShowScrollBar(Bar: Integer; Show: Boolean); virtual;
  2531. procedure DoStartDrag(var DragObject: TDragObject); override;
  2532. procedure DoStartOperation(OperationKind: TVTOperationKind); virtual;
  2533. procedure DoStateChange(Enter: TVirtualTreeStates; Leave: TVirtualTreeStates = []); virtual;
  2534. procedure DoStructureChange(Node: PVirtualNode; Reason: TChangeReason); virtual;
  2535. procedure DoTimerScroll; virtual;
  2536. procedure DoUpdating(State: TVTUpdateState); virtual;
  2537. function DoValidateCache: Boolean; virtual;
  2538. procedure DragAndDrop(AllowedEffects: DWord; DataObject: IDataObject;
  2539. var DragEffect: LongInt); virtual;
  2540. procedure DragCanceled; override;
  2541. function DragDrop(const DataObject: IDataObject; KeyState: Integer; Pt: TPoint;
  2542. var Effect: Integer): HResult; reintroduce; virtual;
  2543. function DragEnter(KeyState: Integer; Pt: TPoint; var Effect: Integer): HResult; virtual;
  2544. procedure DragFinished; virtual;
  2545. procedure DragLeave; virtual;
  2546. function DragOver(Source: TObject; KeyState: Integer; DragState: TDragState; Pt: TPoint;
  2547. var Effect: LongInt): HResult; reintroduce; virtual;
  2548. procedure DrawDottedHLine(const PaintInfo: TVTPaintInfo; Left, Right, Top: Integer); virtual;
  2549. procedure DrawDottedVLine(const PaintInfo: TVTPaintInfo; Top, Bottom, Left: Integer; UseSelectedBkColor: Boolean = False); virtual;
  2550. procedure EndOperation(OperationKind: TVTOperationKind);
  2551. procedure EnsureNodeFocused(); virtual;
  2552. function FindNodeInSelection(P: PVirtualNode; var Index: Integer; LowBound, HighBound: Integer): Boolean; virtual;
  2553. procedure FinishChunkHeader(Stream: TStream; StartPos, EndPos: Integer); virtual;
  2554. procedure FontChanged(AFont: TObject); virtual;
  2555. function GetBorderDimensions: TSize; virtual;
  2556. function GetCheckImage(Node: PVirtualNode; ImgCheckType: TCheckType = ctNone;
  2557. ImgCheckState: TCheckState = csUncheckedNormal; ImgEnabled: Boolean = True): Integer; virtual;
  2558. class function GetCheckImageListFor(Kind: TCheckImageKind): TCustomImageList; virtual;
  2559. function GetColumnClass: TVirtualTreeColumnClass; virtual;
  2560. function GetDefaultHintKind: TVTHintKind; virtual;
  2561. function GetHeaderClass: TVTHeaderClass; virtual;
  2562. function GetHintWindowClass: THintWindowClass; virtual;
  2563. procedure GetImageIndex(var Info: TVTPaintInfo; Kind: TVTImageKind; InfoIndex: TVTImageInfoIndex;
  2564. DefaultImages: TCustomImageList); virtual;
  2565. function GetNodeImageSize(Node: PVirtualNode): TSize; virtual;
  2566. function GetMaxRightExtend: Cardinal; virtual;
  2567. procedure GetNativeClipboardFormats(var Formats: TFormatEtcArray); virtual;
  2568. function GetOperationCanceled: Boolean;
  2569. function GetOptionsClass: TTreeOptionsClass; virtual;
  2570. function GetTreeFromDataObject(const DataObject: IDataObject): TBaseVirtualTree; virtual;
  2571. procedure HandleHotTrack(X, Y: Integer); virtual;
  2572. procedure HandleIncrementalSearch(CharCode: Word); virtual;
  2573. procedure HandleMouseDblClick(var Message: TWMMouse; const HitInfo: THitInfo); virtual;
  2574. procedure HandleMouseDown(var Message: TWMMouse; var HitInfo: THitInfo); virtual;
  2575. procedure HandleMouseUp(var Message: TWMMouse; const HitInfo: THitInfo); virtual;
  2576. function HasImage(Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex): Boolean; virtual;
  2577. function HasPopupMenu(Node: PVirtualNode; Column: TColumnIndex; Pos: TPoint): Boolean; virtual;
  2578. procedure InitChildren(Node: PVirtualNode); virtual;
  2579. procedure InitNode(Node: PVirtualNode); virtual;
  2580. procedure InternalAddFromStream(Stream: TStream; Version: Integer; Node: PVirtualNode); virtual;
  2581. function InternalAddToSelection(Node: PVirtualNode; ForceInsert: Boolean): Boolean; overload;
  2582. function InternalAddToSelection(const NewItems: TNodeArray; NewLength: Integer;
  2583. ForceInsert: Boolean): Boolean; overload;
  2584. procedure InternalCacheNode(Node: PVirtualNode); virtual;
  2585. procedure InternalClearSelection; virtual;
  2586. procedure InternalConnectNode(Node, Destination: PVirtualNode; Target: TBaseVirtualTree; Mode: TVTNodeAttachMode); virtual;
  2587. function InternalData(Node: PVirtualNode): Pointer;
  2588. procedure InternalDisconnectNode(Node: PVirtualNode; KeepFocus: Boolean; Reindex: Boolean = True); virtual;
  2589. procedure InternalRemoveFromSelection(Node: PVirtualNode); virtual;
  2590. procedure InvalidateCache;
  2591. procedure Loaded; override;
  2592. procedure MainColumnChanged; virtual;
  2593. procedure MarkCutCopyNodes; virtual;
  2594. procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
  2595. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  2596. procedure OriginalWMNCPaint(DC: HDC); virtual;
  2597. procedure Paint; override;
  2598. procedure PaintCheckImage(Canvas: TCanvas; const ImageInfo: TVTImageInfo; Selected: Boolean); virtual;
  2599. procedure PaintImage(var PaintInfo: TVTPaintInfo; ImageInfoIndex: TVTImageInfoIndex; DoOverlay: Boolean); virtual;
  2600. procedure PaintNodeButton(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; const R: TRect; ButtonX,
  2601. ButtonY: Integer; BidiMode: TBiDiMode); virtual;
  2602. procedure PaintTreeLines(const PaintInfo: TVTPaintInfo; VAlignment, IndentSize: Integer;
  2603. LineImage: TLineImage); virtual;
  2604. procedure PaintSelectionRectangle(Target: TCanvas; WindowOrgX: Integer; const SelectionRect: TRect;
  2605. TargetRect: TRect); virtual;
  2606. procedure PanningWindowProc(var Message: TMessage); virtual;
  2607. procedure PrepareCell(var PaintInfo: TVTPaintInfo; WindowOrgX, MaxWidth: Integer); virtual;
  2608. function ReadChunk(Stream: TStream; Version: Integer; Node: PVirtualNode; ChunkType,
  2609. ChunkSize: Integer): Boolean; virtual;
  2610. procedure ReadNode(Stream: TStream; Version: Integer; Node: PVirtualNode); virtual;
  2611. procedure RedirectFontChangeEvent(Canvas: TCanvas); virtual;
  2612. procedure RemoveFromSelection(Node: PVirtualNode); virtual;
  2613. procedure UpdateNextNodeToSelect(Node: PVirtualNode); virtual;
  2614. function RenderOLEData(const FormatEtcIn: TFormatEtc; out Medium: TStgMedium; ForClipboard: Boolean): HResult; virtual;
  2615. procedure ResetRangeAnchor; virtual;
  2616. procedure RestoreFontChangeEvent(Canvas: TCanvas); virtual;
  2617. procedure SelectNodes(StartNode, EndNode: PVirtualNode; AddOnly: Boolean); virtual;
  2618. procedure SetFocusedNodeAndColumn(Node: PVirtualNode; Column: TColumnIndex); virtual;
  2619. procedure SkipNode(Stream: TStream); virtual;
  2620. procedure StartOperation(OperationKind: TVTOperationKind);
  2621. procedure StartWheelPanning(Position: TPoint); virtual;
  2622. procedure StopWheelPanning; virtual;
  2623. procedure StructureChange(Node: PVirtualNode; Reason: TChangeReason); virtual;
  2624. function SuggestDropEffect(Source: TObject; Shift: TShiftState; Pt: TPoint; AllowedEffects: Integer): Integer; virtual;
  2625. procedure ToggleSelection(StartNode, EndNode: PVirtualNode); virtual;
  2626. procedure UnselectNodes(StartNode, EndNode: PVirtualNode); virtual;
  2627. procedure UpdateColumnCheckState(Col: TVirtualTreeColumn);
  2628. procedure UpdateDesigner; virtual;
  2629. procedure UpdateEditBounds; virtual;
  2630. procedure UpdateHeaderRect; virtual;
  2631. procedure UpdateWindowAndDragImage(const Tree: TBaseVirtualTree; TreeRect: TRect; UpdateNCArea,
  2632. ReshowDragImage: Boolean); virtual;
  2633. procedure ValidateCache; virtual;
  2634. procedure ValidateNodeDataSize(var Size: Integer); virtual;
  2635. procedure WndProc(var Message: TMessage); override;
  2636. procedure WriteChunks(Stream: TStream; Node: PVirtualNode); virtual;
  2637. procedure WriteNode(Stream: TStream; Node: PVirtualNode); virtual;
  2638. procedure VclStyleChanged;
  2639. property VclStyleEnabled: Boolean read FVclStyleEnabled;
  2640. property Alignment: TAlignment read FAlignment write SetAlignment default taLeftJustify;
  2641. property AnimationDuration: Cardinal read FAnimationDuration write SetAnimationDuration default 200;
  2642. property AutoExpandDelay: Cardinal read FAutoExpandDelay write FAutoExpandDelay default 1000;
  2643. property AutoScrollDelay: Cardinal read FAutoScrollDelay write FAutoScrollDelay default 1000;
  2644. property AutoScrollInterval: TAutoScrollInterval read FAutoScrollInterval write FAutoScrollInterval default 1;
  2645. property Background: TPicture read FBackground write SetBackground;
  2646. property BackgroundOffsetX: Integer index 0 read FBackgroundOffsetX write SetBackgroundOffset default 0;
  2647. property BackgroundOffsetY: Integer index 1 read FBackgroundOffsetY write SetBackgroundOffset default 0;
  2648. property BorderStyle: TBorderStyle read FBorderStyle write SetBorderStyle default bsSingle;
  2649. property BottomSpace: Cardinal read FBottomSpace write SetBottomSpace default 0;
  2650. property ButtonFillMode: TVTButtonFillMode read FButtonFillMode write SetButtonFillMode default fmTreeColor;
  2651. property ButtonStyle: TVTButtonStyle read FButtonStyle write SetButtonStyle default bsRectangle;
  2652. property ChangeDelay: Cardinal read FChangeDelay write FChangeDelay default 0;
  2653. property CheckImageKind: TCheckImageKind read FCheckImageKind write SetCheckImageKind default ckSystemDefault;
  2654. property ClipboardFormats: TClipboardFormats read FClipboardFormats write SetClipboardFormats;
  2655. property Colors: TVTColors read FColors write SetColors;
  2656. property CustomCheckImages: TCustomImageList read FCustomCheckImages write SetCustomCheckImages;
  2657. property DefaultHintKind: TVTHintKind read GetDefaultHintKind;
  2658. property DefaultNodeHeight: Cardinal read FDefaultNodeHeight write SetDefaultNodeHeight default 18;
  2659. property DefaultPasteMode: TVTNodeAttachMode read FDefaultPasteMode write FDefaultPasteMode default amAddChildLast;
  2660. property DragHeight: Integer read FDragHeight write FDragHeight default 350;
  2661. property DragImageKind: TVTDragImageKind read FDragImageKind write FDragImageKind default diComplete;
  2662. property DragOperations: TDragOperations read FDragOperations write FDragOperations default [doCopy, doMove];
  2663. property DragSelection: TNodeArray read FDragSelection;
  2664. property LastDragEffect: LongInt read FLastDragEffect;
  2665. property DragType: TVTDragType read FDragType write FDragType default dtOLE;
  2666. property DragWidth: Integer read FDragWidth write FDragWidth default 200;
  2667. property DrawSelectionMode: TVTDrawSelectionMode read FDrawSelectionMode write FDrawSelectionMode
  2668. default smDottedRectangle;
  2669. property EditColumn: TColumnIndex read FEditColumn write FEditColumn;
  2670. property EditDelay: Cardinal read FEditDelay write FEditDelay default 1000;
  2671. property EffectiveOffsetX: Integer read FEffectiveOffsetX;
  2672. property Header: TVTHeader read FHeader write SetHeader;
  2673. property HeaderRect: TRect read FHeaderRect;
  2674. property HintAnimation: THintAnimationType read FAnimationType write FAnimationType default hatSystemDefault;
  2675. property HintMode: TVTHintMode read FHintMode write FHintMode default hmDefault;
  2676. property HintData: TVTHintData read FHintData write FHintData;
  2677. property HotCursor: TCursor read FHotCursor write FHotCursor default crDefault;
  2678. property Images: TCustomImageList read FImages write SetImages;
  2679. property IncrementalSearch: TVTIncrementalSearch read FIncrementalSearch write SetSearchOption default isNone;
  2680. property IncrementalSearchDirection: TVTSearchDirection read FSearchDirection write FSearchDirection default sdForward;
  2681. property IncrementalSearchStart: TVTSearchStart read FSearchStart write FSearchStart default ssFocusedNode;
  2682. property IncrementalSearchTimeout: Cardinal read FSearchTimeout write FSearchTimeout default 1000;
  2683. property Indent: Cardinal read FIndent write SetIndent default 18;
  2684. property LastClickPos: TPoint read FLastClickPos write FLastClickPos;
  2685. property LastDropMode: TDropMode read FLastDropMode write FLastDropMode;
  2686. property LastHintRect: TRect read FLastHintRect write FLastHintRect;
  2687. property LineMode: TVTLineMode read FLineMode write SetLineMode default lmNormal;
  2688. property LineStyle: TVTLineStyle read FLineStyle write SetLineStyle default lsDotted;
  2689. property Margin: Integer read FMargin write SetMargin default 4;
  2690. property NextNodeToSelect: PVirtualNode read FNextNodeToSelect; // Next tree node that we would like to select if the current one gets deleted
  2691. property NodeAlignment: TVTNodeAlignment read FNodeAlignment write SetNodeAlignment default naProportional;
  2692. property NodeDataSize: Integer read FNodeDataSize write SetNodeDataSize default -1;
  2693. property OperationCanceled: Boolean read GetOperationCanceled;
  2694. property HotMinusBM: TBitmap read FHotMinusBM;
  2695. property HotPlusBM: TBitmap read FHotPlusBM;
  2696. property MinusBM: TBitmap read FMinusBM;
  2697. property PlusBM: TBitmap read FPlusBM;
  2698. property RangeX: Cardinal read GetRangeX;// Returns the width of the virtual tree in pixels, (not ClientWidth). If there are columns it returns the total width of all of them; otherwise it returns the maximum of the all the line's data widths.
  2699. property RangeY: Cardinal read FRangeY;
  2700. property RootNodeCount: Cardinal read GetRootNodeCount write SetRootNodeCount default 0;
  2701. property ScrollBarOptions: TScrollBarOptions read FScrollBarOptions write SetScrollBarOptions;
  2702. property SelectionBlendFactor: Byte read FSelectionBlendFactor write FSelectionBlendFactor default 128;
  2703. property SelectionCurveRadius: Cardinal read FSelectionCurveRadius write SetSelectionCurveRadius default 0;
  2704. property StateImages: TCustomImageList read FStateImages write SetStateImages;
  2705. property TextMargin: Integer read FTextMargin write SetTextMargin default 4;
  2706. property TotalInternalDataSize: Cardinal read FTotalInternalDataSize;
  2707. property TreeOptions: TCustomVirtualTreeOptions read FOptions write SetOptions;
  2708. property WantTabs: Boolean read FWantTabs write FWantTabs default False;
  2709. property OnAddToSelection: TVTAddToSelectionEvent read FOnAddToSelection write FOnAddToSelection;
  2710. property OnAdvancedHeaderDraw: TVTAdvancedHeaderPaintEvent read FOnAdvancedHeaderDraw write FOnAdvancedHeaderDraw;
  2711. property OnAfterAutoFitColumn: TVTAfterAutoFitColumnEvent read FOnAfterAutoFitColumn write FOnAfterAutoFitColumn;
  2712. property OnAfterAutoFitColumns: TVTAfterAutoFitColumnsEvent read FOnAfterAutoFitColumns write FOnAfterAutoFitColumns;
  2713. property OnAfterCellPaint: TVTAfterCellPaintEvent read FOnAfterCellPaint write FOnAfterCellPaint;
  2714. property OnAfterColumnExport : TVTColumnExportEvent read FOnAfterColumnExport write FOnAfterColumnExport;
  2715. property OnAfterColumnWidthTracking: TVTAfterColumnWidthTrackingEvent read FOnAfterColumnWidthTracking write FOnAfterColumnWidthTracking;
  2716. property OnAfterGetMaxColumnWidth: TVTAfterGetMaxColumnWidthEvent read FOnAfterGetMaxColumnWidth write FOnAfterGetMaxColumnWidth;
  2717. property OnAfterHeaderExport: TVTTreeExportEvent read FOnAfterHeaderExport write FOnAfterHeaderExport;
  2718. property OnAfterHeaderHeightTracking: TVTAfterHeaderHeightTrackingEvent read FOnAfterHeaderHeightTracking
  2719. write FOnAfterHeaderHeightTracking;
  2720. property OnAfterItemErase: TVTAfterItemEraseEvent read FOnAfterItemErase write FOnAfterItemErase;
  2721. property OnAfterItemPaint: TVTAfterItemPaintEvent read FOnAfterItemPaint write FOnAfterItemPaint;
  2722. property OnAfterNodeExport: TVTNodeExportEvent read FOnAfterNodeExport write FOnAfterNodeExport;
  2723. property OnAfterPaint: TVTPaintEvent read FOnAfterPaint write FOnAfterPaint;
  2724. property OnAfterTreeExport: TVTTreeExportEvent read FOnAfterTreeExport write FOnAfterTreeExport;
  2725. property OnBeforeAutoFitColumn: TVTBeforeAutoFitColumnEvent read FOnBeforeAutoFitColumn write FOnBeforeAutoFitColumn;
  2726. property OnBeforeAutoFitColumns: TVTBeforeAutoFitColumnsEvent read FOnBeforeAutoFitColumns write FOnBeforeAutoFitColumns;
  2727. property OnBeforeCellPaint: TVTBeforeCellPaintEvent read FOnBeforeCellPaint write FOnBeforeCellPaint;
  2728. property OnBeforeColumnExport: TVTColumnExportEvent read FOnBeforeColumnExport write FOnBeforeColumnExport;
  2729. property OnBeforeColumnWidthTracking: TVTBeforeColumnWidthTrackingEvent read FOnBeforeColumnWidthTracking
  2730. write FOnBeforeColumnWidthTracking;
  2731. property OnBeforeDrawTreeLine: TVTBeforeDrawLineImageEvent read FOnBeforeDrawLineImage write FOnBeforeDrawLineImage;
  2732. property OnBeforeGetMaxColumnWidth: TVTBeforeGetMaxColumnWidthEvent read FOnBeforeGetMaxColumnWidth write FOnBeforeGetMaxColumnWidth;
  2733. property OnBeforeHeaderExport: TVTTreeExportEvent read FOnBeforeHeaderExport write FOnBeforeHeaderExport;
  2734. property OnBeforeHeaderHeightTracking: TVTBeforeHeaderHeightTrackingEvent read FOnBeforeHeaderHeightTracking
  2735. write FOnBeforeHeaderHeightTracking;
  2736. property OnBeforeItemErase: TVTBeforeItemEraseEvent read FOnBeforeItemErase write FOnBeforeItemErase;
  2737. property OnBeforeItemPaint: TVTBeforeItemPaintEvent read FOnBeforeItemPaint write FOnBeforeItemPaint;
  2738. property OnBeforeNodeExport: TVTNodeExportEvent read FOnBeforeNodeExport write FOnBeforeNodeExport;
  2739. property OnBeforePaint: TVTPaintEvent read FOnBeforePaint write FOnBeforePaint;
  2740. property OnBeforeTreeExport: TVTTreeExportEvent read FOnBeforeTreeExport write FOnBeforeTreeExport;
  2741. property OnCanSplitterResizeColumn: TVTCanSplitterResizeColumnEvent read FOnCanSplitterResizeColumn write FOnCanSplitterResizeColumn;
  2742. property OnCanSplitterResizeHeader: TVTCanSplitterResizeHeaderEvent read FOnCanSplitterResizeHeader write FOnCanSplitterResizeHeader;
  2743. property OnCanSplitterResizeNode: TVTCanSplitterResizeNodeEvent read FOnCanSplitterResizeNode write FOnCanSplitterResizeNode;
  2744. property OnChange: TVTChangeEvent read FOnChange write FOnChange;
  2745. property OnChecked: TVTChangeEvent read FOnChecked write FOnChecked;
  2746. property OnChecking: TVTCheckChangingEvent read FOnChecking write FOnChecking;
  2747. property OnCollapsed: TVTChangeEvent read FOnCollapsed write FOnCollapsed;
  2748. property OnCollapsing: TVTChangingEvent read FOnCollapsing write FOnCollapsing;
  2749. property OnColumnClick: TVTColumnClickEvent read FOnColumnClick write FOnColumnClick;
  2750. property OnColumnDblClick: TVTColumnDblClickEvent read FOnColumnDblClick write FOnColumnDblClick;
  2751. property OnColumnExport : TVTColumnExportEvent read FOnColumnExport write FOnColumnExport;
  2752. property OnColumnResize: TVTHeaderNotifyEvent read FOnColumnResize write FOnColumnResize;
  2753. property OnColumnWidthDblClickResize: TVTColumnWidthDblClickResizeEvent read FOnColumnWidthDblClickResize
  2754. write FOnColumnWidthDblClickResize;
  2755. property OnColumnWidthTracking: TVTColumnWidthTrackingEvent read FOnColumnWidthTracking write FOnColumnWidthTracking;
  2756. property OnCompareNodes: TVTCompareEvent read FOnCompareNodes write FOnCompareNodes;
  2757. property OnCreateDataObject: TVTCreateDataObjectEvent read FOnCreateDataObject write FOnCreateDataObject;
  2758. property OnCreateDragManager: TVTCreateDragManagerEvent read FOnCreateDragManager write FOnCreateDragManager;
  2759. property OnCreateEditor: TVTCreateEditorEvent read FOnCreateEditor write FOnCreateEditor;
  2760. property OnDragAllowed: TVTDragAllowedEvent read FOnDragAllowed write FOnDragAllowed;
  2761. property OnDragOver: TVTDragOverEvent read FOnDragOver write FOnDragOver;
  2762. property OnDragDrop: TVTDragDropEvent read FOnDragDrop write FOnDragDrop;
  2763. property OnDrawHint: TVTDrawHintEvent read FOnDrawHint write FOnDrawHint;
  2764. property OnEditCancelled: TVTEditCancelEvent read FOnEditCancelled write FOnEditCancelled;
  2765. property OnEditing: TVTEditChangingEvent read FOnEditing write FOnEditing;
  2766. property OnEdited: TVTEditChangeEvent read FOnEdited write FOnEdited;
  2767. property OnEndOperation: TVTOperationEvent read FOnEndOperation write FOnEndOperation;
  2768. property OnExpanded: TVTChangeEvent read FOnExpanded write FOnExpanded;
  2769. property OnExpanding: TVTChangingEvent read FOnExpanding write FOnExpanding;
  2770. property OnFocusChanged: TVTFocusChangeEvent read FOnFocusChanged write FOnFocusChanged;
  2771. property OnFocusChanging: TVTFocusChangingEvent read FOnFocusChanging write FOnFocusChanging;
  2772. property OnFreeNode: TVTFreeNodeEvent read FOnFreeNode write FOnFreeNode;
  2773. property OnGetCellIsEmpty: TVTGetCellIsEmptyEvent read FOnGetCellIsEmpty write FOnGetCellIsEmpty;
  2774. property OnGetCursor: TVTGetCursorEvent read FOnGetCursor write FOnGetCursor;
  2775. property OnGetHeaderCursor: TVTGetHeaderCursorEvent read FOnGetHeaderCursor write FOnGetHeaderCursor;
  2776. property OnGetHelpContext: TVTHelpContextEvent read FOnGetHelpContext write FOnGetHelpContext;
  2777. property OnGetHintSize: TVTGetHintSizeEvent read FOnGetHintSize write
  2778. FOnGetHintSize;
  2779. property OnGetHintKind: TVTHintKindEvent read FOnGetHintKind write
  2780. FOnGetHintKind;
  2781. property OnGetImageIndex: TVTGetImageEvent read FOnGetImage write FOnGetImage;
  2782. property OnGetImageIndexEx: TVTGetImageExEvent read FOnGetImageEx write FOnGetImageEx;
  2783. property OnGetImageText: TVTGetImageTextEvent read FOnGetImageText write FOnGetImageText;
  2784. property OnGetLineStyle: TVTGetLineStyleEvent read FOnGetLineStyle write FOnGetLineStyle;
  2785. property OnGetNodeDataSize: TVTGetNodeDataSizeEvent read FOnGetNodeDataSize write FOnGetNodeDataSize;
  2786. property OnGetPopupMenu: TVTPopupEvent read FOnGetPopupMenu write FOnGetPopupMenu;
  2787. property OnGetUserClipboardFormats: TVTGetUserClipboardFormatsEvent read FOnGetUserClipboardFormats
  2788. write FOnGetUserClipboardFormats;
  2789. property OnHeaderClick: TVTHeaderClickEvent read FOnHeaderClick write FOnHeaderClick;
  2790. property OnHeaderDblClick: TVTHeaderClickEvent read FOnHeaderDblClick write FOnHeaderDblClick;
  2791. property OnHeaderDragged: TVTHeaderDraggedEvent read FOnHeaderDragged write FOnHeaderDragged;
  2792. property OnHeaderDraggedOut: TVTHeaderDraggedOutEvent read FOnHeaderDraggedOut write FOnHeaderDraggedOut;
  2793. property OnHeaderDragging: TVTHeaderDraggingEvent read FOnHeaderDragging write FOnHeaderDragging;
  2794. property OnHeaderDraw: TVTHeaderPaintEvent read FOnHeaderDraw write FOnHeaderDraw;
  2795. property OnHeaderDrawQueryElements: TVTHeaderPaintQueryElementsEvent read FOnHeaderDrawQueryElements
  2796. write FOnHeaderDrawQueryElements;
  2797. property OnHeaderHeightTracking: TVTHeaderHeightTrackingEvent read FOnHeaderHeightTracking
  2798. write FOnHeaderHeightTracking;
  2799. property OnHeaderHeightDblClickResize: TVTHeaderHeightDblClickResizeEvent read FOnHeaderHeightDblClickResize
  2800. write FOnHeaderHeightDblClickResize;
  2801. property OnHeaderMouseDown: TVTHeaderMouseEvent read FOnHeaderMouseDown write FOnHeaderMouseDown;
  2802. property OnHeaderMouseMove: TVTHeaderMouseMoveEvent read FOnHeaderMouseMove write FOnHeaderMouseMove;
  2803. property OnHeaderMouseUp: TVTHeaderMouseEvent read FOnHeaderMouseUp write FOnHeaderMouseUp;
  2804. property OnHotChange: TVTHotNodeChangeEvent read FOnHotChange write FOnHotChange;
  2805. property OnIncrementalSearch: TVTIncrementalSearchEvent read FOnIncrementalSearch write FOnIncrementalSearch;
  2806. property OnInitChildren: TVTInitChildrenEvent read FOnInitChildren write FOnInitChildren;
  2807. property OnInitNode: TVTInitNodeEvent read FOnInitNode write FOnInitNode;
  2808. property OnKeyAction: TVTKeyActionEvent read FOnKeyAction write FOnKeyAction;
  2809. property OnLoadNode: TVTSaveNodeEvent read FOnLoadNode write FOnLoadNode;
  2810. property OnLoadTree: TVTSaveTreeEvent read FOnLoadTree write FOnLoadTree;
  2811. property OnMeasureItem: TVTMeasureItemEvent read FOnMeasureItem write FOnMeasureItem;
  2812. property OnMouseEnter: TNotifyEvent read FOnMouseEnter write FOnMouseEnter;
  2813. property OnMouseLeave: TNotifyEvent read FOnMouseLeave write FOnMouseLeave;
  2814. property OnNodeClick: TVTNodeClickEvent read FOnNodeClick write FOnNodeClick;
  2815. property OnNodeCopied: TVTNodeCopiedEvent read FOnNodeCopied write FOnNodeCopied;
  2816. property OnNodeCopying: TVTNodeCopyingEvent read FOnNodeCopying write FOnNodeCopying;
  2817. property OnNodeDblClick: TVTNodeClickEvent read FOnNodeDblClick write FOnNodeDblClick;
  2818. property OnNodeExport: TVTNodeExportEvent read FOnNodeExport write FOnNodeExport;
  2819. property OnNodeHeightTracking: TVTNodeHeightTrackingEvent read FOnNodeHeightTracking write FOnNodeHeightTracking;
  2820. property OnNodeHeightDblClickResize: TVTNodeHeightDblClickResizeEvent read FOnNodeHeightDblClickResize
  2821. write FOnNodeHeightDblClickResize;
  2822. property OnNodeMoved: TVTNodeMovedEvent read FOnNodeMoved write FOnNodeMoved;
  2823. property OnNodeMoving: TVTNodeMovingEvent read FOnNodeMoving write FOnNodeMoving;
  2824. property OnPaintBackground: TVTBackgroundPaintEvent read FOnPaintBackground write FOnPaintBackground;
  2825. property OnRemoveFromSelection: TVTRemoveFromSelectionEvent read FOnRemoveFromSelection write FOnRemoveFromSelection;
  2826. property OnRenderOLEData: TVTRenderOLEDataEvent read FOnRenderOLEData write FOnRenderOLEData;
  2827. property OnResetNode: TVTChangeEvent read FOnResetNode write FOnResetNode;
  2828. property OnSaveNode: TVTSaveNodeEvent read FOnSaveNode write FOnSaveNode;
  2829. property OnSaveTree: TVTSaveTreeEvent read FOnSaveTree write FOnSaveTree;
  2830. property OnScroll: TVTScrollEvent read FOnScroll write FOnScroll;
  2831. property OnShowScrollBar: TVTScrollBarShowEvent read FOnShowScrollBar write FOnShowScrollBar;
  2832. property OnStartOperation: TVTOperationEvent read FOnStartOperation write FOnStartOperation;
  2833. property OnStateChange: TVTStateChangeEvent read FOnStateChange write FOnStateChange;
  2834. property OnStructureChange: TVTStructureChangeEvent read FOnStructureChange write FOnStructureChange;
  2835. property OnUpdating: TVTUpdatingEvent read FOnUpdating write FOnUpdating;
  2836. public
  2837. constructor Create(AOwner: TComponent); override;
  2838. destructor Destroy; override;
  2839. function AbsoluteIndex(Node: PVirtualNode): Cardinal;
  2840. function AddChild(Parent: PVirtualNode; UserData: Pointer = nil): PVirtualNode; virtual;
  2841. procedure AddFromStream(Stream: TStream; TargetNode: PVirtualNode);
  2842. procedure AfterConstruction; override;
  2843. procedure Assign(Source: TPersistent); override;
  2844. procedure BeginDrag(Immediate: Boolean; Threshold: Integer = -1);
  2845. procedure BeginSynch;
  2846. procedure BeginUpdate; virtual;
  2847. procedure CancelCutOrCopy;
  2848. function CancelEditNode: Boolean;
  2849. procedure CancelOperation;
  2850. function CanEdit(Node: PVirtualNode; Column: TColumnIndex): Boolean; virtual;
  2851. function CanFocus: Boolean; override;
  2852. procedure Clear; virtual;
  2853. procedure ClearChecked;
  2854. procedure ClearSelection;
  2855. function CopyTo(Source: PVirtualNode; Tree: TBaseVirtualTree; Mode: TVTNodeAttachMode;
  2856. ChildrenOnly: Boolean): PVirtualNode; overload;
  2857. function CopyTo(Source, Target: PVirtualNode; Mode: TVTNodeAttachMode;
  2858. ChildrenOnly: Boolean): PVirtualNode; overload;
  2859. procedure CopyToClipboard; virtual;
  2860. procedure CutToClipboard; virtual;
  2861. procedure DeleteChildren(Node: PVirtualNode; ResetHasChildren: Boolean = False);
  2862. procedure DeleteNode(Node: PVirtualNode; Reindex: Boolean = True);
  2863. procedure DeleteSelectedNodes; virtual;
  2864. function Dragging: Boolean;
  2865. function EditNode(Node: PVirtualNode; Column: TColumnIndex): Boolean; virtual;
  2866. function EndEditNode: Boolean;
  2867. procedure EndSynch;
  2868. procedure EndUpdate; virtual;
  2869. procedure EnsureNodeSelected(); virtual;
  2870. function ExecuteAction(Action: TBasicAction): Boolean; override;
  2871. procedure FinishCutOrCopy;
  2872. procedure FlushClipboard;
  2873. procedure FullCollapse(Node: PVirtualNode = nil); virtual;
  2874. procedure FullExpand(Node: PVirtualNode = nil); virtual;
  2875. function GetControlsAlignment: TAlignment; override;
  2876. function GetDisplayRect(Node: PVirtualNode; Column: TColumnIndex; TextOnly: Boolean; Unclipped: Boolean = False;
  2877. ApplyCellContentMargin: Boolean = False): TRect;
  2878. function GetEffectivelyFiltered(Node: PVirtualNode): Boolean;
  2879. function GetEffectivelyVisible(Node: PVirtualNode): Boolean;
  2880. function GetFirst(ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  2881. function GetFirstChecked(State: TCheckState = csCheckedNormal; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  2882. function GetFirstChild(Node: PVirtualNode): PVirtualNode;
  2883. function GetFirstChildNoInit(Node: PVirtualNode): PVirtualNode;
  2884. function GetFirstCutCopy(ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  2885. function GetFirstInitialized(ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  2886. function GetFirstLeaf: PVirtualNode;
  2887. function GetFirstLevel(NodeLevel: Cardinal): PVirtualNode;
  2888. function GetFirstNoInit(ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  2889. function GetFirstSelected(ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  2890. function GetFirstVisible(Node: PVirtualNode = nil; ConsiderChildrenAbove: Boolean = True;
  2891. IncludeFiltered: Boolean = False): PVirtualNode;
  2892. function GetFirstVisibleChild(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode;
  2893. function GetFirstVisibleChildNoInit(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode;
  2894. function GetFirstVisibleNoInit(Node: PVirtualNode = nil; ConsiderChildrenAbove: Boolean = True;
  2895. IncludeFiltered: Boolean = False): PVirtualNode;
  2896. procedure GetHitTestInfoAt(X, Y: Integer; Relative: Boolean; var HitInfo: THitInfo); virtual;
  2897. function GetLast(Node: PVirtualNode = nil; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  2898. function GetLastInitialized(Node: PVirtualNode = nil; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  2899. function GetLastNoInit(Node: PVirtualNode = nil; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  2900. function GetLastChild(Node: PVirtualNode): PVirtualNode;
  2901. function GetLastChildNoInit(Node: PVirtualNode): PVirtualNode;
  2902. function GetLastVisible(Node: PVirtualNode = nil; ConsiderChildrenAbove: Boolean = True;
  2903. IncludeFiltered: Boolean = False): PVirtualNode;
  2904. function GetLastVisibleChild(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode;
  2905. function GetLastVisibleChildNoInit(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode;
  2906. function GetLastVisibleNoInit(Node: PVirtualNode = nil; ConsiderChildrenAbove: Boolean = True;
  2907. IncludeFiltered: Boolean = False): PVirtualNode;
  2908. function GetMaxColumnWidth(Column: TColumnIndex; UseSmartColumnWidth: Boolean = False): Integer; virtual;
  2909. function GetNext(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  2910. function GetNextChecked(Node: PVirtualNode; State: TCheckState = csCheckedNormal;
  2911. ConsiderChildrenAbove: Boolean = False): PVirtualNode; overload;
  2912. function GetNextChecked(Node: PVirtualNode; ConsiderChildrenAbove: Boolean): PVirtualNode; overload;
  2913. function GetNextCutCopy(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  2914. function GetNextInitialized(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  2915. function GetNextLeaf(Node: PVirtualNode): PVirtualNode;
  2916. function GetNextLevel(Node: PVirtualNode; NodeLevel: Cardinal): PVirtualNode;
  2917. function GetNextNoInit(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  2918. function GetNextSelected(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  2919. function GetNextSibling(Node: PVirtualNode): PVirtualNode;
  2920. function GetNextSiblingNoInit(Node: PVirtualNode): PVirtualNode;
  2921. function GetNextVisible(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = True): PVirtualNode;
  2922. function GetNextVisibleNoInit(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = True): PVirtualNode;
  2923. function GetNextVisibleSibling(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode;
  2924. function GetNextVisibleSiblingNoInit(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode;
  2925. function GetNodeAt(const P: TPoint): PVirtualNode; overload; {$if CompilerVersion >= 18}inline;{$ifend}
  2926. function GetNodeAt(X, Y: Integer): PVirtualNode; overload;
  2927. function GetNodeAt(X, Y: Integer; Relative: Boolean; var NodeTop: Integer): PVirtualNode; overload;
  2928. function GetNodeData(Node: PVirtualNode): Pointer;
  2929. function GetNodeLevel(Node: PVirtualNode): Cardinal;
  2930. function GetPrevious(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  2931. function GetPreviousChecked(Node: PVirtualNode; State: TCheckState = csCheckedNormal;
  2932. ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  2933. function GetPreviousCutCopy(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  2934. function GetPreviousInitialized(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  2935. function GetPreviousLeaf(Node: PVirtualNode): PVirtualNode;
  2936. function GetPreviousLevel(Node: PVirtualNode; NodeLevel: Cardinal): PVirtualNode;
  2937. function GetPreviousNoInit(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  2938. function GetPreviousSelected(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  2939. function GetPreviousSibling(Node: PVirtualNode): PVirtualNode;
  2940. function GetPreviousSiblingNoInit(Node: PVirtualNode): PVirtualNode;
  2941. function GetPreviousVisible(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = True): PVirtualNode;
  2942. function GetPreviousVisibleNoInit(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = True): PVirtualNode;
  2943. function GetPreviousVisibleSibling(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode;
  2944. function GetPreviousVisibleSiblingNoInit(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode;
  2945. function GetSortedCutCopySet(Resolve: Boolean): TNodeArray;
  2946. function GetSortedSelection(Resolve: Boolean): TNodeArray;
  2947. procedure GetTextInfo(Node: PVirtualNode; Column: TColumnIndex; const AFont: TFont; var R: TRect;
  2948. var Text: UnicodeString); virtual;
  2949. function GetTreeRect: TRect;
  2950. function GetVisibleParent(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode;
  2951. function HasAsParent(Node, PotentialParent: PVirtualNode): Boolean;
  2952. function InsertNode(Node: PVirtualNode; Mode: TVTNodeAttachMode; UserData: Pointer = nil): PVirtualNode;
  2953. procedure InvalidateChildren(Node: PVirtualNode; Recursive: Boolean);
  2954. procedure InvalidateColumn(Column: TColumnIndex);
  2955. function InvalidateNode(Node: PVirtualNode): TRect; virtual;
  2956. procedure InvalidateToBottom(Node: PVirtualNode);
  2957. procedure InvertSelection(VisibleOnly: Boolean);
  2958. function IsEditing: Boolean;
  2959. function IsMouseSelecting: Boolean;
  2960. function IsEmpty: Boolean;
  2961. function IterateSubtree(Node: PVirtualNode; Callback: TVTGetNodeProc; Data: Pointer; Filter: TVirtualNodeStates = [];
  2962. DoInit: Boolean = False; ChildNodesOnly: Boolean = False): PVirtualNode;
  2963. procedure LoadFromFile(const FileName: TFileName); virtual;
  2964. procedure LoadFromStream(Stream: TStream); virtual;
  2965. procedure MeasureItemHeight(const Canvas: TCanvas; Node: PVirtualNode); virtual;
  2966. procedure MoveTo(Source, Target: PVirtualNode; Mode: TVTNodeAttachMode; ChildrenOnly: Boolean); overload;
  2967. procedure MoveTo(Node: PVirtualNode; Tree: TBaseVirtualTree; Mode: TVTNodeAttachMode;
  2968. ChildrenOnly: Boolean); overload;
  2969. procedure PaintTree(TargetCanvas: TCanvas; Window: TRect; Target: TPoint; PaintOptions: TVTInternalPaintOptions;
  2970. PixelFormat: TPixelFormat = pfDevice); virtual;
  2971. function PasteFromClipboard: Boolean; virtual;
  2972. procedure PrepareDragImage(HotSpot: TPoint; const DataObject: IDataObject);
  2973. procedure Print(Printer: TPrinter; PrintHeader: Boolean);
  2974. function ProcessDrop(DataObject: IDataObject; TargetNode: PVirtualNode; var Effect: Integer; Mode:
  2975. TVTNodeAttachMode): Boolean;
  2976. function ProcessOLEData(Source: TBaseVirtualTree; DataObject: IDataObject; TargetNode: PVirtualNode;
  2977. Mode: TVTNodeAttachMode; Optimized: Boolean): Boolean;
  2978. procedure RepaintNode(Node: PVirtualNode);
  2979. procedure ReinitChildren(Node: PVirtualNode; Recursive: Boolean); virtual;
  2980. procedure ReinitNode(Node: PVirtualNode; Recursive: Boolean); virtual;
  2981. procedure ResetNode(Node: PVirtualNode); virtual;
  2982. procedure SaveToFile(const FileName: TFileName);
  2983. procedure SaveToStream(Stream: TStream; Node: PVirtualNode = nil); virtual;
  2984. function ScrollIntoView(Node: PVirtualNode; Center: Boolean; Horizontally: Boolean = False): Boolean; overload;
  2985. function ScrollIntoView(Column: TColumnIndex; Center: Boolean; Node: PVirtualNode = nil): Boolean; overload;
  2986. procedure SelectAll(VisibleOnly: Boolean);
  2987. procedure Sort(Node: PVirtualNode; Column: TColumnIndex; Direction: TSortDirection; DoInit: Boolean = True); virtual;
  2988. procedure SortTree(Column: TColumnIndex; Direction: TSortDirection; DoInit: Boolean = True); virtual;
  2989. procedure ToggleNode(Node: PVirtualNode);
  2990. function UpdateAction(Action: TBasicAction): Boolean; override;
  2991. procedure UpdateHorizontalRange;
  2992. procedure UpdateHorizontalScrollBar(DoRepaint: Boolean);
  2993. procedure UpdateRanges;
  2994. procedure UpdateScrollBars(DoRepaint: Boolean); virtual;
  2995. procedure UpdateVerticalRange;
  2996. procedure UpdateVerticalScrollBar(DoRepaint: Boolean);
  2997. function UseRightToLeftReading: Boolean;
  2998. procedure ValidateChildren(Node: PVirtualNode; Recursive: Boolean);
  2999. procedure ValidateNode(Node: PVirtualNode; Recursive: Boolean);
  3000. { Enumerations }
  3001. function Nodes(ConsiderChildrenAbove: Boolean = False): TVTVirtualNodeEnumeration;
  3002. function CheckedNodes(State: TCheckState = csCheckedNormal; ConsiderChildrenAbove: Boolean = False): TVTVirtualNodeEnumeration;
  3003. function ChildNodes(Node: PVirtualNode): TVTVirtualNodeEnumeration;
  3004. function CutCopyNodes(ConsiderChildrenAbove: Boolean = False): TVTVirtualNodeEnumeration;
  3005. function InitializedNodes(ConsiderChildrenAbove: Boolean = False): TVTVirtualNodeEnumeration;
  3006. function LeafNodes: TVTVirtualNodeEnumeration;
  3007. function LevelNodes(NodeLevel: Cardinal): TVTVirtualNodeEnumeration;
  3008. function NoInitNodes(ConsiderChildrenAbove: Boolean = False): TVTVirtualNodeEnumeration;
  3009. function SelectedNodes(ConsiderChildrenAbove: Boolean = False): TVTVirtualNodeEnumeration;
  3010. function VisibleNodes(Node: PVirtualNode = nil; ConsiderChildrenAbove: Boolean = True;
  3011. IncludeFiltered: Boolean = False): TVTVirtualNodeEnumeration;
  3012. function VisibleChildNodes(Node: PVirtualNode; IncludeFiltered: Boolean = False): TVTVirtualNodeEnumeration;
  3013. function VisibleChildNoInitNodes(Node: PVirtualNode; IncludeFiltered: Boolean = False): TVTVirtualNodeEnumeration;
  3014. function VisibleNoInitNodes(Node: PVirtualNode = nil; ConsiderChildrenAbove: Boolean = True;
  3015. IncludeFiltered: Boolean = False): TVTVirtualNodeEnumeration;
  3016. property Accessible: IAccessible read FAccessible write FAccessible;
  3017. property AccessibleItem: IAccessible read FAccessibleItem write FAccessibleItem;
  3018. property AccessibleName: string read FAccessibleName write FAccessibleName;
  3019. property BottomNode: PVirtualNode read GetBottomNode write SetBottomNode;
  3020. property CheckedCount: Integer read GetCheckedCount;
  3021. property CheckImages: TCustomImageList read FCheckImages;
  3022. property CheckState[Node: PVirtualNode]: TCheckState read GetCheckState write SetCheckState;
  3023. property CheckType[Node: PVirtualNode]: TCheckType read GetCheckType write SetCheckType;
  3024. property ChildCount[Node: PVirtualNode]: Cardinal read GetChildCount write SetChildCount;
  3025. property ChildrenInitialized[Node: PVirtualNode]: Boolean read GetChildrenInitialized;
  3026. property CutCopyCount: Integer read GetCutCopyCount;
  3027. property DragImage: TVTDragImage read FDragImage;
  3028. property DragManager: IVTDragManager read GetDragManager;
  3029. property DropTargetNode: PVirtualNode read FDropTargetNode write FDropTargetNode;
  3030. property EditLink: IVTEditLink read FEditLink;
  3031. property EmptyListMessage: UnicodeString read FEmptyListMessage write SetEmptyListMessage;
  3032. property Expanded[Node: PVirtualNode]: Boolean read GetExpanded write SetExpanded;
  3033. property FocusedColumn: TColumnIndex read FFocusedColumn write SetFocusedColumn default InvalidColumn;
  3034. property FocusedNode: PVirtualNode read FFocusedNode write SetFocusedNode;
  3035. property Font;
  3036. property FullyVisible[Node: PVirtualNode]: Boolean read GetFullyVisible write SetFullyVisible;
  3037. property HasChildren[Node: PVirtualNode]: Boolean read GetHasChildren write SetHasChildren;
  3038. property HotNode: PVirtualNode read FCurrentHotNode;
  3039. property IsDisabled[Node: PVirtualNode]: Boolean read GetDisabled write SetDisabled;
  3040. property IsEffectivelyFiltered[Node: PVirtualNode]: Boolean read GetEffectivelyFiltered;
  3041. property IsEffectivelyVisible[Node: PVirtualNode]: Boolean read GetEffectivelyVisible;
  3042. property IsFiltered[Node: PVirtualNode]: Boolean read GetFiltered write SetFiltered;
  3043. property IsVisible[Node: PVirtualNode]: Boolean read GetVisible write SetVisible;
  3044. property MultiLine[Node: PVirtualNode]: Boolean read GetMultiline write SetMultiline;
  3045. property NodeHeight[Node: PVirtualNode]: Cardinal read GetNodeHeight write SetNodeHeight;
  3046. property NodeParent[Node: PVirtualNode]: PVirtualNode read GetNodeParent write SetNodeParent;
  3047. property OffsetX: Integer read FOffsetX write SetOffsetX;
  3048. property OffsetXY: TPoint read GetOffsetXY write SetOffsetXY;
  3049. property OffsetY: Integer read FOffsetY write SetOffsetY;
  3050. property OperationCount: Cardinal read FOperationCount;
  3051. property RootNode: PVirtualNode read FRoot;
  3052. property SearchBuffer: UnicodeString read FSearchBuffer;
  3053. property Selected[Node: PVirtualNode]: Boolean read GetSelected write SetSelected;
  3054. property SelectionLocked: Boolean read FSelectionLocked write FSelectionLocked;
  3055. property TotalCount: Cardinal read GetTotalCount;
  3056. property TreeStates: TVirtualTreeStates read FStates write FStates;
  3057. property SelectedCount: Integer read FSelectionCount;
  3058. property TopNode: PVirtualNode read GetTopNode write SetTopNode;
  3059. property VerticalAlignment[Node: PVirtualNode]: Byte read GetVerticalAlignment write SetVerticalAlignment;
  3060. property VisibleCount: Cardinal read FVisibleCount;
  3061. property VisiblePath[Node: PVirtualNode]: Boolean read GetVisiblePath write SetVisiblePath;
  3062. property UpdateCount: Cardinal read FUpdateCount;
  3063. property DoubleBuffered: Boolean read GetDoubleBuffered write SetDoubleBuffered default True;
  3064. end;
  3065. // --------- TCustomVirtualStringTree
  3066. // Options regarding strings (useful only for the string tree and descendants):
  3067. TVTStringOption = (
  3068. toSaveCaptions, // If set then the caption is automatically saved with the tree node, regardless of what is
  3069. // saved in the user data.
  3070. toShowStaticText, // Show static text in a caption which can be differently formatted than the caption
  3071. // but cannot be edited.
  3072. toAutoAcceptEditChange // Automatically accept changes during edit if the user finishes editing other then
  3073. // VK_RETURN or ESC. If not set then changes are cancelled.
  3074. );
  3075. TVTStringOptions = set of TVTStringOption;
  3076. const
  3077. DefaultStringOptions = [toSaveCaptions, toAutoAcceptEditChange];
  3078. type
  3079. TCustomStringTreeOptions = class(TCustomVirtualTreeOptions)
  3080. private
  3081. FStringOptions: TVTStringOptions;
  3082. procedure SetStringOptions(const Value: TVTStringOptions);
  3083. protected
  3084. property StringOptions: TVTStringOptions read FStringOptions write SetStringOptions default DefaultStringOptions;
  3085. public
  3086. constructor Create(AOwner: TBaseVirtualTree); override;
  3087. procedure AssignTo(Dest: TPersistent); override;
  3088. end;
  3089. TStringTreeOptions = class(TCustomStringTreeOptions)
  3090. published
  3091. property AnimationOptions;
  3092. property AutoOptions;
  3093. property ExportMode;
  3094. property MiscOptions;
  3095. property PaintOptions;
  3096. property SelectionOptions;
  3097. property StringOptions;
  3098. end;
  3099. TCustomVirtualStringTree = class;
  3100. // Edit support classes.
  3101. TStringEditLink = class;
  3102. {$ifdef TntSupport}
  3103. TVTEdit = class(TTntEdit)
  3104. {$else}
  3105. TVTEdit = class(TCustomEdit)
  3106. {$endif TntSupport}
  3107. private
  3108. procedure CMAutoAdjust(var Message: TMessage); message CM_AUTOADJUST;
  3109. procedure CMExit(var Message: TMessage); message CM_EXIT;
  3110. procedure CMRelease(var Message: TMessage); message CM_RELEASE;
  3111. procedure CNCommand(var Message: TWMCommand); message CN_COMMAND;
  3112. procedure WMChar(var Message: TWMChar); message WM_CHAR;
  3113. procedure WMDestroy(var Message: TWMDestroy); message WM_DESTROY;
  3114. procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
  3115. procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN;
  3116. protected
  3117. FRefLink: IVTEditLink;
  3118. FLink: TStringEditLink;
  3119. procedure AutoAdjustSize; virtual;
  3120. procedure CreateParams(var Params: TCreateParams); override;
  3121. public
  3122. constructor Create(Link: TStringEditLink); reintroduce;
  3123. procedure Release; virtual;
  3124. property AutoSelect;
  3125. property AutoSize;
  3126. property BorderStyle;
  3127. property CharCase;
  3128. property HideSelection;
  3129. property MaxLength;
  3130. property OEMConvert;
  3131. property PasswordChar;
  3132. end;
  3133. TStringEditLink = class(TInterfacedObject, IVTEditLink)
  3134. private
  3135. FEdit: TVTEdit; // A normal custom edit control.
  3136. protected
  3137. FTree: TCustomVirtualStringTree; // A back reference to the tree calling.
  3138. FNode: PVirtualNode; // The node to be edited.
  3139. FColumn: TColumnIndex; // The column of the node.
  3140. FAlignment: TAlignment;
  3141. FTextBounds: TRect; // Smallest rectangle around the text.
  3142. FStopping: Boolean; // Set to True when the edit link requests stopping the edit action.
  3143. procedure SetEdit(const Value: TVTEdit); // Setter for the FEdit member;
  3144. public
  3145. constructor Create; virtual;
  3146. destructor Destroy; override;
  3147. property Node : PVirtualNode read FNode; // [IPK] Make FNode accessible
  3148. property Column: TColumnIndex read FColumn; // [IPK] Make Column(Index) accessible
  3149. function BeginEdit: Boolean; virtual; stdcall;
  3150. function CancelEdit: Boolean; virtual; stdcall;
  3151. property Edit: TVTEdit read FEdit write SetEdit;
  3152. function EndEdit: Boolean; virtual; stdcall;
  3153. function GetBounds: TRect; virtual; stdcall;
  3154. function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; virtual; stdcall;
  3155. procedure ProcessMessage(var Message: TMessage); virtual; stdcall;
  3156. procedure SetBounds(R: TRect); virtual; stdcall;
  3157. end;
  3158. // Describes the type of text to return in the text and draw info retrival events.
  3159. TVSTTextType = (
  3160. ttNormal, // normal label of the node, this is also the text which can be edited
  3161. ttStatic // static (non-editable) text after the normal text
  3162. );
  3163. // Describes the source to use when converting a string tree into a string for clipboard etc.
  3164. TVSTTextSourceType = (
  3165. tstAll, // All nodes are rendered. Initialization is done on the fly.
  3166. tstInitialized, // Only initialized nodes are rendered.
  3167. tstSelected, // Only selected nodes are rendered.
  3168. tstCutCopySet, // Only nodes currently marked as being in the cut/copy clipboard set are rendered.
  3169. tstVisible, // Only visible nodes are rendered.
  3170. tstChecked // Only checked nodes are rendered
  3171. );
  3172. TVTPaintText = procedure(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
  3173. TextType: TVSTTextType) of object;
  3174. TVSTGetTextEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
  3175. TextType: TVSTTextType; var CellText: UnicodeString) of object;
  3176. TVSTGetHintEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
  3177. var LineBreakStyle: TVTTooltipLineBreakStyle; var HintText: UnicodeString) of object;
  3178. // New text can only be set for variable caption.
  3179. TVSTNewTextEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
  3180. NewText: UnicodeString) of object;
  3181. TVSTShortenStringEvent = procedure(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; Node: PVirtualNode;
  3182. Column: TColumnIndex; const S: UnicodeString; TextSpace: Integer; var Result: UnicodeString;
  3183. var Done: Boolean) of object;
  3184. TVTMeasureTextEvent = procedure(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; Node: PVirtualNode;
  3185. Column: TColumnIndex; const Text: UnicodeString; var Extent: Integer) of object;
  3186. TVTDrawTextEvent = procedure(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; Node: PVirtualNode;
  3187. Column: TColumnIndex; const Text: UnicodeString; const CellRect: TRect; var DefaultDraw: Boolean) of object;
  3188. TCustomVirtualStringTree = class(TBaseVirtualTree)
  3189. private
  3190. FDefaultText: UnicodeString; // text to show if there's no OnGetText event handler (e.g. at design time)
  3191. FTextHeight: Integer; // true size of the font
  3192. FEllipsisWidth: Integer; // width of '...' for the current font
  3193. FInternalDataOffset: Cardinal; // offset to the internal data of the string tree
  3194. FOnPaintText: TVTPaintText; // triggered before either normal or fixed text is painted to allow
  3195. // even finer customization (kind of sub cell painting)
  3196. FOnGetText: TVSTGetTextEvent; // used to retrieve the string to be displayed for a specific node
  3197. FOnGetHint: TVSTGetHintEvent; // used to retrieve the hint to be displayed for a specific node
  3198. FOnNewText: TVSTNewTextEvent; // used to notify the application about an edited node caption
  3199. FOnShortenString: TVSTShortenStringEvent; // used to allow the application a customized string shortage
  3200. FOnMeasureTextWidth: TVTMeasureTextEvent; // used to adjust the width of the cells
  3201. FOnMeasureTextHeight: TVTMeasureTextEvent;
  3202. FOnDrawText: TVTDrawTextEvent; // used to custom draw the node text
  3203. function GetImageText(Node: PVirtualNode; Kind: TVTImageKind;
  3204. Column: TColumnIndex): UnicodeString;
  3205. procedure GetRenderStartValues(Source: TVSTTextSourceType; var Node: PVirtualNode;
  3206. var NextNodeProc: TGetNextNodeProc);
  3207. function GetOptions: TCustomStringTreeOptions;
  3208. function GetStaticText(Node: PVirtualNode; Column: TColumnIndex): UnicodeString;
  3209. function GetText(Node: PVirtualNode; Column: TColumnIndex): UnicodeString;
  3210. procedure ReadText(Reader: TReader);
  3211. procedure SetDefaultText(const Value: UnicodeString);
  3212. procedure SetOptions(const Value: TCustomStringTreeOptions);
  3213. procedure SetText(Node: PVirtualNode; Column: TColumnIndex; const Value: UnicodeString);
  3214. procedure WriteText(Writer: TWriter);
  3215. procedure WMSetFont(var Msg: TWMSetFont); message WM_SETFONT;
  3216. procedure GetDataFromGrid(const AStrings : TStringList; const IncludeHeading : Boolean = True);
  3217. protected
  3218. FPreviouslySelected: TStringList;
  3219. procedure InitializeTextProperties(var PaintInfo: TVTPaintInfo); // [IPK] - private to protected
  3220. procedure PaintNormalText(var PaintInfo: TVTPaintInfo; TextOutFlags: Integer; Text: UnicodeString); virtual; // [IPK] - private to protected
  3221. procedure PaintStaticText(const PaintInfo: TVTPaintInfo; TextOutFlags: Integer; const Text: UnicodeString); virtual; // [IPK] - private to protected
  3222. procedure AdjustPaintCellRect(var PaintInfo: TVTPaintInfo; var NextNonEmpty: TColumnIndex); override;
  3223. function CanExportNode(Node: PVirtualNode): Boolean;
  3224. function CalculateStaticTextWidth(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; Text: UnicodeString): Integer; virtual;
  3225. function CalculateTextWidth(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; Text: UnicodeString): Integer; virtual;
  3226. function ColumnIsEmpty(Node: PVirtualNode; Column: TColumnIndex): Boolean; override;
  3227. procedure DefineProperties(Filer: TFiler); override;
  3228. function DoCreateEditor(Node: PVirtualNode; Column: TColumnIndex): IVTEditLink; override;
  3229. function DoGetNodeHint(Node: PVirtualNode; Column: TColumnIndex; var LineBreakStyle: TVTTooltipLineBreakStyle): UnicodeString; override;
  3230. function DoGetNodeTooltip(Node: PVirtualNode; Column: TColumnIndex; var LineBreakStyle: TVTTooltipLineBreakStyle): UnicodeString; override;
  3231. function DoGetNodeExtraWidth(Node: PVirtualNode; Column: TColumnIndex; Canvas: TCanvas = nil): Integer; override;
  3232. function DoGetNodeWidth(Node: PVirtualNode; Column: TColumnIndex; Canvas: TCanvas = nil): Integer; override;
  3233. procedure DoGetText(Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
  3234. var Text: UnicodeString); virtual;
  3235. function DoIncrementalSearch(Node: PVirtualNode; const Text: UnicodeString): Integer; override;
  3236. procedure DoNewText(Node: PVirtualNode; Column: TColumnIndex; Text: UnicodeString); virtual;
  3237. procedure DoPaintNode(var PaintInfo: TVTPaintInfo); override;
  3238. procedure DoPaintText(Node: PVirtualNode; const Canvas: TCanvas; Column: TColumnIndex;
  3239. TextType: TVSTTextType); virtual;
  3240. function DoShortenString(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; const S: UnicodeString; Width: Integer;
  3241. EllipsisWidth: Integer = 0): UnicodeString; virtual;
  3242. procedure DoTextDrawing(var PaintInfo: TVTPaintInfo; Text: UnicodeString; CellRect: TRect; DrawFormat: Cardinal); virtual;
  3243. function DoTextMeasuring(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; Text: UnicodeString): TSize; virtual;
  3244. function GetOptionsClass: TTreeOptionsClass; override;
  3245. function InternalData(Node: PVirtualNode): Pointer;
  3246. procedure MainColumnChanged; override;
  3247. function ReadChunk(Stream: TStream; Version: Integer; Node: PVirtualNode; ChunkType,
  3248. ChunkSize: Integer): Boolean; override;
  3249. procedure ReadOldStringOptions(Reader: TReader);
  3250. function RenderOLEData(const FormatEtcIn: TFormatEtc; out Medium: TStgMedium; ForClipboard: Boolean): HResult; override;
  3251. procedure WriteChunks(Stream: TStream; Node: PVirtualNode); override;
  3252. property DefaultText: UnicodeString read FDefaultText write SetDefaultText stored False;
  3253. property EllipsisWidth: Integer read FEllipsisWidth;
  3254. property TreeOptions: TCustomStringTreeOptions read GetOptions write SetOptions;
  3255. property OnGetHint: TVSTGetHintEvent read FOnGetHint write FOnGetHint;
  3256. property OnGetText: TVSTGetTextEvent read FOnGetText write FOnGetText;
  3257. property OnNewText: TVSTNewTextEvent read FOnNewText write FOnNewText;
  3258. property OnPaintText: TVTPaintText read FOnPaintText write FOnPaintText;
  3259. property OnShortenString: TVSTShortenStringEvent read FOnShortenString write FOnShortenString;
  3260. property OnMeasureTextWidth: TVTMeasureTextEvent read FOnMeasureTextWidth write FOnMeasureTextWidth;
  3261. property OnMeasureTextHeight: TVTMeasureTextEvent read FOnMeasureTextHeight write FOnMeasureTextHeight;
  3262. property OnDrawText: TVTDrawTextEvent read FOnDrawText write FOnDrawText;
  3263. public
  3264. constructor Create(AOwner: TComponent); override;
  3265. destructor Destroy(); override;
  3266. function AddChild(Parent: PVirtualNode; UserData: Pointer = nil): PVirtualNode; override;
  3267. function ComputeNodeHeight(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; S: UnicodeString = ''): Integer; virtual;
  3268. function ContentToClipboard(Format: Word; Source: TVSTTextSourceType): HGLOBAL;
  3269. procedure ContentToCustom(Source: TVSTTextSourceType);
  3270. function ContentToHTML(Source: TVSTTextSourceType; Caption: UnicodeString = ''): RawByteString;
  3271. function ContentToRTF(Source: TVSTTextSourceType): RawByteString;
  3272. function ContentToText(Source: TVSTTextSourceType; Separator: Char): AnsiString; overload;
  3273. function ContentToText(Source: TVSTTextSourceType; const Separator: AnsiString): AnsiString; overload;
  3274. function ContentToUnicode(Source: TVSTTextSourceType; Separator: WideChar): UnicodeString; overload;
  3275. function ContentToUnicode(Source: TVSTTextSourceType; const Separator: UnicodeString): UnicodeString; overload;
  3276. procedure GetTextInfo(Node: PVirtualNode; Column: TColumnIndex; const AFont: TFont; var R: TRect;
  3277. var Text: UnicodeString); override;
  3278. function InvalidateNode(Node: PVirtualNode): TRect; override;
  3279. function Path(Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; Delimiter: WideChar): UnicodeString;
  3280. procedure ReinitNode(Node: PVirtualNode; Recursive: Boolean); override;
  3281. procedure AddToSelection(Node: PVirtualNode); override;
  3282. procedure RemoveFromSelection(Node: PVirtualNode); override;
  3283. function SaveToCSVFile(const FileNameWithPath : TFileName; const IncludeHeading : Boolean) : Boolean;
  3284. property ImageText[Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex]: UnicodeString read GetImageText;
  3285. property StaticText[Node: PVirtualNode; Column: TColumnIndex]: UnicodeString read GetStaticText;
  3286. property Text[Node: PVirtualNode; Column: TColumnIndex]: UnicodeString read GetText write SetText;
  3287. end;
  3288. TVirtualStringTree = class(TCustomVirtualStringTree)
  3289. private
  3290. function GetOptions: TStringTreeOptions;
  3291. procedure SetOptions(const Value: TStringTreeOptions);
  3292. protected
  3293. function GetOptionsClass: TTreeOptionsClass; override;
  3294. {$if CompilerVersion >= 23}
  3295. class constructor Create();
  3296. {$ifend}
  3297. public
  3298. property Canvas;
  3299. property RangeX;
  3300. property LastDragEffect;
  3301. published
  3302. property AccessibleName;
  3303. property Action;
  3304. property Align;
  3305. property Alignment;
  3306. property Anchors;
  3307. property AnimationDuration;
  3308. property AutoExpandDelay;
  3309. property AutoScrollDelay;
  3310. property AutoScrollInterval;
  3311. property Background;
  3312. property BackgroundOffsetX;
  3313. property BackgroundOffsetY;
  3314. property BiDiMode;
  3315. property BevelEdges;
  3316. property BevelInner;
  3317. property BevelOuter;
  3318. property BevelKind;
  3319. property BevelWidth;
  3320. property BorderStyle;
  3321. property BottomSpace;
  3322. property ButtonFillMode;
  3323. property ButtonStyle;
  3324. property BorderWidth;
  3325. property ChangeDelay;
  3326. property CheckImageKind;
  3327. property ClipboardFormats;
  3328. property Color;
  3329. property Colors;
  3330. property Constraints;
  3331. property Ctl3D;
  3332. property CustomCheckImages;
  3333. property DefaultNodeHeight;
  3334. property DefaultPasteMode;
  3335. property DefaultText;
  3336. property DragCursor;
  3337. property DragHeight;
  3338. property DragKind;
  3339. property DragImageKind;
  3340. property DragMode;
  3341. property DragOperations;
  3342. property DragType;
  3343. property DragWidth;
  3344. property DrawSelectionMode;
  3345. property EditDelay;
  3346. property EmptyListMessage;
  3347. property Enabled;
  3348. property Font;
  3349. property Header;
  3350. property HintAnimation;
  3351. property HintMode;
  3352. property HotCursor;
  3353. property Images;
  3354. property IncrementalSearch;
  3355. property IncrementalSearchDirection;
  3356. property IncrementalSearchStart;
  3357. property IncrementalSearchTimeout;
  3358. property Indent;
  3359. property LineMode;
  3360. property LineStyle;
  3361. property Margin;
  3362. property NodeAlignment;
  3363. property NodeDataSize;
  3364. property OperationCanceled;
  3365. property ParentBiDiMode;
  3366. property ParentColor default False;
  3367. property ParentCtl3D;
  3368. property ParentFont;
  3369. property ParentShowHint;
  3370. property PopupMenu;
  3371. property RootNodeCount;
  3372. property ScrollBarOptions;
  3373. property SelectionBlendFactor;
  3374. property SelectionCurveRadius;
  3375. property ShowHint;
  3376. property StateImages;
  3377. {$if CompilerVersion >= 24}
  3378. property StyleElements;
  3379. {$ifend}
  3380. property TabOrder;
  3381. property TabStop default True;
  3382. property TextMargin;
  3383. property TreeOptions: TStringTreeOptions read GetOptions write SetOptions;
  3384. property Visible;
  3385. property WantTabs;
  3386. property OnAddToSelection;
  3387. property OnAdvancedHeaderDraw;
  3388. property OnAfterAutoFitColumn;
  3389. property OnAfterAutoFitColumns;
  3390. property OnAfterCellPaint;
  3391. property OnAfterColumnExport;
  3392. property OnAfterColumnWidthTracking;
  3393. property OnAfterGetMaxColumnWidth;
  3394. property OnAfterHeaderExport;
  3395. property OnAfterHeaderHeightTracking;
  3396. property OnAfterItemErase;
  3397. property OnAfterItemPaint;
  3398. property OnAfterNodeExport;
  3399. property OnAfterPaint;
  3400. property OnAfterTreeExport;
  3401. property OnBeforeAutoFitColumn;
  3402. property OnBeforeAutoFitColumns;
  3403. property OnBeforeCellPaint;
  3404. property OnBeforeColumnExport;
  3405. property OnBeforeColumnWidthTracking;
  3406. property OnBeforeDrawTreeLine;
  3407. property OnBeforeGetMaxColumnWidth;
  3408. property OnBeforeHeaderExport;
  3409. property OnBeforeHeaderHeightTracking;
  3410. property OnBeforeItemErase;
  3411. property OnBeforeItemPaint;
  3412. property OnBeforeNodeExport;
  3413. property OnBeforePaint;
  3414. property OnBeforeTreeExport;
  3415. property OnCanSplitterResizeColumn;
  3416. property OnCanSplitterResizeHeader;
  3417. property OnCanSplitterResizeNode;
  3418. property OnChange;
  3419. property OnChecked;
  3420. property OnChecking;
  3421. property OnClick;
  3422. property OnCollapsed;
  3423. property OnCollapsing;
  3424. property OnColumnClick;
  3425. property OnColumnDblClick;
  3426. property OnColumnExport;
  3427. property OnColumnResize;
  3428. property OnColumnWidthDblClickResize;
  3429. property OnColumnWidthTracking;
  3430. property OnCompareNodes;
  3431. property OnContextPopup;
  3432. property OnCreateDataObject;
  3433. property OnCreateDragManager;
  3434. property OnCreateEditor;
  3435. property OnDblClick;
  3436. property OnDragAllowed;
  3437. property OnDragOver;
  3438. property OnDragDrop;
  3439. property OnDrawHint;
  3440. property OnDrawText;
  3441. property OnEditCancelled;
  3442. property OnEdited;
  3443. property OnEditing;
  3444. property OnEndDock;
  3445. property OnEndDrag;
  3446. property OnEndOperation;
  3447. property OnEnter;
  3448. property OnExit;
  3449. property OnExpanded;
  3450. property OnExpanding;
  3451. property OnFocusChanged;
  3452. property OnFocusChanging;
  3453. property OnFreeNode;
  3454. property OnGetCellIsEmpty;
  3455. property OnGetCursor;
  3456. property OnGetHeaderCursor;
  3457. property OnGetText;
  3458. property OnPaintText;
  3459. property OnGetHelpContext;
  3460. property OnGetHintKind;
  3461. property OnGetHintSize;
  3462. property OnGetImageIndex;
  3463. property OnGetImageIndexEx;
  3464. property OnGetImageText;
  3465. property OnGetHint;
  3466. property OnGetLineStyle;
  3467. property OnGetNodeDataSize;
  3468. property OnGetPopupMenu;
  3469. property OnGetUserClipboardFormats;
  3470. property OnHeaderClick;
  3471. property OnHeaderDblClick;
  3472. property OnHeaderDragged;
  3473. property OnHeaderDraggedOut;
  3474. property OnHeaderDragging;
  3475. property OnHeaderDraw;
  3476. property OnHeaderDrawQueryElements;
  3477. property OnHeaderHeightDblClickResize;
  3478. property OnHeaderHeightTracking;
  3479. property OnHeaderMouseDown;
  3480. property OnHeaderMouseMove;
  3481. property OnHeaderMouseUp;
  3482. property OnHotChange;
  3483. property OnIncrementalSearch;
  3484. property OnInitChildren;
  3485. property OnInitNode;
  3486. property OnKeyAction;
  3487. property OnKeyDown;
  3488. property OnKeyPress;
  3489. property OnKeyUp;
  3490. property OnLoadNode;
  3491. property OnLoadTree;
  3492. property OnMeasureItem;
  3493. property OnMeasureTextWidth;
  3494. property OnMeasureTextHeight;
  3495. property OnMouseDown;
  3496. property OnMouseMove;
  3497. property OnMouseUp;
  3498. property OnMouseWheel;
  3499. property OnMouseEnter;
  3500. property OnMouseLeave;
  3501. property OnNewText;
  3502. property OnNodeClick;
  3503. property OnNodeCopied;
  3504. property OnNodeCopying;
  3505. property OnNodeDblClick;
  3506. property OnNodeExport;
  3507. property OnNodeHeightDblClickResize;
  3508. property OnNodeHeightTracking;
  3509. property OnNodeMoved;
  3510. property OnNodeMoving;
  3511. property OnPaintBackground;
  3512. property OnRemoveFromSelection;
  3513. property OnRenderOLEData;
  3514. property OnResetNode;
  3515. property OnResize;
  3516. property OnSaveNode;
  3517. property OnSaveTree;
  3518. property OnScroll;
  3519. property OnShortenString;
  3520. property OnShowScrollBar;
  3521. property OnStartDock;
  3522. property OnStartDrag;
  3523. property OnStartOperation;
  3524. property OnStateChange;
  3525. property OnStructureChange;
  3526. property OnUpdating;
  3527. {$if CompilerVersion >= 22}
  3528. property OnCanResize;
  3529. property OnGesture;
  3530. property Touch;
  3531. {$ifend}
  3532. end;
  3533. TVTDrawNodeEvent = procedure(Sender: TBaseVirtualTree; const PaintInfo: TVTPaintInfo) of object;
  3534. TVTGetCellContentMarginEvent = procedure(Sender: TBaseVirtualTree; HintCanvas: TCanvas; Node: PVirtualNode;
  3535. Column: TColumnIndex; CellContentMarginType: TVTCellContentMarginType; var CellContentMargin: TPoint) of object;
  3536. TVTGetNodeWidthEvent = procedure(Sender: TBaseVirtualTree; HintCanvas: TCanvas; Node: PVirtualNode;
  3537. Column: TColumnIndex; var NodeWidth: Integer) of object;
  3538. // Tree descendant to let an application draw its stuff itself.
  3539. TCustomVirtualDrawTree = class(TBaseVirtualTree)
  3540. private
  3541. FOnDrawNode: TVTDrawNodeEvent;
  3542. FOnGetCellContentMargin: TVTGetCellContentMarginEvent;
  3543. FOnGetNodeWidth: TVTGetNodeWidthEvent;
  3544. protected
  3545. function DoGetCellContentMargin(Node: PVirtualNode; Column: TColumnIndex;
  3546. CellContentMarginType: TVTCellContentMarginType = ccmtAllSides; Canvas: TCanvas = nil): TPoint; override;
  3547. function DoGetNodeWidth(Node: PVirtualNode; Column: TColumnIndex; Canvas: TCanvas = nil): Integer; override;
  3548. procedure DoPaintNode(var PaintInfo: TVTPaintInfo); override;
  3549. function GetDefaultHintKind: TVTHintKind; override;
  3550. property OnDrawNode: TVTDrawNodeEvent read FOnDrawNode write FOnDrawNode;
  3551. property OnGetCellContentMargin: TVTGetCellContentMarginEvent read FOnGetCellContentMargin write FOnGetCellContentMargin;
  3552. property OnGetNodeWidth: TVTGetNodeWidthEvent read FOnGetNodeWidth write FOnGetNodeWidth;
  3553. end;
  3554. TVirtualDrawTree = class(TCustomVirtualDrawTree)
  3555. private
  3556. function GetOptions: TVirtualTreeOptions;
  3557. procedure SetOptions(const Value: TVirtualTreeOptions);
  3558. protected
  3559. function GetOptionsClass: TTreeOptionsClass; override;
  3560. {$if CompilerVersion >= 23}
  3561. class constructor Create();
  3562. {$ifend}
  3563. public
  3564. property Canvas;
  3565. property LastDragEffect;
  3566. published
  3567. property Action;
  3568. property Align;
  3569. property Alignment;
  3570. property Anchors;
  3571. property AnimationDuration;
  3572. property AutoExpandDelay;
  3573. property AutoScrollDelay;
  3574. property AutoScrollInterval;
  3575. property Background;
  3576. property BackgroundOffsetX;
  3577. property BackgroundOffsetY;
  3578. property BiDiMode;
  3579. property BevelEdges;
  3580. property BevelInner;
  3581. property BevelOuter;
  3582. property BevelKind;
  3583. property BevelWidth;
  3584. property BorderStyle;
  3585. property BottomSpace;
  3586. property ButtonFillMode;
  3587. property ButtonStyle;
  3588. property BorderWidth;
  3589. property ChangeDelay;
  3590. property CheckImageKind;
  3591. property ClipboardFormats;
  3592. property Color;
  3593. property Colors;
  3594. property Constraints;
  3595. property Ctl3D;
  3596. property CustomCheckImages;
  3597. property DefaultNodeHeight;
  3598. property DefaultPasteMode;
  3599. property DragCursor;
  3600. property DragHeight;
  3601. property DragKind;
  3602. property DragImageKind;
  3603. property DragMode;
  3604. property DragOperations;
  3605. property DragType;
  3606. property DragWidth;
  3607. property DrawSelectionMode;
  3608. property EditDelay;
  3609. property Enabled;
  3610. property Font;
  3611. property Header;
  3612. property HintAnimation;
  3613. property HintMode;
  3614. property HotCursor;
  3615. property Images;
  3616. property IncrementalSearch;
  3617. property IncrementalSearchDirection;
  3618. property IncrementalSearchStart;
  3619. property IncrementalSearchTimeout;
  3620. property Indent;
  3621. property LineMode;
  3622. property LineStyle;
  3623. property Margin;
  3624. property NodeAlignment;
  3625. property NodeDataSize;
  3626. property OperationCanceled;
  3627. property ParentBiDiMode;
  3628. property ParentColor default False;
  3629. property ParentCtl3D;
  3630. property ParentFont;
  3631. property ParentShowHint;
  3632. property PopupMenu;
  3633. property RootNodeCount;
  3634. property ScrollBarOptions;
  3635. property SelectionBlendFactor;
  3636. property SelectionCurveRadius;
  3637. property ShowHint;
  3638. property StateImages;
  3639. property TabOrder;
  3640. property TabStop default True;
  3641. property TextMargin;
  3642. property TreeOptions: TVirtualTreeOptions read GetOptions write SetOptions;
  3643. property Visible;
  3644. property WantTabs;
  3645. property OnAddToSelection;
  3646. property OnAdvancedHeaderDraw;
  3647. property OnAfterAutoFitColumn;
  3648. property OnAfterAutoFitColumns;
  3649. property OnAfterCellPaint;
  3650. property OnAfterColumnExport;
  3651. property OnAfterColumnWidthTracking;
  3652. property OnAfterGetMaxColumnWidth;
  3653. property OnAfterHeaderExport;
  3654. property OnAfterHeaderHeightTracking;
  3655. property OnAfterItemErase;
  3656. property OnAfterItemPaint;
  3657. property OnAfterNodeExport;
  3658. property OnAfterPaint;
  3659. property OnAfterTreeExport;
  3660. property OnBeforeAutoFitColumn;
  3661. property OnBeforeAutoFitColumns;
  3662. property OnBeforeCellPaint;
  3663. property OnBeforeColumnExport;
  3664. property OnBeforeColumnWidthTracking;
  3665. property OnBeforeDrawTreeLine;
  3666. property OnBeforeGetMaxColumnWidth;
  3667. property OnBeforeHeaderExport;
  3668. property OnBeforeHeaderHeightTracking;
  3669. property OnBeforeItemErase;
  3670. property OnBeforeItemPaint;
  3671. property OnBeforeNodeExport;
  3672. property OnBeforePaint;
  3673. property OnBeforeTreeExport;
  3674. property OnCanSplitterResizeColumn;
  3675. property OnCanSplitterResizeHeader;
  3676. property OnCanSplitterResizeNode;
  3677. property OnChange;
  3678. property OnChecked;
  3679. property OnChecking;
  3680. property OnClick;
  3681. property OnCollapsed;
  3682. property OnCollapsing;
  3683. property OnColumnClick;
  3684. property OnColumnDblClick;
  3685. property OnColumnExport;
  3686. property OnColumnResize;
  3687. property OnColumnWidthDblClickResize;
  3688. property OnColumnWidthTracking;
  3689. property OnCompareNodes;
  3690. property OnContextPopup;
  3691. property OnCreateDataObject;
  3692. property OnCreateDragManager;
  3693. property OnCreateEditor;
  3694. property OnDblClick;
  3695. property OnDragAllowed;
  3696. property OnDragOver;
  3697. property OnDragDrop;
  3698. property OnDrawHint;
  3699. property OnDrawNode;
  3700. property OnEdited;
  3701. property OnEditing;
  3702. property OnEndDock;
  3703. property OnEndDrag;
  3704. property OnEndOperation;
  3705. property OnEnter;
  3706. property OnExit;
  3707. property OnExpanded;
  3708. property OnExpanding;
  3709. property OnFocusChanged;
  3710. property OnFocusChanging;
  3711. property OnFreeNode;
  3712. property OnGetCellIsEmpty;
  3713. property OnGetCursor;
  3714. property OnGetHeaderCursor;
  3715. property OnGetHelpContext;
  3716. property OnGetHintKind;
  3717. property OnGetHintSize;
  3718. property OnGetImageIndex;
  3719. property OnGetImageIndexEx;
  3720. property OnGetLineStyle;
  3721. property OnGetNodeDataSize;
  3722. property OnGetNodeWidth;
  3723. property OnGetPopupMenu;
  3724. property OnGetUserClipboardFormats;
  3725. property OnHeaderClick;
  3726. property OnHeaderDblClick;
  3727. property OnHeaderDragged;
  3728. property OnHeaderDraggedOut;
  3729. property OnHeaderDragging;
  3730. property OnHeaderDraw;
  3731. property OnHeaderDrawQueryElements;
  3732. property OnHeaderHeightTracking;
  3733. property OnHeaderHeightDblClickResize;
  3734. property OnHeaderMouseDown;
  3735. property OnHeaderMouseMove;
  3736. property OnHeaderMouseUp;
  3737. property OnHotChange;
  3738. property OnIncrementalSearch;
  3739. property OnInitChildren;
  3740. property OnInitNode;
  3741. property OnKeyAction;
  3742. property OnKeyDown;
  3743. property OnKeyPress;
  3744. property OnKeyUp;
  3745. property OnLoadNode;
  3746. property OnLoadTree;
  3747. property OnMeasureItem;
  3748. property OnMouseDown;
  3749. property OnMouseMove;
  3750. property OnMouseUp;
  3751. property OnMouseWheel;
  3752. property OnNodeClick;
  3753. property OnNodeCopied;
  3754. property OnNodeCopying;
  3755. property OnNodeDblClick;
  3756. property OnNodeExport;
  3757. property OnNodeHeightTracking;
  3758. property OnNodeHeightDblClickResize;
  3759. property OnNodeMoved;
  3760. property OnNodeMoving;
  3761. property OnPaintBackground;
  3762. property OnRemoveFromSelection;
  3763. property OnRenderOLEData;
  3764. property OnResetNode;
  3765. property OnResize;
  3766. property OnSaveNode;
  3767. property OnSaveTree;
  3768. property OnScroll;
  3769. property OnShowScrollBar;
  3770. property OnStartDock;
  3771. property OnStartDrag;
  3772. property OnStartOperation;
  3773. property OnStateChange;
  3774. property OnStructureChange;
  3775. property OnUpdating;
  3776. {$if CompilerVersion >= 22}
  3777. property OnCanResize;
  3778. property OnGesture;
  3779. property Touch;
  3780. {$ifend}
  3781. {$if CompilerVersion >= 24}
  3782. property StyleElements;
  3783. {$ifend}
  3784. end;
  3785. type
  3786. // Describes the mode how to blend pixels.
  3787. TBlendMode = (
  3788. bmConstantAlpha, // apply given constant alpha
  3789. bmPerPixelAlpha, // use alpha value of the source pixel
  3790. bmMasterAlpha, // use alpha value of source pixel and multiply it with the constant alpha value
  3791. bmConstantAlphaAndColor // blend the destination color with the given constant color und the constant alpha value
  3792. );
  3793. // OLE Clipboard and drag'n drop helper
  3794. procedure EnumerateVTClipboardFormats(TreeClass: TVirtualTreeClass; const List: TStrings); overload;
  3795. procedure EnumerateVTClipboardFormats(TreeClass: TVirtualTreeClass; var Formats: TFormatEtcArray); overload;
  3796. function GetVTClipboardFormatDescription(AFormat: Word): string;
  3797. procedure RegisterVTClipboardFormat(AFormat: Word; TreeClass: TVirtualTreeClass; Priority: Cardinal); overload;
  3798. function RegisterVTClipboardFormat(Description: string; TreeClass: TVirtualTreeClass; Priority: Cardinal;
  3799. tymed: Integer = TYMED_HGLOBAL; ptd: PDVTargetDevice = nil; dwAspect: Integer = DVASPECT_CONTENT;
  3800. lindex: Integer = -1): Word; overload;
  3801. // utility routines
  3802. procedure AlphaBlend(Source, Destination: HDC; R: TRect; Target: TPoint; Mode: TBlendMode; ConstantAlpha, Bias: Integer);
  3803. procedure PrtStretchDrawDIB(Canvas: TCanvas; DestRect: TRect; ABitmap: TBitmap);
  3804. function ShortenString(DC: HDC; const S: UnicodeString; Width: Integer; EllipsisWidth: Integer = 0): UnicodeString;
  3805. function TreeFromNode(Node: PVirtualNode): TBaseVirtualTree;
  3806. procedure GetStringDrawRect(DC: HDC; const S: UnicodeString; var Bounds: TRect; DrawFormat: Cardinal);
  3807. function WrapString(DC: HDC; const S: UnicodeString; const Bounds: TRect; RTL: Boolean;
  3808. DrawFormat: Cardinal): UnicodeString;
  3809. function GetUtilityImages: TCustomImageList;
  3810. procedure ShowError(Msg: UnicodeString; HelpContext: Integer); // [IPK] Surface this to interface
  3811. //----------------------------------------------------------------------------------------------------------------------
  3812. implementation
  3813. {$R VirtualTrees.res}
  3814. uses
  3815. Consts, Math,
  3816. AxCtrls, // TOLEStream
  3817. MMSystem, // for animation timer (does not include further resources)
  3818. TypInfo, // for migration stuff
  3819. ActnList,
  3820. StdActns, // for standard action support
  3821. {$ifdef UNICODE}
  3822. AnsiStrings,
  3823. {$endif UNICODE}
  3824. StrUtils,
  3825. VTAccessibilityFactory, GraphUtil; // accessibility helper class
  3826. resourcestring
  3827. // Localizable strings.
  3828. SEditLinkIsNil = 'Edit link must not be nil.';
  3829. SWrongMoveError = 'Target node cannot be a child node of the node to be moved.';
  3830. SWrongStreamFormat = 'Unable to load tree structure, the format is wrong.';
  3831. SWrongStreamVersion = 'Unable to load tree structure, the version is unknown.';
  3832. SStreamTooSmall = 'Unable to load tree structure, not enough data available.';
  3833. SCorruptStream1 = 'Stream data corrupt. A node''s anchor chunk is missing.';
  3834. SCorruptStream2 = 'Stream data corrupt. Unexpected data after node''s end position.';
  3835. SClipboardFailed = 'Clipboard operation failed.';
  3836. SCannotSetUserData = 'Cannot set initial user data because there is not enough user data space allocated.';
  3837. const
  3838. ClipboardStates = [tsCopyPending, tsCutPending];
  3839. DefaultScrollUpdateFlags = [suoRepaintHeader, suoRepaintScrollBars, suoScrollClientArea, suoUpdateNCArea];
  3840. TreeNodeSize = (SizeOf(TVirtualNode) + (SizeOf(Pointer) - 1)) and not (SizeOf(Pointer) - 1); // used for node allocation and access to internal data
  3841. // Lookup to quickly convert a specific check state into its pressed counterpart and vice versa.
  3842. PressedState: array[TCheckState] of TCheckState = (
  3843. csUncheckedPressed, csUncheckedPressed, csCheckedPressed, csCheckedPressed, csMixedPressed, csMixedPressed
  3844. );
  3845. UnpressedState: array[TCheckState] of TCheckState = (
  3846. csUncheckedNormal, csUncheckedNormal, csCheckedNormal, csCheckedNormal, csMixedNormal, csMixedNormal
  3847. );
  3848. MouseButtonDown = [tsLeftButtonDown, tsMiddleButtonDown, tsRightButtonDown];
  3849. // Do not modify the copyright in any way! Usage of this unit is prohibited without the copyright notice
  3850. // in the compiled binary file.
  3851. Copyright: string = 'Virtual Treeview Š 1999, 2010 Mike Lischke';
  3852. var
  3853. StandardOLEFormat: TFormatEtc = (
  3854. // Format must later be set.
  3855. cfFormat: 0;
  3856. // No specific target device to render on.
  3857. ptd: nil;
  3858. // Normal content to render.
  3859. dwAspect: DVASPECT_CONTENT;
  3860. // No specific page of multipage data (we don't use multipage data by default).
  3861. lindex: -1;
  3862. // Acceptable storage formats are IStream and global memory. The first is preferred.
  3863. tymed: TYMED_ISTREAM or TYMED_HGLOBAL;
  3864. );
  3865. {$if CompilerVersion < 23}
  3866. type
  3867. TElementEdge = (
  3868. eeRaisedOuter
  3869. );
  3870. TElementEdges = set of TElementEdge;
  3871. TElementEdgeFlag = (
  3872. efRect
  3873. );
  3874. TElementEdgeFlags = set of TElementEdgeFlag;
  3875. // For compatibility with Delphi XE and earlier, prevents deprecated warnings in Delphi XE2 and higher
  3876. StyleServices = class
  3877. class function Enabled: Boolean;
  3878. class function DrawEdge(DC: HDC; Details: TThemedElementDetails; const R: TRect;
  3879. Edges: TElementEdges; Flags: TElementEdgeFlags; ContentRect: PRect = nil): Boolean;
  3880. class function DrawElement(DC: HDC; Details: TThemedElementDetails; const R: TRect; ClipRect: PRect = nil): Boolean;
  3881. class function GetElementDetails(Detail: TThemedHeader): TThemedElementDetails; overload;
  3882. class function GetElementDetails(Detail: TThemedToolTip): TThemedElementDetails; overload;
  3883. class function GetElementDetails(Detail: TThemedWindow): TThemedElementDetails; overload;
  3884. class function GetElementDetails(Detail: TThemedButton): TThemedElementDetails; overload;
  3885. class procedure PaintBorder(Control: TWinControl; EraseLRCorner: Boolean);
  3886. end;
  3887. class function StyleServices.Enabled: Boolean;
  3888. begin
  3889. Result := ThemeServices.ThemesEnabled;
  3890. end;
  3891. class function StyleServices.DrawEdge(DC: HDC; Details: TThemedElementDetails; const R: TRect;
  3892. Edges: TElementEdges; Flags: TElementEdgeFlags; ContentRect: PRect = nil): Boolean;
  3893. begin
  3894. Assert((Edges = [eeRaisedOuter]) and (Flags = [efRect]));
  3895. ThemeServices.DrawEdge(DC, Details, R, BDR_RAISEDOUTER, BF_RECT);
  3896. Result := Enabled;
  3897. end;
  3898. class function StyleServices.DrawElement(DC: HDC; Details: TThemedElementDetails; const R: TRect; ClipRect: PRect = nil): Boolean;
  3899. begin
  3900. ThemeServices.DrawElement(DC, Details, R, ClipRect);
  3901. Result := Enabled;
  3902. end;
  3903. class function StyleServices.GetElementDetails(Detail: TThemedHeader): TThemedElementDetails;
  3904. begin
  3905. Result := ThemeServices.GetElementDetails(Detail);
  3906. end;
  3907. class function StyleServices.GetElementDetails(Detail: TThemedToolTip): TThemedElementDetails;
  3908. begin
  3909. Result := ThemeServices.GetElementDetails(Detail);
  3910. end;
  3911. class function StyleServices.GetElementDetails(Detail: TThemedWindow): TThemedElementDetails;
  3912. begin
  3913. Result := ThemeServices.GetElementDetails(Detail);
  3914. end;
  3915. class function StyleServices.GetElementDetails(Detail: TThemedButton): TThemedElementDetails;
  3916. begin
  3917. Result := ThemeServices.GetElementDetails(Detail);
  3918. end;
  3919. class procedure StyleServices.PaintBorder(Control: TWinControl; EraseLRCorner: Boolean);
  3920. begin
  3921. ThemeServices.PaintBorder(Control, EraseLRCorner);
  3922. end;
  3923. {$ifend}
  3924. type
  3925. // protection against TRect record method that cause problems with with-statements
  3926. TWithSafeRect = record
  3927. case Integer of
  3928. 0: (Left, Top, Right, Bottom: Longint);
  3929. 1: (TopLeft, BottomRight: TPoint);
  3930. end;
  3931. type // streaming support
  3932. TMagicID = array[0..5] of WideChar;
  3933. TChunkHeader = record
  3934. ChunkType,
  3935. ChunkSize: Integer; // contains the size of the chunk excluding the header
  3936. end;
  3937. // base information about a node
  3938. TBaseChunkBody = packed record
  3939. ChildCount,
  3940. NodeHeight: Cardinal;
  3941. States: TVirtualNodeStates;
  3942. Align: Byte;
  3943. CheckState: TCheckState;
  3944. CheckType: TCheckType;
  3945. Reserved: Cardinal;
  3946. end;
  3947. TBaseChunk = packed record
  3948. Header: TChunkHeader;
  3949. Body: TBaseChunkBody;
  3950. end;
  3951. // Toggle animation modes.
  3952. TToggleAnimationMode = (
  3953. tamScrollUp,
  3954. tamScrollDown,
  3955. tamNoScroll
  3956. );
  3957. // Internally used data for animations.
  3958. TToggleAnimationData = record
  3959. Window: HWND; // copy of the tree's window handle
  3960. DC: HDC; // the DC of the window to erase uncovered parts
  3961. Brush: HBRUSH; // the brush to be used to erase uncovered parts
  3962. R1,
  3963. R2: TRect; // animation rectangles
  3964. Mode1,
  3965. Mode2: TToggleAnimationMode; // animation modes
  3966. ScaleFactor: Double; // the factor between the missing step size when doing two animations
  3967. MissedSteps: Double;
  3968. end;
  3969. TCanvasEx = class(TCanvas);
  3970. const
  3971. MagicID: TMagicID = (#$2045, 'V', 'T', WideChar(VTTreeStreamVersion), ' ', #$2046);
  3972. // chunk IDs
  3973. NodeChunk = 1;
  3974. BaseChunk = 2; // chunk containing node state, check state, child node count etc.
  3975. // this chunk is immediately followed by all child nodes
  3976. CaptionChunk = 3; // used by the string tree to store a node's caption
  3977. UserChunk = 4; // used for data supplied by the application
  3978. {$if CompilerVersion < 19}
  3979. const
  3980. TVP_HOTGLYPH = 4;
  3981. {$ifend}
  3982. RTLFlag: array[Boolean] of Integer = (0, ETO_RTLREADING);
  3983. AlignmentToDrawFlag: array[TAlignment] of Cardinal = (DT_LEFT, DT_RIGHT, DT_CENTER);
  3984. WideNull = WideChar(#0);
  3985. WideCR = WideChar(#13);
  3986. WideLF = WideChar(#10);
  3987. WideLineSeparator = WideChar(#2028);
  3988. type
  3989. TCriticalSection = class(TObject)
  3990. protected
  3991. FSection: TRTLCriticalSection;
  3992. public
  3993. constructor Create;
  3994. destructor Destroy; override;
  3995. procedure Enter;
  3996. procedure Leave;
  3997. end;
  3998. // internal worker thread
  3999. TWorkerThread = class(TThread)
  4000. private
  4001. FCurrentTree: TBaseVirtualTree;
  4002. FWaiterList: TThreadList;
  4003. FRefCount: Cardinal;
  4004. protected
  4005. procedure CancelValidation(Tree: TBaseVirtualTree);
  4006. procedure Execute; override;
  4007. public
  4008. constructor Create(CreateSuspended: Boolean);
  4009. destructor Destroy; override;
  4010. procedure AddTree(Tree: TBaseVirtualTree);
  4011. procedure RemoveTree(Tree: TBaseVirtualTree);
  4012. property CurrentTree: TBaseVirtualTree read FCurrentTree;
  4013. end;
  4014. // Helper classes to speed up rendering text formats for clipboard and drag'n drop transfers.
  4015. TBufferedAnsiString = class
  4016. private
  4017. FStart,
  4018. FPosition,
  4019. FEnd: PAnsiChar;
  4020. function GetAsString: RawByteString;
  4021. public
  4022. destructor Destroy; override;
  4023. procedure Add(const S: RawByteString);
  4024. procedure AddNewLine;
  4025. property AsString: RawByteString read GetAsString;
  4026. end;
  4027. TWideBufferedString = class
  4028. private
  4029. FStart,
  4030. FPosition,
  4031. FEnd: PWideChar;
  4032. function GetAsString: UnicodeString;
  4033. public
  4034. destructor Destroy; override;
  4035. procedure Add(const S: UnicodeString);
  4036. procedure AddNewLine;
  4037. property AsString: UnicodeString read GetAsString;
  4038. end;
  4039. var
  4040. WorkerThread: TWorkerThread;
  4041. WorkEvent: THandle;
  4042. Watcher: TCriticalSection;
  4043. LightCheckImages, // global light check images
  4044. DarkCheckImages, // global heavy check images
  4045. LightTickImages, // global light tick images
  4046. DarkTickImages, // global heavy check images
  4047. FlatImages, // global flat check images
  4048. XPImages, // global XP style check images
  4049. UtilityImages, // some small additional images (e.g for header dragging)
  4050. SystemCheckImages, // global system check images
  4051. SystemFlatCheckImages: TImageList; // global flat system check images
  4052. Initialized: Boolean; // True if global structures have been initialized.
  4053. NeedToUnitialize: Boolean; // True if the OLE subsystem could be initialized successfully.
  4054. //----------------- TClipboardFormats ----------------------------------------------------------------------------------
  4055. type
  4056. PClipboardFormatListEntry = ^TClipboardFormatListEntry;
  4057. TClipboardFormatListEntry = record
  4058. Description: string; // The string used to register the format with Windows.
  4059. TreeClass: TVirtualTreeClass; // The tree class which supports rendering this format.
  4060. Priority: Cardinal; // Number which determines the order of formats used in IDataObject.
  4061. FormatEtc: TFormatEtc; // The definition of the format in the IDataObject.
  4062. end;
  4063. TClipboardFormatList = class
  4064. private
  4065. FList: TList;
  4066. procedure Sort;
  4067. public
  4068. constructor Create;
  4069. destructor Destroy; override;
  4070. procedure Add(FormatString: string; AClass: TVirtualTreeClass; Priority: Cardinal; AFormatEtc: TFormatEtc);
  4071. procedure Clear;
  4072. procedure EnumerateFormats(TreeClass: TVirtualTreeClass; var Formats: TFormatEtcArray;
  4073. const AllowedFormats: TClipboardFormats = nil); overload;
  4074. procedure EnumerateFormats(TreeClass: TVirtualTreeClass; const Formats: TStrings); overload;
  4075. function FindFormat(FormatString: string): PClipboardFormatListEntry; overload;
  4076. function FindFormat(FormatString: string; var Fmt: Word): TVirtualTreeClass; overload;
  4077. function FindFormat(Fmt: Word; var Description: string): TVirtualTreeClass; overload;
  4078. end;
  4079. var
  4080. InternalClipboardFormats: TClipboardFormatList;
  4081. //----------------------------------------------------------------------------------------------------------------------
  4082. constructor TClipboardFormatList.Create;
  4083. begin
  4084. FList := TList.Create;
  4085. end;
  4086. //----------------------------------------------------------------------------------------------------------------------
  4087. destructor TClipboardFormatList.Destroy;
  4088. begin
  4089. Clear;
  4090. FList.Free;
  4091. inherited;
  4092. end;
  4093. //----------------------------------------------------------------------------------------------------------------------
  4094. procedure TClipboardFormatList.Sort;
  4095. // Sorts all entry for priority (increasing priority value).
  4096. //--------------- local function --------------------------------------------
  4097. procedure QuickSort(L, R: Integer);
  4098. var
  4099. I, J: Integer;
  4100. P, T: PClipboardFormatListEntry;
  4101. begin
  4102. repeat
  4103. I := L;
  4104. J := R;
  4105. P := FList[(L + R) shr 1];
  4106. repeat
  4107. while PClipboardFormatListEntry(FList[I]).Priority < P.Priority do
  4108. Inc(I);
  4109. while PClipboardFormatListEntry(FList[J]).Priority > P.Priority do
  4110. Dec(J);
  4111. if I <= J then
  4112. begin
  4113. T := FList[I];
  4114. FList[I] := FList[J];
  4115. FList[J] := T;
  4116. Inc(I);
  4117. Dec(J);
  4118. end;
  4119. until I > J;
  4120. if L < J then
  4121. QuickSort(L, J);
  4122. L := I;
  4123. until I >= R;
  4124. end;
  4125. //--------------- end local function ----------------------------------------
  4126. begin
  4127. if FList.Count > 1 then
  4128. QuickSort(0, FList.Count - 1);
  4129. end;
  4130. //----------------------------------------------------------------------------------------------------------------------
  4131. procedure TClipboardFormatList.Add(FormatString: string; AClass: TVirtualTreeClass; Priority: Cardinal;
  4132. AFormatEtc: TFormatEtc);
  4133. // Adds the given data to the internal list. The priority value is used to sort formats for importance. Larger priority
  4134. // values mean less priority.
  4135. var
  4136. Entry: PClipboardFormatListEntry;
  4137. begin
  4138. New(Entry);
  4139. Entry.Description := FormatString;
  4140. Entry.TreeClass := AClass;
  4141. Entry.Priority := Priority;
  4142. Entry.FormatEtc := AFormatEtc;
  4143. FList.Add(Entry);
  4144. Sort;
  4145. end;
  4146. //----------------------------------------------------------------------------------------------------------------------
  4147. procedure TClipboardFormatList.Clear;
  4148. var
  4149. I: Integer;
  4150. begin
  4151. for I := 0 to FList.Count - 1 do
  4152. Dispose(PClipboardFormatListEntry(FList[I]));
  4153. FList.Clear;
  4154. end;
  4155. //----------------------------------------------------------------------------------------------------------------------
  4156. procedure TClipboardFormatList.EnumerateFormats(TreeClass: TVirtualTreeClass; var Formats: TFormatEtcArray;
  4157. const AllowedFormats: TClipboardFormats = nil);
  4158. // Returns a list of format records for the given class. If assigned the AllowedFormats is used to limit the
  4159. // enumerated formats to those described in the list.
  4160. var
  4161. I, Count: Integer;
  4162. Entry: PClipboardFormatListEntry;
  4163. begin
  4164. SetLength(Formats, FList.Count);
  4165. Count := 0;
  4166. for I := 0 to FList.Count - 1 do
  4167. begin
  4168. Entry := FList[I];
  4169. // Does the tree class support this clipboard format?
  4170. if TreeClass.InheritsFrom(Entry.TreeClass) then
  4171. begin
  4172. // Is this format allowed to be included?
  4173. if (AllowedFormats = nil) or (AllowedFormats.IndexOf(Entry.Description) > -1) then
  4174. begin
  4175. // The list could change before we use the FormatEtc so it is best not to pass a pointer to the true FormatEtc
  4176. // structure. Instead make a copy and send that.
  4177. Formats[Count] := Entry.FormatEtc;
  4178. Inc(Count);
  4179. end;
  4180. end;
  4181. end;
  4182. SetLength(Formats, Count);
  4183. end;
  4184. //----------------------------------------------------------------------------------------------------------------------
  4185. procedure TClipboardFormatList.EnumerateFormats(TreeClass: TVirtualTreeClass; const Formats: TStrings);
  4186. // Returns a list of format descriptions for the given class.
  4187. var
  4188. I: Integer;
  4189. Entry: PClipboardFormatListEntry;
  4190. begin
  4191. for I := 0 to FList.Count - 1 do
  4192. begin
  4193. Entry := FList[I];
  4194. if TreeClass.InheritsFrom(Entry.TreeClass) then
  4195. Formats.Add(Entry.Description);
  4196. end;
  4197. end;
  4198. //----------------------------------------------------------------------------------------------------------------------
  4199. function TClipboardFormatList.FindFormat(FormatString: string): PClipboardFormatListEntry;
  4200. var
  4201. I: Integer;
  4202. Entry: PClipboardFormatListEntry;
  4203. begin
  4204. Result := nil;
  4205. for I := FList.Count - 1 downto 0 do
  4206. begin
  4207. Entry := FList[I];
  4208. if CompareText(Entry.Description, FormatString) = 0 then
  4209. begin
  4210. Result := Entry;
  4211. Break;
  4212. end;
  4213. end;
  4214. end;
  4215. //----------------------------------------------------------------------------------------------------------------------
  4216. function TClipboardFormatList.FindFormat(FormatString: string; var Fmt: Word): TVirtualTreeClass;
  4217. var
  4218. I: Integer;
  4219. Entry: PClipboardFormatListEntry;
  4220. begin
  4221. Result := nil;
  4222. for I := FList.Count - 1 downto 0 do
  4223. begin
  4224. Entry := FList[I];
  4225. if CompareText(Entry.Description, FormatString) = 0 then
  4226. begin
  4227. Result := Entry.TreeClass;
  4228. Fmt := Entry.FormatEtc.cfFormat;
  4229. Break;
  4230. end;
  4231. end;
  4232. end;
  4233. //----------------------------------------------------------------------------------------------------------------------
  4234. function TClipboardFormatList.FindFormat(Fmt: Word; var Description: string): TVirtualTreeClass;
  4235. var
  4236. I: Integer;
  4237. Entry: PClipboardFormatListEntry;
  4238. begin
  4239. Result := nil;
  4240. for I := FList.Count - 1 downto 0 do
  4241. begin
  4242. Entry := FList[I];
  4243. if Entry.FormatEtc.cfFormat = Fmt then
  4244. begin
  4245. Result := Entry.TreeClass;
  4246. Description := Entry.Description;
  4247. Break;
  4248. end;
  4249. end;
  4250. end;
  4251. //----------------------------------------------------------------------------------------------------------------------
  4252. type
  4253. TClipboardFormatEntry = record
  4254. ID: Word;
  4255. Description: string;
  4256. end;
  4257. var
  4258. ClipboardDescriptions: array [1..CF_MAX - 1] of TClipboardFormatEntry = (
  4259. (ID: CF_TEXT; Description: 'Plain text'), // Do not localize
  4260. (ID: CF_BITMAP; Description: 'Windows bitmap'), // Do not localize
  4261. (ID: CF_METAFILEPICT; Description: 'Windows metafile'), // Do not localize
  4262. (ID: CF_SYLK; Description: 'Symbolic link'), // Do not localize
  4263. (ID: CF_DIF; Description: 'Data interchange format'), // Do not localize
  4264. (ID: CF_TIFF; Description: 'Tiff image'), // Do not localize
  4265. (ID: CF_OEMTEXT; Description: 'OEM text'), // Do not localize
  4266. (ID: CF_DIB; Description: 'DIB image'), // Do not localize
  4267. (ID: CF_PALETTE; Description: 'Palette data'), // Do not localize
  4268. (ID: CF_PENDATA; Description: 'Pen data'), // Do not localize
  4269. (ID: CF_RIFF; Description: 'Riff audio data'), // Do not localize
  4270. (ID: CF_WAVE; Description: 'Wav audio data'), // Do not localize
  4271. (ID: CF_UNICODETEXT; Description: 'Unicode text'), // Do not localize
  4272. (ID: CF_ENHMETAFILE; Description: 'Enhanced metafile image'), // Do not localize
  4273. (ID: CF_HDROP; Description: 'File name(s)'), // Do not localize
  4274. (ID: CF_LOCALE; Description: 'Locale descriptor') // Do not localize
  4275. {$if CompilerVersion >= 23}
  4276. ,(ID: CF_DIBV5; Description: 'DIB image V5') // Do not localize
  4277. {$ifend}
  4278. );
  4279. //----------------------------------------------------------------------------------------------------------------------
  4280. procedure EnumerateVTClipboardFormats(TreeClass: TVirtualTreeClass; const List: TStrings);
  4281. begin
  4282. if InternalClipboardFormats = nil then
  4283. InternalClipboardFormats := TClipboardFormatList.Create;
  4284. InternalClipboardFormats.EnumerateFormats(TreeClass, List);
  4285. end;
  4286. //----------------------------------------------------------------------------------------------------------------------
  4287. procedure EnumerateVTClipboardFormats(TreeClass: TVirtualTreeClass; var Formats: TFormatEtcArray);
  4288. begin
  4289. if InternalClipboardFormats = nil then
  4290. InternalClipboardFormats := TClipboardFormatList.Create;
  4291. InternalClipboardFormats.EnumerateFormats(TreeClass, Formats);
  4292. end;
  4293. //----------------------------------------------------------------------------------------------------------------------
  4294. function GetVTClipboardFormatDescription(AFormat: Word): string;
  4295. begin
  4296. if InternalClipboardFormats = nil then
  4297. InternalClipboardFormats := TClipboardFormatList.Create;
  4298. if InternalClipboardFormats.FindFormat(AFormat, Result) = nil then
  4299. Result := '';
  4300. end;
  4301. //----------------------------------------------------------------------------------------------------------------------
  4302. procedure RegisterVTClipboardFormat(AFormat: Word; TreeClass: TVirtualTreeClass; Priority: Cardinal);
  4303. // Registers the given clipboard format for the given TreeClass.
  4304. var
  4305. I: Integer;
  4306. Buffer: array[0..2048] of Char;
  4307. FormatEtc: TFormatEtc;
  4308. begin
  4309. if InternalClipboardFormats = nil then
  4310. InternalClipboardFormats := TClipboardFormatList.Create;
  4311. // Assumes a HGlobal format.
  4312. FormatEtc.cfFormat := AFormat;
  4313. FormatEtc.ptd := nil;
  4314. FormatEtc.dwAspect := DVASPECT_CONTENT;
  4315. FormatEtc.lindex := -1;
  4316. FormatEtc.tymed := TYMED_HGLOBAL;
  4317. // Determine description string of the given format. For predefined formats we need the lookup table because they
  4318. // don't have a description string. For registered formats the description string is the string which was used
  4319. // to register them.
  4320. if AFormat < CF_MAX then
  4321. begin
  4322. for I := 1 to High(ClipboardDescriptions) do
  4323. if ClipboardDescriptions[I].ID = AFormat then
  4324. begin
  4325. InternalClipboardFormats.Add(ClipboardDescriptions[I].Description, TreeClass, Priority, FormatEtc);
  4326. Break;
  4327. end;
  4328. end
  4329. else
  4330. begin
  4331. GetClipboardFormatName(AFormat, Buffer, Length(Buffer));
  4332. InternalClipboardFormats.Add(Buffer, TreeClass, Priority, FormatEtc);
  4333. end;
  4334. end;
  4335. //----------------------------------------------------------------------------------------------------------------------
  4336. function RegisterVTClipboardFormat(Description: string; TreeClass: TVirtualTreeClass; Priority: Cardinal;
  4337. tymed: Integer = TYMED_HGLOBAL; ptd: PDVTargetDevice = nil; dwAspect: Integer = DVASPECT_CONTENT;
  4338. lindex: Integer = -1): Word;
  4339. // Alternative method to register a certain clipboard format for a given tree class. Registration with the
  4340. // clipboard is done here too and the assigned ID returned by the function.
  4341. // tymed may contain or'ed TYMED constants which allows to register several storage formats for one clipboard format.
  4342. var
  4343. FormatEtc: TFormatEtc;
  4344. begin
  4345. if InternalClipboardFormats = nil then
  4346. InternalClipboardFormats := TClipboardFormatList.Create;
  4347. Result := RegisterClipboardFormat(PChar(Description));
  4348. FormatEtc.cfFormat := Result;
  4349. FormatEtc.ptd := ptd;
  4350. FormatEtc.dwAspect := dwAspect;
  4351. FormatEtc.lindex := lindex;
  4352. FormatEtc.tymed := tymed;
  4353. InternalClipboardFormats.Add(Description, TreeClass, Priority, FormatEtc);
  4354. end;
  4355. //----------------- utility functions ----------------------------------------------------------------------------------
  4356. function GetUtilityImages: TCustomImageList; // [IPK]
  4357. begin
  4358. Result := UtilityImages;
  4359. end;
  4360. //----------------------------------------------------------------------------------------------------------------------
  4361. procedure ShowError(Msg: UnicodeString; HelpContext: Integer);
  4362. begin
  4363. raise EVirtualTreeError.CreateHelp(Msg, HelpContext);
  4364. end;
  4365. //----------------------------------------------------------------------------------------------------------------------
  4366. function TreeFromNode(Node: PVirtualNode): TBaseVirtualTree;
  4367. // Returns the tree the node currently belongs to or nil if the node is not attached to a tree.
  4368. begin
  4369. Assert(Assigned(Node), 'Node must not be nil.');
  4370. // The root node is marked by having its NextSibling (and PrevSibling) pointing to itself.
  4371. while Assigned(Node) and (Node.NextSibling <> Node) do
  4372. Node := Node.Parent;
  4373. if Assigned(Node) then
  4374. Result := TBaseVirtualTree(Node.Parent)
  4375. else
  4376. Result := nil;
  4377. end;
  4378. //----------------------------------------------------------------------------------------------------------------------
  4379. function OrderRect(const R: TRect): TRect;
  4380. // Converts the incoming rectangle so that left and top are always less than or equal to right and bottom.
  4381. begin
  4382. if R.Left < R.Right then
  4383. begin
  4384. Result.Left := R.Left;
  4385. Result.Right := R.Right;
  4386. end
  4387. else
  4388. begin
  4389. Result.Left := R.Right;
  4390. Result.Right := R.Left;
  4391. end;
  4392. if R.Top < R.Bottom then
  4393. begin
  4394. Result.Top := R.Top;
  4395. Result.Bottom := R.Bottom;
  4396. end
  4397. else
  4398. begin
  4399. Result.Top := R.Bottom;
  4400. Result.Bottom := R.Top;
  4401. end;
  4402. end;
  4403. //----------------------------------------------------------------------------------------------------------------------
  4404. procedure QuickSort(const TheArray: TNodeArray; L, R: Integer);
  4405. var
  4406. I, J: Integer;
  4407. P, T: Pointer;
  4408. begin
  4409. repeat
  4410. I := L;
  4411. J := R;
  4412. P := TheArray[(L + R) shr 1];
  4413. repeat
  4414. while PAnsiChar(TheArray[I]) < PAnsiChar(P) do
  4415. Inc(I);
  4416. while PAnsiChar(TheArray[J]) > PAnsiChar(P) do
  4417. Dec(J);
  4418. if I <= J then
  4419. begin
  4420. T := TheArray[I];
  4421. TheArray[I] := TheArray[J];
  4422. TheArray[J] := T;
  4423. Inc(I);
  4424. Dec(J);
  4425. end;
  4426. until I > J;
  4427. if L < J then
  4428. QuickSort(TheArray, L, J);
  4429. L := I;
  4430. until I >= R;
  4431. end;
  4432. //----------------------------------------------------------------------------------------------------------------------
  4433. function ShortenString(DC: HDC; const S: UnicodeString; Width: Integer; EllipsisWidth: Integer = 0): UnicodeString;
  4434. // Adjusts the given string S so that it fits into the given width. EllipsisWidth gives the width of
  4435. // the three points to be added to the shorted string. If this value is 0 then it will be determined implicitely.
  4436. // For higher speed (and multiple entries to be shorted) specify this value explicitely.
  4437. // Note: It is assumed that the string really needs shortage. Check this in advance.
  4438. var
  4439. Size: TSize;
  4440. Len: Integer;
  4441. L, H, N, W: Integer;
  4442. begin
  4443. Len := Length(S);
  4444. if (Len = 0) or (Width <= 0) then
  4445. Result := ''
  4446. else
  4447. begin
  4448. // Determine width of triple point using the current DC settings (if not already done).
  4449. if EllipsisWidth = 0 then
  4450. begin
  4451. GetTextExtentPoint32W(DC, '...', 3, Size);
  4452. EllipsisWidth := Size.cx;
  4453. end;
  4454. if Width <= EllipsisWidth then
  4455. Result := ''
  4456. else
  4457. begin
  4458. // Do a binary search for the optimal string length which fits into the given width.
  4459. L := 0;
  4460. H := Len - 1;
  4461. while L < H do
  4462. begin
  4463. N := (L + H + 1) shr 1;
  4464. GetTextExtentPoint32W(DC, PWideChar(S), N, Size);
  4465. W := Size.cx + EllipsisWidth;
  4466. if W <= Width then
  4467. L := N
  4468. else
  4469. H := N - 1;
  4470. end;
  4471. Result := Copy(S, 1, L) + '...';
  4472. end;
  4473. end;
  4474. end;
  4475. //----------------------------------------------------------------------------------------------------------------------
  4476. function WrapString(DC: HDC; const S: UnicodeString; const Bounds: TRect; RTL: Boolean;
  4477. DrawFormat: Cardinal): UnicodeString;
  4478. // Wrap the given string S so that it fits into a space of given width.
  4479. // RTL determines if right-to-left reading is active.
  4480. var
  4481. Width,
  4482. Len,
  4483. WordCounter,
  4484. WordsInLine,
  4485. I, W: Integer;
  4486. Buffer,
  4487. Line: UnicodeString;
  4488. Words: array of UnicodeString;
  4489. R: TRect;
  4490. begin
  4491. Result := '';
  4492. // Leading and trailing are ignored.
  4493. Buffer := Trim(S);
  4494. Len := Length(Buffer);
  4495. if Len < 1 then
  4496. Exit;
  4497. Width := Bounds.Right - Bounds.Left;
  4498. R := Rect(0, 0, 0, 0);
  4499. // Count the words in the string.
  4500. WordCounter := 1;
  4501. for I := 1 to Len do
  4502. if Buffer[I] = ' ' then
  4503. Inc(WordCounter);
  4504. SetLength(Words, WordCounter);
  4505. if RTL then
  4506. begin
  4507. // At first we split the string into words with the last word being the
  4508. // first element in Words.
  4509. W := 0;
  4510. for I := 1 to Len do
  4511. if Buffer[I] = ' ' then
  4512. Inc(W)
  4513. else
  4514. Words[W] := Words[W] + Buffer[I];
  4515. // Compose Result.
  4516. while WordCounter > 0 do
  4517. begin
  4518. WordsInLine := 0;
  4519. Line := '';
  4520. while WordCounter > 0 do
  4521. begin
  4522. GetStringDrawRect(DC, Line + IfThen(WordsInLine > 0, ' ', '') + Words[WordCounter - 1], R, DrawFormat);
  4523. if R.Right > Width then
  4524. begin
  4525. // If at least one word fits into this line then continue with the next line.
  4526. if WordsInLine > 0 then
  4527. Break;
  4528. Buffer := Words[WordCounter - 1];
  4529. if Len > 1 then
  4530. begin
  4531. for Len := Length(Buffer) - 1 downto 2 do
  4532. begin
  4533. GetStringDrawRect(DC, RightStr(Buffer, Len), R, DrawFormat);
  4534. if R.Right <= Width then
  4535. Break;
  4536. end;
  4537. end
  4538. else
  4539. Len := Length(Buffer);
  4540. Line := Line + RightStr(Buffer, Max(Len, 1));
  4541. Words[WordCounter - 1] := LeftStr(Buffer, Length(Buffer) - Max(Len, 1));
  4542. if Words[WordCounter - 1] = '' then
  4543. Dec(WordCounter);
  4544. Break;
  4545. end
  4546. else
  4547. begin
  4548. Dec(WordCounter);
  4549. Line := Words[WordCounter] + IfThen(WordsInLine > 0, ' ', '') + Line;
  4550. Inc(WordsInLine);
  4551. end;
  4552. end;
  4553. Result := Result + Line + WideLF;
  4554. end;
  4555. end
  4556. else
  4557. begin
  4558. // At first we split the string into words with the last word being the
  4559. // first element in Words.
  4560. W := WordCounter - 1;
  4561. for I := 1 to Len do
  4562. if Buffer[I] = ' ' then
  4563. Dec(W)
  4564. else
  4565. Words[W] := Words[W] + Buffer[I];
  4566. // Compose Result.
  4567. while WordCounter > 0 do
  4568. begin
  4569. WordsInLine := 0;
  4570. Line := '';
  4571. while WordCounter > 0 do
  4572. begin
  4573. GetStringDrawRect(DC, Line + IfThen(WordsInLine > 0, ' ', '') + Words[WordCounter - 1], R, DrawFormat);
  4574. if R.Right > Width then
  4575. begin
  4576. // If at least one word fits into this line then continue with the next line.
  4577. if WordsInLine > 0 then
  4578. Break;
  4579. Buffer := Words[WordCounter - 1];
  4580. if Len > 1 then
  4581. begin
  4582. for Len := Length(Buffer) - 1 downto 2 do
  4583. begin
  4584. GetStringDrawRect(DC, LeftStr(Buffer, Len), R, DrawFormat);
  4585. if R.Right <= Width then
  4586. Break;
  4587. end;
  4588. end
  4589. else
  4590. Len := Length(Buffer);
  4591. Line := Line + LeftStr(Buffer, Max(Len, 1));
  4592. Words[WordCounter - 1] := RightStr(Buffer, Length(Buffer) - Max(Len, 1));
  4593. if Words[WordCounter - 1] = '' then
  4594. Dec(WordCounter);
  4595. Break;
  4596. end
  4597. else
  4598. begin
  4599. Dec(WordCounter);
  4600. Line := Line + IfThen(WordsInLine > 0, ' ', '') + Words[WordCounter];
  4601. Inc(WordsInLine);
  4602. end;
  4603. end;
  4604. Result := Result + Line + WideLF;
  4605. end;
  4606. end;
  4607. Len := Length(Result);
  4608. if Result[Len] = WideLF then
  4609. SetLength(Result, Len - 1);
  4610. end;
  4611. //----------------------------------------------------------------------------------------------------------------------
  4612. procedure GetStringDrawRect(DC: HDC; const S: UnicodeString; var Bounds: TRect; DrawFormat: Cardinal);
  4613. // Calculates bounds of a drawing rectangle for the given string
  4614. begin
  4615. Bounds.Right := Bounds.Left + 1;
  4616. Bounds.Bottom := Bounds.Top + 1;
  4617. Windows.DrawTextW(DC, PWideChar(S), Length(S), Bounds, DrawFormat or DT_CALCRECT);
  4618. end;
  4619. //----------------------------------------------------------------------------------------------------------------------
  4620. procedure FillDragRectangles(DragWidth, DragHeight, DeltaX, DeltaY: Integer; var RClip, RScroll, RSamp1, RSamp2, RDraw1,
  4621. RDraw2: TRect);
  4622. // Fills the given rectangles with values which can be used while dragging around an image
  4623. // (used in DragMove of the drag manager and DragTo of the header columns).
  4624. begin
  4625. // ScrollDC limits
  4626. RClip := Rect(0, 0, DragWidth, DragHeight);
  4627. if DeltaX > 0 then
  4628. begin
  4629. // move to the left
  4630. if DeltaY = 0 then
  4631. begin
  4632. // move only to the left
  4633. // background movement
  4634. RScroll := Rect(0, 0, DragWidth - DeltaX, DragHeight);
  4635. RSamp1 := Rect(0, 0, DeltaX, DragHeight);
  4636. RDraw1 := Rect(DragWidth - DeltaX, 0, DeltaX, DragHeight);
  4637. end
  4638. else
  4639. if DeltaY < 0 then
  4640. begin
  4641. // move to bottom left
  4642. RScroll := Rect(0, -DeltaY, DragWidth - DeltaX, DragHeight);
  4643. RSamp1 := Rect(0, 0, DeltaX, DragHeight);
  4644. RSamp2 := Rect(DeltaX, DragHeight + DeltaY, DragWidth - DeltaX, -DeltaY);
  4645. RDraw1 := Rect(0, 0, DragWidth - DeltaX, -DeltaY);
  4646. RDraw2 := Rect(DragWidth - DeltaX, 0, DeltaX, DragHeight);
  4647. end
  4648. else
  4649. begin
  4650. // move to upper left
  4651. RScroll := Rect(0, 0, DragWidth - DeltaX, DragHeight - DeltaY);
  4652. RSamp1 := Rect(0, 0, DeltaX, DragHeight);
  4653. RSamp2 := Rect(DeltaX, 0, DragWidth - DeltaX, DeltaY);
  4654. RDraw1 := Rect(0, DragHeight - DeltaY, DragWidth - DeltaX, DeltaY);
  4655. RDraw2 := Rect(DragWidth - DeltaX, 0, DeltaX, DragHeight);
  4656. end;
  4657. end
  4658. else
  4659. if DeltaX = 0 then
  4660. begin
  4661. // vertical movement only
  4662. if DeltaY < 0 then
  4663. begin
  4664. // move downwards
  4665. RScroll := Rect(0, -DeltaY, DragWidth, DragHeight);
  4666. RSamp2 := Rect(0, DragHeight + DeltaY, DragWidth, -DeltaY);
  4667. RDraw2 := Rect(0, 0, DragWidth, -DeltaY);
  4668. end
  4669. else
  4670. begin
  4671. // move upwards
  4672. RScroll := Rect(0, 0, DragWidth, DragHeight - DeltaY);
  4673. RSamp2 := Rect(0, 0, DragWidth, DeltaY);
  4674. RDraw2 := Rect(0, DragHeight - DeltaY, DragWidth, DeltaY);
  4675. end;
  4676. end
  4677. else
  4678. begin
  4679. // move to the right
  4680. if DeltaY > 0 then
  4681. begin
  4682. // move up right
  4683. RScroll := Rect(-DeltaX, 0, DragWidth, DragHeight);
  4684. RSamp1 := Rect(0, 0, DragWidth + DeltaX, DeltaY);
  4685. RSamp2 := Rect(DragWidth + DeltaX, 0, -DeltaX, DragHeight);
  4686. RDraw1 := Rect(0, 0, -DeltaX, DragHeight);
  4687. RDraw2 := Rect(-DeltaX, DragHeight - DeltaY, DragWidth + DeltaX, DeltaY);
  4688. end
  4689. else
  4690. if DeltaY = 0 then
  4691. begin
  4692. // to the right only
  4693. RScroll := Rect(-DeltaX, 0, DragWidth, DragHeight);
  4694. RSamp1 := Rect(DragWidth + DeltaX, 0, -DeltaX, DragHeight);
  4695. RDraw1 := Rect(0, 0, -DeltaX, DragHeight);
  4696. end
  4697. else
  4698. begin
  4699. // move down right
  4700. RScroll := Rect(-DeltaX, -DeltaY, DragWidth, DragHeight);
  4701. RSamp1 := Rect(0, DragHeight + DeltaY, DragWidth + DeltaX, -DeltaY);
  4702. RSamp2 := Rect(DragWidth + DeltaX, 0, -DeltaX, DragHeight);
  4703. RDraw1 := Rect(0, 0, -DeltaX, DragHeight);
  4704. RDraw2 := Rect(-DeltaX, 0, DragWidth + DeltaX, -DeltaY);
  4705. end;
  4706. end;
  4707. end;
  4708. //----------------------------------------------------------------------------------------------------------------------
  4709. procedure AlphaBlendLineConstant(Source, Destination: Pointer; Count: Integer; ConstantAlpha, Bias: Integer);
  4710. // Blends a line of Count pixels from Source to Destination using a constant alpha value.
  4711. // The layout of a pixel must be BGRA where A is ignored (but is calculated as the other components).
  4712. // ConstantAlpha must be in the range 0..255 where 0 means totally transparent (destination pixel only)
  4713. // and 255 totally opaque (source pixel only).
  4714. // Bias is an additional value which gets added to every component and must be in the range -128..127
  4715. //
  4716. {$ifdef CPUX64}
  4717. // RCX contains Source
  4718. // RDX contains Destination
  4719. // R8D contains Count
  4720. // R9D contains ConstantAlpha
  4721. // Bias is on the stack
  4722. asm
  4723. //.NOFRAME
  4724. // Load XMM3 with the constant alpha value (replicate it for every component).
  4725. // Expand it to word size.
  4726. MOVD XMM3, R9D // ConstantAlpha
  4727. PUNPCKLWD XMM3, XMM3
  4728. PUNPCKLDQ XMM3, XMM3
  4729. // Load XMM5 with the bias value.
  4730. MOVD XMM5, [Bias]
  4731. PUNPCKLWD XMM5, XMM5
  4732. PUNPCKLDQ XMM5, XMM5
  4733. // Load XMM4 with 128 to allow for saturated biasing.
  4734. MOV R10D, 128
  4735. MOVD XMM4, R10D
  4736. PUNPCKLWD XMM4, XMM4
  4737. PUNPCKLDQ XMM4, XMM4
  4738. @1: // The pixel loop calculates an entire pixel in one run.
  4739. // Note: The pixel byte values are expanded into the higher bytes of a word due
  4740. // to the way unpacking works. We compensate for this with an extra shift.
  4741. MOVD XMM1, DWORD PTR [RCX] // data is unaligned
  4742. MOVD XMM2, DWORD PTR [RDX] // data is unaligned
  4743. PXOR XMM0, XMM0 // clear source pixel register for unpacking
  4744. PUNPCKLBW XMM0, XMM1{[RCX]} // unpack source pixel byte values into words
  4745. PSRLW XMM0, 8 // move higher bytes to lower bytes
  4746. PXOR XMM1, XMM1 // clear target pixel register for unpacking
  4747. PUNPCKLBW XMM1, XMM2{[RDX]} // unpack target pixel byte values into words
  4748. MOVQ XMM2, XMM1 // make a copy of the shifted values, we need them again
  4749. PSRLW XMM1, 8 // move higher bytes to lower bytes
  4750. // calculation is: target = (alpha * (source - target) + 256 * target) / 256
  4751. PSUBW XMM0, XMM1 // source - target
  4752. PMULLW XMM0, XMM3 // alpha * (source - target)
  4753. PADDW XMM0, XMM2 // add target (in shifted form)
  4754. PSRLW XMM0, 8 // divide by 256
  4755. // Bias is accounted for by conversion of range 0..255 to -128..127,
  4756. // doing a saturated add and convert back to 0..255.
  4757. PSUBW XMM0, XMM4
  4758. PADDSW XMM0, XMM5
  4759. PADDW XMM0, XMM4
  4760. PACKUSWB XMM0, XMM0 // convert words to bytes with saturation
  4761. MOVD DWORD PTR [RDX], XMM0 // store the result
  4762. @3:
  4763. ADD RCX, 4
  4764. ADD RDX, 4
  4765. DEC R8D
  4766. JNZ @1
  4767. end;
  4768. {$else}
  4769. // EAX contains Source
  4770. // EDX contains Destination
  4771. // ECX contains Count
  4772. // ConstantAlpha and Bias are on the stack
  4773. asm
  4774. PUSH ESI // save used registers
  4775. PUSH EDI
  4776. MOV ESI, EAX // ESI becomes the actual source pointer
  4777. MOV EDI, EDX // EDI becomes the actual target pointer
  4778. // Load MM6 with the constant alpha value (replicate it for every component).
  4779. // Expand it to word size.
  4780. MOV EAX, [ConstantAlpha]
  4781. DB $0F, $6E, $F0 /// MOVD MM6, EAX
  4782. DB $0F, $61, $F6 /// PUNPCKLWD MM6, MM6
  4783. DB $0F, $62, $F6 /// PUNPCKLDQ MM6, MM6
  4784. // Load MM5 with the bias value.
  4785. MOV EAX, [Bias]
  4786. DB $0F, $6E, $E8 /// MOVD MM5, EAX
  4787. DB $0F, $61, $ED /// PUNPCKLWD MM5, MM5
  4788. DB $0F, $62, $ED /// PUNPCKLDQ MM5, MM5
  4789. // Load MM4 with 128 to allow for saturated biasing.
  4790. MOV EAX, 128
  4791. DB $0F, $6E, $E0 /// MOVD MM4, EAX
  4792. DB $0F, $61, $E4 /// PUNPCKLWD MM4, MM4
  4793. DB $0F, $62, $E4 /// PUNPCKLDQ MM4, MM4
  4794. @1: // The pixel loop calculates an entire pixel in one run.
  4795. // Note: The pixel byte values are expanded into the higher bytes of a word due
  4796. // to the way unpacking works. We compensate for this with an extra shift.
  4797. DB $0F, $EF, $C0 /// PXOR MM0, MM0, clear source pixel register for unpacking
  4798. DB $0F, $60, $06 /// PUNPCKLBW MM0, [ESI], unpack source pixel byte values into words
  4799. DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, move higher bytes to lower bytes
  4800. DB $0F, $EF, $C9 /// PXOR MM1, MM1, clear target pixel register for unpacking
  4801. DB $0F, $60, $0F /// PUNPCKLBW MM1, [EDI], unpack target pixel byte values into words
  4802. DB $0F, $6F, $D1 /// MOVQ MM2, MM1, make a copy of the shifted values, we need them again
  4803. DB $0F, $71, $D1, $08 /// PSRLW MM1, 8, move higher bytes to lower bytes
  4804. // calculation is: target = (alpha * (source - target) + 256 * target) / 256
  4805. DB $0F, $F9, $C1 /// PSUBW MM0, MM1, source - target
  4806. DB $0F, $D5, $C6 /// PMULLW MM0, MM6, alpha * (source - target)
  4807. DB $0F, $FD, $C2 /// PADDW MM0, MM2, add target (in shifted form)
  4808. DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, divide by 256
  4809. // Bias is accounted for by conversion of range 0..255 to -128..127,
  4810. // doing a saturated add and convert back to 0..255.
  4811. DB $0F, $F9, $C4 /// PSUBW MM0, MM4
  4812. DB $0F, $ED, $C5 /// PADDSW MM0, MM5
  4813. DB $0F, $FD, $C4 /// PADDW MM0, MM4
  4814. DB $0F, $67, $C0 /// PACKUSWB MM0, MM0, convert words to bytes with saturation
  4815. DB $0F, $7E, $07 /// MOVD [EDI], MM0, store the result
  4816. @3:
  4817. ADD ESI, 4
  4818. ADD EDI, 4
  4819. DEC ECX
  4820. JNZ @1
  4821. POP EDI
  4822. POP ESI
  4823. end;
  4824. {$endif CPUX64}
  4825. //----------------------------------------------------------------------------------------------------------------------
  4826. procedure AlphaBlendLinePerPixel(Source, Destination: Pointer; Count, Bias: Integer);
  4827. // Blends a line of Count pixels from Source to Destination using the alpha value of the source pixels.
  4828. // The layout of a pixel must be BGRA.
  4829. // Bias is an additional value which gets added to every component and must be in the range -128..127
  4830. //
  4831. {$ifdef CPUX64}
  4832. // RCX contains Source
  4833. // RDX contains Destination
  4834. // R8D contains Count
  4835. // R9D contains Bias
  4836. asm
  4837. //.NOFRAME
  4838. // Load XMM5 with the bias value.
  4839. MOVD XMM5, R9D // Bias
  4840. PUNPCKLWD XMM5, XMM5
  4841. PUNPCKLDQ XMM5, XMM5
  4842. // Load XMM4 with 128 to allow for saturated biasing.
  4843. MOV R10D, 128
  4844. MOVD XMM4, R10D
  4845. PUNPCKLWD XMM4, XMM4
  4846. PUNPCKLDQ XMM4, XMM4
  4847. @1: // The pixel loop calculates an entire pixel in one run.
  4848. // Note: The pixel byte values are expanded into the higher bytes of a word due
  4849. // to the way unpacking works. We compensate for this with an extra shift.
  4850. MOVD XMM1, DWORD PTR [RCX] // data is unaligned
  4851. MOVD XMM2, DWORD PTR [RDX] // data is unaligned
  4852. PXOR XMM0, XMM0 // clear source pixel register for unpacking
  4853. PUNPCKLBW XMM0, XMM1{[RCX]} // unpack source pixel byte values into words
  4854. PSRLW XMM0, 8 // move higher bytes to lower bytes
  4855. PXOR XMM1, XMM1 // clear target pixel register for unpacking
  4856. PUNPCKLBW XMM1, XMM2{[RDX]} // unpack target pixel byte values into words
  4857. MOVQ XMM2, XMM1 // make a copy of the shifted values, we need them again
  4858. PSRLW XMM1, 8 // move higher bytes to lower bytes
  4859. // Load XMM3 with the source alpha value (replicate it for every component).
  4860. // Expand it to word size.
  4861. MOVQ XMM3, XMM0
  4862. PUNPCKHWD XMM3, XMM3
  4863. PUNPCKHDQ XMM3, XMM3
  4864. // calculation is: target = (alpha * (source - target) + 256 * target) / 256
  4865. PSUBW XMM0, XMM1 // source - target
  4866. PMULLW XMM0, XMM3 // alpha * (source - target)
  4867. PADDW XMM0, XMM2 // add target (in shifted form)
  4868. PSRLW XMM0, 8 // divide by 256
  4869. // Bias is accounted for by conversion of range 0..255 to -128..127,
  4870. // doing a saturated add and convert back to 0..255.
  4871. PSUBW XMM0, XMM4
  4872. PADDSW XMM0, XMM5
  4873. PADDW XMM0, XMM4
  4874. PACKUSWB XMM0, XMM0 // convert words to bytes with saturation
  4875. MOVD DWORD PTR [RDX], XMM0 // store the result
  4876. @3:
  4877. ADD RCX, 4
  4878. ADD RDX, 4
  4879. DEC R8D
  4880. JNZ @1
  4881. end;
  4882. {$else}
  4883. // EAX contains Source
  4884. // EDX contains Destination
  4885. // ECX contains Count
  4886. // Bias is on the stack
  4887. asm
  4888. PUSH ESI // save used registers
  4889. PUSH EDI
  4890. MOV ESI, EAX // ESI becomes the actual source pointer
  4891. MOV EDI, EDX // EDI becomes the actual target pointer
  4892. // Load MM5 with the bias value.
  4893. MOV EAX, [Bias]
  4894. DB $0F, $6E, $E8 /// MOVD MM5, EAX
  4895. DB $0F, $61, $ED /// PUNPCKLWD MM5, MM5
  4896. DB $0F, $62, $ED /// PUNPCKLDQ MM5, MM5
  4897. // Load MM4 with 128 to allow for saturated biasing.
  4898. MOV EAX, 128
  4899. DB $0F, $6E, $E0 /// MOVD MM4, EAX
  4900. DB $0F, $61, $E4 /// PUNPCKLWD MM4, MM4
  4901. DB $0F, $62, $E4 /// PUNPCKLDQ MM4, MM4
  4902. @1: // The pixel loop calculates an entire pixel in one run.
  4903. // Note: The pixel byte values are expanded into the higher bytes of a word due
  4904. // to the way unpacking works. We compensate for this with an extra shift.
  4905. DB $0F, $EF, $C0 /// PXOR MM0, MM0, clear source pixel register for unpacking
  4906. DB $0F, $60, $06 /// PUNPCKLBW MM0, [ESI], unpack source pixel byte values into words
  4907. DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, move higher bytes to lower bytes
  4908. DB $0F, $EF, $C9 /// PXOR MM1, MM1, clear target pixel register for unpacking
  4909. DB $0F, $60, $0F /// PUNPCKLBW MM1, [EDI], unpack target pixel byte values into words
  4910. DB $0F, $6F, $D1 /// MOVQ MM2, MM1, make a copy of the shifted values, we need them again
  4911. DB $0F, $71, $D1, $08 /// PSRLW MM1, 8, move higher bytes to lower bytes
  4912. // Load MM6 with the source alpha value (replicate it for every component).
  4913. // Expand it to word size.
  4914. DB $0F, $6F, $F0 /// MOVQ MM6, MM0
  4915. DB $0F, $69, $F6 /// PUNPCKHWD MM6, MM6
  4916. DB $0F, $6A, $F6 /// PUNPCKHDQ MM6, MM6
  4917. // calculation is: target = (alpha * (source - target) + 256 * target) / 256
  4918. DB $0F, $F9, $C1 /// PSUBW MM0, MM1, source - target
  4919. DB $0F, $D5, $C6 /// PMULLW MM0, MM6, alpha * (source - target)
  4920. DB $0F, $FD, $C2 /// PADDW MM0, MM2, add target (in shifted form)
  4921. DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, divide by 256
  4922. // Bias is accounted for by conversion of range 0..255 to -128..127,
  4923. // doing a saturated add and convert back to 0..255.
  4924. DB $0F, $F9, $C4 /// PSUBW MM0, MM4
  4925. DB $0F, $ED, $C5 /// PADDSW MM0, MM5
  4926. DB $0F, $FD, $C4 /// PADDW MM0, MM4
  4927. DB $0F, $67, $C0 /// PACKUSWB MM0, MM0, convert words to bytes with saturation
  4928. DB $0F, $7E, $07 /// MOVD [EDI], MM0, store the result
  4929. @3:
  4930. ADD ESI, 4
  4931. ADD EDI, 4
  4932. DEC ECX
  4933. JNZ @1
  4934. POP EDI
  4935. POP ESI
  4936. end;
  4937. {$endif CPUX64}
  4938. //----------------------------------------------------------------------------------------------------------------------
  4939. procedure AlphaBlendLineMaster(Source, Destination: Pointer; Count: Integer; ConstantAlpha, Bias: Integer);
  4940. // Blends a line of Count pixels from Source to Destination using the source pixel and a constant alpha value.
  4941. // The layout of a pixel must be BGRA.
  4942. // ConstantAlpha must be in the range 0..255.
  4943. // Bias is an additional value which gets added to every component and must be in the range -128..127
  4944. //
  4945. {$ifdef CPUX64}
  4946. // RCX contains Source
  4947. // RDX contains Destination
  4948. // R8D contains Count
  4949. // R9D contains ConstantAlpha
  4950. // Bias is on the stack
  4951. asm
  4952. .SAVENV XMM6
  4953. // Load XMM3 with the constant alpha value (replicate it for every component).
  4954. // Expand it to word size.
  4955. MOVD XMM3, R9D // ConstantAlpha
  4956. PUNPCKLWD XMM3, XMM3
  4957. PUNPCKLDQ XMM3, XMM3
  4958. // Load XMM5 with the bias value.
  4959. MOV R10D, [Bias]
  4960. MOVD XMM5, R10D
  4961. PUNPCKLWD XMM5, XMM5
  4962. PUNPCKLDQ XMM5, XMM5
  4963. // Load XMM4 with 128 to allow for saturated biasing.
  4964. MOV R10D, 128
  4965. MOVD XMM4, R10D
  4966. PUNPCKLWD XMM4, XMM4
  4967. PUNPCKLDQ XMM4, XMM4
  4968. @1: // The pixel loop calculates an entire pixel in one run.
  4969. // Note: The pixel byte values are expanded into the higher bytes of a word due
  4970. // to the way unpacking works. We compensate for this with an extra shift.
  4971. MOVD XMM1, DWORD PTR [RCX] // data is unaligned
  4972. MOVD XMM2, DWORD PTR [RDX] // data is unaligned
  4973. PXOR XMM0, XMM0 // clear source pixel register for unpacking
  4974. PUNPCKLBW XMM0, XMM1{[RCX]} // unpack source pixel byte values into words
  4975. PSRLW XMM0, 8 // move higher bytes to lower bytes
  4976. PXOR XMM1, XMM1 // clear target pixel register for unpacking
  4977. PUNPCKLBW XMM1, XMM2{[RCX]} // unpack target pixel byte values into words
  4978. MOVQ XMM2, XMM1 // make a copy of the shifted values, we need them again
  4979. PSRLW XMM1, 8 // move higher bytes to lower bytes
  4980. // Load XMM6 with the source alpha value (replicate it for every component).
  4981. // Expand it to word size.
  4982. MOVQ XMM6, XMM0
  4983. PUNPCKHWD XMM6, XMM6
  4984. PUNPCKHDQ XMM6, XMM6
  4985. PMULLW XMM6, XMM3 // source alpha * master alpha
  4986. PSRLW XMM6, 8 // divide by 256
  4987. // calculation is: target = (alpha * master alpha * (source - target) + 256 * target) / 256
  4988. PSUBW XMM0, XMM1 // source - target
  4989. PMULLW XMM0, XMM6 // alpha * (source - target)
  4990. PADDW XMM0, XMM2 // add target (in shifted form)
  4991. PSRLW XMM0, 8 // divide by 256
  4992. // Bias is accounted for by conversion of range 0..255 to -128..127,
  4993. // doing a saturated add and convert back to 0..255.
  4994. PSUBW XMM0, XMM4
  4995. PADDSW XMM0, XMM5
  4996. PADDW XMM0, XMM4
  4997. PACKUSWB XMM0, XMM0 // convert words to bytes with saturation
  4998. MOVD DWORD PTR [RDX], XMM0 // store the result
  4999. @3:
  5000. ADD RCX, 4
  5001. ADD RDX, 4
  5002. DEC R8D
  5003. JNZ @1
  5004. end;
  5005. {$else}
  5006. // EAX contains Source
  5007. // EDX contains Destination
  5008. // ECX contains Count
  5009. // ConstantAlpha and Bias are on the stack
  5010. asm
  5011. PUSH ESI // save used registers
  5012. PUSH EDI
  5013. MOV ESI, EAX // ESI becomes the actual source pointer
  5014. MOV EDI, EDX // EDI becomes the actual target pointer
  5015. // Load MM6 with the constant alpha value (replicate it for every component).
  5016. // Expand it to word size.
  5017. MOV EAX, [ConstantAlpha]
  5018. DB $0F, $6E, $F0 /// MOVD MM6, EAX
  5019. DB $0F, $61, $F6 /// PUNPCKLWD MM6, MM6
  5020. DB $0F, $62, $F6 /// PUNPCKLDQ MM6, MM6
  5021. // Load MM5 with the bias value.
  5022. MOV EAX, [Bias]
  5023. DB $0F, $6E, $E8 /// MOVD MM5, EAX
  5024. DB $0F, $61, $ED /// PUNPCKLWD MM5, MM5
  5025. DB $0F, $62, $ED /// PUNPCKLDQ MM5, MM5
  5026. // Load MM4 with 128 to allow for saturated biasing.
  5027. MOV EAX, 128
  5028. DB $0F, $6E, $E0 /// MOVD MM4, EAX
  5029. DB $0F, $61, $E4 /// PUNPCKLWD MM4, MM4
  5030. DB $0F, $62, $E4 /// PUNPCKLDQ MM4, MM4
  5031. @1: // The pixel loop calculates an entire pixel in one run.
  5032. // Note: The pixel byte values are expanded into the higher bytes of a word due
  5033. // to the way unpacking works. We compensate for this with an extra shift.
  5034. DB $0F, $EF, $C0 /// PXOR MM0, MM0, clear source pixel register for unpacking
  5035. DB $0F, $60, $06 /// PUNPCKLBW MM0, [ESI], unpack source pixel byte values into words
  5036. DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, move higher bytes to lower bytes
  5037. DB $0F, $EF, $C9 /// PXOR MM1, MM1, clear target pixel register for unpacking
  5038. DB $0F, $60, $0F /// PUNPCKLBW MM1, [EDI], unpack target pixel byte values into words
  5039. DB $0F, $6F, $D1 /// MOVQ MM2, MM1, make a copy of the shifted values, we need them again
  5040. DB $0F, $71, $D1, $08 /// PSRLW MM1, 8, move higher bytes to lower bytes
  5041. // Load MM7 with the source alpha value (replicate it for every component).
  5042. // Expand it to word size.
  5043. DB $0F, $6F, $F8 /// MOVQ MM7, MM0
  5044. DB $0F, $69, $FF /// PUNPCKHWD MM7, MM7
  5045. DB $0F, $6A, $FF /// PUNPCKHDQ MM7, MM7
  5046. DB $0F, $D5, $FE /// PMULLW MM7, MM6, source alpha * master alpha
  5047. DB $0F, $71, $D7, $08 /// PSRLW MM7, 8, divide by 256
  5048. // calculation is: target = (alpha * master alpha * (source - target) + 256 * target) / 256
  5049. DB $0F, $F9, $C1 /// PSUBW MM0, MM1, source - target
  5050. DB $0F, $D5, $C7 /// PMULLW MM0, MM7, alpha * (source - target)
  5051. DB $0F, $FD, $C2 /// PADDW MM0, MM2, add target (in shifted form)
  5052. DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, divide by 256
  5053. // Bias is accounted for by conversion of range 0..255 to -128..127,
  5054. // doing a saturated add and convert back to 0..255.
  5055. DB $0F, $F9, $C4 /// PSUBW MM0, MM4
  5056. DB $0F, $ED, $C5 /// PADDSW MM0, MM5
  5057. DB $0F, $FD, $C4 /// PADDW MM0, MM4
  5058. DB $0F, $67, $C0 /// PACKUSWB MM0, MM0, convert words to bytes with saturation
  5059. DB $0F, $7E, $07 /// MOVD [EDI], MM0, store the result
  5060. @3:
  5061. ADD ESI, 4
  5062. ADD EDI, 4
  5063. DEC ECX
  5064. JNZ @1
  5065. POP EDI
  5066. POP ESI
  5067. end;
  5068. {$endif CPUX64}
  5069. //----------------------------------------------------------------------------------------------------------------------
  5070. procedure AlphaBlendLineMasterAndColor(Destination: Pointer; Count: Integer; ConstantAlpha, Color: Integer);
  5071. // Blends a line of Count pixels in Destination against the given color using a constant alpha value.
  5072. // The layout of a pixel must be BGRA and Color must be rrggbb00 (as stored by a COLORREF).
  5073. // ConstantAlpha must be in the range 0..255.
  5074. //
  5075. {$ifdef CPUX64}
  5076. // RCX contains Destination
  5077. // EDX contains Count
  5078. // R8D contains ConstantAlpha
  5079. // R9D contains Color
  5080. asm
  5081. //.NOFRAME
  5082. // The used formula is: target = (alpha * color + (256 - alpha) * target) / 256.
  5083. // alpha * color (factor 1) and 256 - alpha (factor 2) are constant values which can be calculated in advance.
  5084. // The remaining calculation is therefore: target = (F1 + F2 * target) / 256
  5085. // Load XMM3 with the constant alpha value (replicate it for every component).
  5086. // Expand it to word size. (Every calculation here works on word sized operands.)
  5087. MOVD XMM3, R8D // ConstantAlpha
  5088. PUNPCKLWD XMM3, XMM3
  5089. PUNPCKLDQ XMM3, XMM3
  5090. // Calculate factor 2.
  5091. MOV R10D, $100
  5092. MOVD XMM2, R10D
  5093. PUNPCKLWD XMM2, XMM2
  5094. PUNPCKLDQ XMM2, XMM2
  5095. PSUBW XMM2, XMM3 // XMM2 contains now: 255 - alpha = F2
  5096. // Now calculate factor 1. Alpha is still in XMM3, but the r and b components of Color must be swapped.
  5097. BSWAP R9D // Color
  5098. ROR R9D, 8
  5099. MOVD XMM1, R9D // Load the color and convert to word sized values.
  5100. PXOR XMM4, XMM4
  5101. PUNPCKLBW XMM1, XMM4
  5102. PMULLW XMM1, XMM3 // XMM1 contains now: color * alpha = F1
  5103. @1: // The pixel loop calculates an entire pixel in one run.
  5104. MOVD XMM0, DWORD PTR [RCX]
  5105. PUNPCKLBW XMM0, XMM4
  5106. PMULLW XMM0, XMM2 // calculate F1 + F2 * target
  5107. PADDW XMM0, XMM1
  5108. PSRLW XMM0, 8 // divide by 256
  5109. PACKUSWB XMM0, XMM0 // convert words to bytes with saturation
  5110. MOVD DWORD PTR [RCX], XMM0 // store the result
  5111. ADD RCX, 4
  5112. DEC EDX
  5113. JNZ @1
  5114. end;
  5115. {$else}
  5116. // EAX contains Destination
  5117. // EDX contains Count
  5118. // ECX contains ConstantAlpha
  5119. // Color is passed on the stack
  5120. asm
  5121. // The used formula is: target = (alpha * color + (256 - alpha) * target) / 256.
  5122. // alpha * color (factor 1) and 256 - alpha (factor 2) are constant values which can be calculated in advance.
  5123. // The remaining calculation is therefore: target = (F1 + F2 * target) / 256
  5124. // Load MM3 with the constant alpha value (replicate it for every component).
  5125. // Expand it to word size. (Every calculation here works on word sized operands.)
  5126. DB $0F, $6E, $D9 /// MOVD MM3, ECX
  5127. DB $0F, $61, $DB /// PUNPCKLWD MM3, MM3
  5128. DB $0F, $62, $DB /// PUNPCKLDQ MM3, MM3
  5129. // Calculate factor 2.
  5130. MOV ECX, $100
  5131. DB $0F, $6E, $D1 /// MOVD MM2, ECX
  5132. DB $0F, $61, $D2 /// PUNPCKLWD MM2, MM2
  5133. DB $0F, $62, $D2 /// PUNPCKLDQ MM2, MM2
  5134. DB $0F, $F9, $D3 /// PSUBW MM2, MM3 // MM2 contains now: 255 - alpha = F2
  5135. // Now calculate factor 1. Alpha is still in MM3, but the r and b components of Color must be swapped.
  5136. MOV ECX, [Color]
  5137. BSWAP ECX
  5138. ROR ECX, 8
  5139. DB $0F, $6E, $C9 /// MOVD MM1, ECX // Load the color and convert to word sized values.
  5140. DB $0F, $EF, $E4 /// PXOR MM4, MM4
  5141. DB $0F, $60, $CC /// PUNPCKLBW MM1, MM4
  5142. DB $0F, $D5, $CB /// PMULLW MM1, MM3 // MM1 contains now: color * alpha = F1
  5143. @1: // The pixel loop calculates an entire pixel in one run.
  5144. DB $0F, $6E, $00 /// MOVD MM0, [EAX]
  5145. DB $0F, $60, $C4 /// PUNPCKLBW MM0, MM4
  5146. DB $0F, $D5, $C2 /// PMULLW MM0, MM2 // calculate F1 + F2 * target
  5147. DB $0F, $FD, $C1 /// PADDW MM0, MM1
  5148. DB $0F, $71, $D0, $08 /// PSRLW MM0, 8 // divide by 256
  5149. DB $0F, $67, $C0 /// PACKUSWB MM0, MM0 // convert words to bytes with saturation
  5150. DB $0F, $7E, $00 /// MOVD [EAX], MM0 // store the result
  5151. ADD EAX, 4
  5152. DEC EDX
  5153. JNZ @1
  5154. end;
  5155. {$endif CPUX64}
  5156. //----------------------------------------------------------------------------------------------------------------------
  5157. procedure EMMS;
  5158. // Reset MMX state to use the FPU for other tasks again.
  5159. {$ifdef CPUX64}
  5160. inline;
  5161. begin
  5162. end;
  5163. {$else}
  5164. asm
  5165. DB $0F, $77 /// EMMS
  5166. end;
  5167. {$endif CPUX64}
  5168. //----------------------------------------------------------------------------------------------------------------------
  5169. function GetBitmapBitsFromDeviceContext(DC: HDC; var Width, Height: Integer): Pointer;
  5170. // Helper function used to retrieve the bitmap selected into the given device context. If there is a bitmap then
  5171. // the function will return a pointer to its bits otherwise nil is returned.
  5172. // Additionally the dimensions of the bitmap are returned.
  5173. var
  5174. Bitmap: HBITMAP;
  5175. DIB: TDIBSection;
  5176. begin
  5177. Result := nil;
  5178. Width := 0;
  5179. Height := 0;
  5180. Bitmap := GetCurrentObject(DC, OBJ_BITMAP);
  5181. if Bitmap <> 0 then
  5182. begin
  5183. if GetObject(Bitmap, SizeOf(DIB), @DIB) = SizeOf(DIB) then
  5184. begin
  5185. Assert(DIB.dsBm.bmPlanes * DIB.dsBm.bmBitsPixel = 32, 'Alpha blending error: bitmap must use 32 bpp.');
  5186. Result := DIB.dsBm.bmBits;
  5187. Width := DIB.dsBmih.biWidth;
  5188. Height := DIB.dsBmih.biHeight;
  5189. end;
  5190. end;
  5191. Assert(Result <> nil, 'Alpha blending DC error: no bitmap available.');
  5192. end;
  5193. //----------------------------------------------------------------------------------------------------------------------
  5194. function CalculateScanline(Bits: Pointer; Width, Height, Row: Integer): Pointer;
  5195. // Helper function to calculate the start address for the given row.
  5196. begin
  5197. if Height > 0 then // bottom-up DIB
  5198. Row := Height - Row - 1;
  5199. // Return DWORD aligned address of the requested scanline.
  5200. Result := PAnsiChar(Bits) + Row * ((Width * 32 + 31) and not 31) div 8;
  5201. end;
  5202. //----------------------------------------------------------------------------------------------------------------------
  5203. procedure AlphaBlend(Source, Destination: HDC; R: TRect; Target: TPoint; Mode: TBlendMode; ConstantAlpha, Bias: Integer);
  5204. // Optimized alpha blend procedure using MMX instructions to perform as quick as possible.
  5205. // For this procedure to work properly it is important that both source and target bitmap use the 32 bit color format.
  5206. // R describes the source rectangle to work on.
  5207. // Target is the place (upper left corner) in the target bitmap where to blend to. Note that source width + X offset
  5208. // must be less or equal to the target width. Similar for the height.
  5209. // If Mode is bmConstantAlpha then the blend operation uses the given ConstantAlpha value for all pixels.
  5210. // If Mode is bmPerPixelAlpha then each pixel is blended using its individual alpha value (the alpha value of the source).
  5211. // If Mode is bmMasterAlpha then each pixel is blended using its individual alpha value multiplied by ConstantAlpha.
  5212. // If Mode is bmConstantAlphaAndColor then each destination pixel is blended using ConstantAlpha but also a constant
  5213. // color which will be obtained from Bias. In this case no offset value is added, otherwise Bias is used as offset.
  5214. // Blending of a color into target only (bmConstantAlphaAndColor) ignores Source (the DC) and Target (the position).
  5215. // CAUTION: This procedure does not check whether MMX instructions are actually available! Call it only if MMX is really
  5216. // usable.
  5217. var
  5218. Y: Integer;
  5219. SourceRun,
  5220. TargetRun: PByte;
  5221. SourceBits,
  5222. DestBits: Pointer;
  5223. SourceWidth,
  5224. SourceHeight,
  5225. DestWidth,
  5226. DestHeight: Integer;
  5227. begin
  5228. if not IsRectEmpty(R) then
  5229. begin
  5230. // Note: it is tempting to optimize the special cases for constant alpha 0 and 255 by just ignoring soure
  5231. // (alpha = 0) or simply do a blit (alpha = 255). But this does not take the bias into account.
  5232. case Mode of
  5233. bmConstantAlpha:
  5234. begin
  5235. // Get a pointer to the bitmap bits for the source and target device contexts.
  5236. // Note: this supposes that both contexts do actually have bitmaps assigned!
  5237. SourceBits := GetBitmapBitsFromDeviceContext(Source, SourceWidth, SourceHeight);
  5238. DestBits := GetBitmapBitsFromDeviceContext(Destination, DestWidth, DestHeight);
  5239. if Assigned(SourceBits) and Assigned(DestBits) then
  5240. begin
  5241. for Y := 0 to R.Bottom - R.Top - 1 do
  5242. begin
  5243. SourceRun := CalculateScanline(SourceBits, SourceWidth, SourceHeight, Y + R.Top);
  5244. Inc(SourceRun, 4 * R.Left);
  5245. TargetRun := CalculateScanline(DestBits, DestWidth, DestHeight, Y + Target.Y);
  5246. Inc(TargetRun, 4 * Target.X);
  5247. AlphaBlendLineConstant(SourceRun, TargetRun, R.Right - R.Left, ConstantAlpha, Bias);
  5248. end;
  5249. end;
  5250. EMMS;
  5251. end;
  5252. bmPerPixelAlpha:
  5253. begin
  5254. SourceBits := GetBitmapBitsFromDeviceContext(Source, SourceWidth, SourceHeight);
  5255. DestBits := GetBitmapBitsFromDeviceContext(Destination, DestWidth, DestHeight);
  5256. if Assigned(SourceBits) and Assigned(DestBits) then
  5257. begin
  5258. for Y := 0 to R.Bottom - R.Top - 1 do
  5259. begin
  5260. SourceRun := CalculateScanline(SourceBits, SourceWidth, SourceHeight, Y + R.Top);
  5261. Inc(SourceRun, 4 * R.Left);
  5262. TargetRun := CalculateScanline(DestBits, DestWidth, DestHeight, Y + Target.Y);
  5263. Inc(TargetRun, 4 * Target.X);
  5264. AlphaBlendLinePerPixel(SourceRun, TargetRun, R.Right - R.Left, Bias);
  5265. end;
  5266. end;
  5267. EMMS;
  5268. end;
  5269. bmMasterAlpha:
  5270. begin
  5271. SourceBits := GetBitmapBitsFromDeviceContext(Source, SourceWidth, SourceHeight);
  5272. DestBits := GetBitmapBitsFromDeviceContext(Destination, DestWidth, DestHeight);
  5273. if Assigned(SourceBits) and Assigned(DestBits) then
  5274. begin
  5275. for Y := 0 to R.Bottom - R.Top - 1 do
  5276. begin
  5277. SourceRun := CalculateScanline(SourceBits, SourceWidth, SourceHeight, Y + R.Top);
  5278. Inc(SourceRun, 4 * Target.X);
  5279. TargetRun := CalculateScanline(DestBits, DestWidth, DestHeight, Y + Target.Y);
  5280. AlphaBlendLineMaster(SourceRun, TargetRun, R.Right - R.Left, ConstantAlpha, Bias);
  5281. end;
  5282. end;
  5283. EMMS;
  5284. end;
  5285. bmConstantAlphaAndColor:
  5286. begin
  5287. // Source is ignored since there is a constant color value.
  5288. DestBits := GetBitmapBitsFromDeviceContext(Destination, DestWidth, DestHeight);
  5289. if Assigned(DestBits) then
  5290. begin
  5291. for Y := 0 to R.Bottom - R.Top - 1 do
  5292. begin
  5293. TargetRun := CalculateScanline(DestBits, DestWidth, DestHeight, Y + R.Top);
  5294. Inc(TargetRun, 4 * R.Left);
  5295. AlphaBlendLineMasterAndColor(TargetRun, R.Right - R.Left, ConstantAlpha, Bias);
  5296. end;
  5297. end;
  5298. EMMS;
  5299. end;
  5300. end;
  5301. end;
  5302. end;
  5303. //----------------------------------------------------------------------------------------------------------------------
  5304. function GetRGBColor(Value: TColor): DWORD;
  5305. // Little helper to convert a Delphi color to an image list color.
  5306. begin
  5307. Result := ColorToRGB(Value);
  5308. case Result of
  5309. clNone:
  5310. Result := CLR_NONE;
  5311. clDefault:
  5312. Result := CLR_DEFAULT;
  5313. end;
  5314. end;
  5315. //----------------------------------------------------------------------------------------------------------------------
  5316. const
  5317. Grays: array[0..3] of TColor = (clWhite, clSilver, clGray, clBlack);
  5318. SysGrays: array[0..3] of TColor = (clWindow, clBtnFace, clBtnShadow, clBtnText);
  5319. procedure ConvertImageList(IL: TImageList; const ImageName: string; ColorRemapping: Boolean = True);
  5320. // Loads a bunch of images given by ImageName into IL. If ColorRemapping = True then a mapping of gray values to
  5321. // system colors is performed.
  5322. var
  5323. Images,
  5324. OneImage: TBitmap;
  5325. I: Integer;
  5326. MaskColor: TColor;
  5327. Source,
  5328. Dest: TRect;
  5329. begin
  5330. Watcher.Enter;
  5331. try
  5332. // Since we want the image list appearing in the correct system colors, we have to remap its colors.
  5333. Images := TBitmap.Create;
  5334. OneImage := TBitmap.Create;
  5335. if ColorRemapping then
  5336. Images.Handle := CreateMappedRes(FindClassHInstance(TBaseVirtualTree), PChar(ImageName), Grays, SysGrays)
  5337. else
  5338. Images.Handle := LoadBitmap(FindClassHInstance(TBaseVirtualTree), PChar(ImageName));
  5339. try
  5340. Assert(Images.Height > 0, 'Internal image "' + ImageName + '" is missing or corrupt.');
  5341. if Images.Height = 0 then
  5342. Exit;// This should never happen, it prevents a division by zero exception below in the for loop, which we have seen in a few cases
  5343. // It is assumed that the image height determines also the width of one entry in the image list.
  5344. IL.Clear;
  5345. IL.Height := Images.Height;
  5346. IL.Width := Images.Height;
  5347. OneImage.Width := IL.Width;
  5348. OneImage.Height := IL.Height;
  5349. MaskColor := Images.Canvas.Pixels[0, 0]; // this is usually clFuchsia
  5350. Dest := Rect(0, 0, IL.Width, IL.Height);
  5351. for I := 0 to (Images.Width div Images.Height) - 1 do
  5352. begin
  5353. Source := Rect(I * IL.Width, 0, (I + 1) * IL.Width, IL.Height);
  5354. OneImage.Canvas.CopyRect(Dest, Images.Canvas, Source);
  5355. IL.AddMasked(OneImage, MaskColor);
  5356. end;
  5357. finally
  5358. Images.Free;
  5359. OneImage.Free;
  5360. end;
  5361. finally
  5362. Watcher.Leave;
  5363. end;
  5364. end;
  5365. //----------------------------------------------------------------------------------------------------------------------
  5366. procedure CreateSystemImageSet(var IL: TImageList; Flags: Cardinal; Flat: Boolean);
  5367. // Creates a system check image set.
  5368. // Note: the DarkCheckImages and FlatImages image lists must already be filled, as some images from them are copied here.
  5369. const
  5370. MaskColor: TColor = clRed;
  5371. var
  5372. BM: TBitmap;
  5373. //--------------- local functions -------------------------------------------
  5374. procedure AddNodeImages(IL: TImageList);
  5375. var
  5376. I: Integer;
  5377. OffsetX,
  5378. OffsetY: Integer;
  5379. begin
  5380. // The offsets are used to center the node images in case the sizes differ.
  5381. OffsetX := (IL.Width - DarkCheckImages.Width) div 2;
  5382. OffsetY := (IL.Height - DarkCheckImages.Height) div 2;
  5383. for I := 21 to 24 do
  5384. begin
  5385. BM.Canvas.FillRect(Rect(0, 0, BM.Width, BM.Height));
  5386. if Flat then
  5387. FlatImages.Draw(BM.Canvas, OffsetX, OffsetY, I)
  5388. else
  5389. DarkCheckImages.Draw(BM.Canvas, OffsetX, OffsetY, I);
  5390. IL.AddMasked(BM, MaskColor);
  5391. end;
  5392. end;
  5393. //---------------------------------------------------------------------------
  5394. procedure AddSystemImage(IL: TImageList; Index: Integer);
  5395. var
  5396. ButtonState: Cardinal;
  5397. ButtonType: Cardinal;
  5398. begin
  5399. BM.Canvas.FillRect(Rect(0, 0, BM.Width, BM.Height));
  5400. if Index < 8 then
  5401. ButtonType := DFCS_BUTTONRADIO
  5402. else
  5403. ButtonType := DFCS_BUTTONCHECK;
  5404. if Index >= 16 then
  5405. ButtonType := ButtonType or DFCS_BUTTON3STATE;
  5406. case Index mod 4 of
  5407. 0:
  5408. ButtonState := 0;
  5409. 1:
  5410. ButtonState := DFCS_HOT;
  5411. 2:
  5412. ButtonState := DFCS_PUSHED;
  5413. else
  5414. ButtonState := DFCS_INACTIVE;
  5415. end;
  5416. if Index in [4..7, 12..19] then
  5417. ButtonState := ButtonState or DFCS_CHECKED;
  5418. if Flat then
  5419. ButtonState := ButtonState or DFCS_FLAT;
  5420. DrawFrameControl(BM.Canvas.Handle, Rect(1, 2, BM.Width - 2, BM.Height - 1), DFC_BUTTON, ButtonType or ButtonState);
  5421. IL.AddMasked(BM, MaskColor);
  5422. end;
  5423. //--------------- end local functions ---------------------------------------
  5424. var
  5425. I, Width, Height: Integer;
  5426. begin
  5427. Width := GetSystemMetrics(SM_CXMENUCHECK) + 3;
  5428. Height := GetSystemMetrics(SM_CYMENUCHECK) + 3;
  5429. IL := TImageList.CreateSize(Width, Height);
  5430. with IL do
  5431. Handle := ImageList_Create(Width, Height, Flags, 0, AllocBy);
  5432. IL.Masked := True;
  5433. IL.BkColor := clWhite;
  5434. // Create a temporary bitmap, which holds the intermediate images.
  5435. BM := TBitmap.Create;
  5436. try
  5437. // Make the bitmap the same size as the image list is to avoid problems when adding.
  5438. BM.Width := IL.Width;
  5439. BM.Height := IL.Height;
  5440. BM.Canvas.Brush.Color := MaskColor;
  5441. BM.Canvas.Brush.Style := bsSolid;
  5442. BM.Canvas.FillRect(Rect(0, 0, BM.Width, BM.Height));
  5443. IL.AddMasked(BM, MaskColor);
  5444. // Add the 20 system checkbox and radiobutton images.
  5445. for I := 0 to 19 do
  5446. AddSystemImage(IL, I);
  5447. // Add the 4 node images from the dark check set.
  5448. AddNodeImages(IL);
  5449. finally
  5450. BM.Free;
  5451. end;
  5452. end;
  5453. //----------------------------------------------------------------------------------------------------------------------
  5454. function HasMMX: Boolean;
  5455. // Helper method to determine whether the current processor supports MMX.
  5456. {$ifdef CPUX64}
  5457. begin
  5458. // We use SSE2 in the "MMX-functions"
  5459. Result := True;
  5460. end;
  5461. {$else}
  5462. asm
  5463. PUSH EBX
  5464. XOR EAX, EAX // Result := False
  5465. PUSHFD // determine if the processor supports the CPUID command
  5466. POP EDX
  5467. MOV ECX, EDX
  5468. XOR EDX, $200000
  5469. PUSH EDX
  5470. POPFD
  5471. PUSHFD
  5472. POP EDX
  5473. XOR ECX, EDX
  5474. JZ @1 // no CPUID support so we can't even get to the feature information
  5475. PUSH EDX
  5476. POPFD
  5477. MOV EAX, 1
  5478. DW $A20F // CPUID, EAX contains now version info and EDX feature information
  5479. MOV EBX, EAX // free EAX to get the result value
  5480. XOR EAX, EAX // Result := False
  5481. CMP EBX, $50
  5482. JB @1 // if processor family is < 5 then it is not a Pentium class processor
  5483. TEST EDX, $800000
  5484. JZ @1 // if the MMX bit is not set then we don't have MMX
  5485. INC EAX // Result := True
  5486. @1:
  5487. POP EBX
  5488. end;
  5489. {$endif CPUX64}
  5490. //----------------------------------------------------------------------------------------------------------------------
  5491. procedure PrtStretchDrawDIB(Canvas: TCanvas; DestRect: TRect; ABitmap: TBitmap);
  5492. // Stretch draw on to the new canvas.
  5493. var
  5494. Header,
  5495. Bits: Pointer;
  5496. HeaderSize,
  5497. BitsSize: Cardinal;
  5498. begin
  5499. GetDIBSizes(ABitmap.Handle, HeaderSize, BitsSize);
  5500. GetMem(Header, HeaderSize);
  5501. GetMem(Bits, BitsSize);
  5502. try
  5503. GetDIB(ABitmap.Handle, ABitmap.Palette, Header^, Bits^);
  5504. StretchDIBits(Canvas.Handle, DestRect.Left, DestRect.Top, DestRect.Right - DestRect.Left, DestRect.Bottom -
  5505. DestRect.Top, 0, 0, ABitmap.Width, ABitmap.Height, Bits, TBitmapInfo(Header^), DIB_RGB_COLORS, SRCCOPY);
  5506. finally
  5507. FreeMem(Header);
  5508. FreeMem(Bits);
  5509. end;
  5510. end;
  5511. //----------------------------------------------------------------------------------------------------------------------
  5512. procedure ClipCanvas(Canvas: TCanvas; ClipRect: TRect; VisibleRegion: HRGN = 0);
  5513. // Clip a given canvas to ClipRect while transforming the given rect to device coordinates.
  5514. var
  5515. ClipRegion: HRGN;
  5516. begin
  5517. // Regions expect their coordinates in device coordinates, hence we have to transform the region rectangle.
  5518. LPtoDP(Canvas.Handle, ClipRect, 2);
  5519. ClipRegion := CreateRectRgnIndirect(ClipRect);
  5520. if VisibleRegion <> 0 then
  5521. CombineRgn(ClipRegion, ClipRegion, VisibleRegion, RGN_AND);
  5522. SelectClipRgn(Canvas.Handle, ClipRegion);
  5523. DeleteObject(ClipRegion);
  5524. end;
  5525. //----------------------------------------------------------------------------------------------------------------------
  5526. procedure SetCanvasOrigin(Canvas: TCanvas; X, Y: Integer);
  5527. // Set the coordinate space origin of a given canvas.
  5528. var
  5529. P: TPoint;
  5530. begin
  5531. // Reset origin as otherwise we would accumulate the origin shifts when calling LPtoDP.
  5532. SetWindowOrgEx(Canvas.Handle, 0, 0, nil);
  5533. // The shifting is expected in physical points, so we have to transform them accordingly.
  5534. P := Point(X, Y);
  5535. LPtoDP(Canvas.Handle, P, 1);
  5536. // Do the shift.
  5537. SetWindowOrgEx(Canvas.Handle, P.X, P.Y, nil);
  5538. end;
  5539. //----------------------------------------------------------------------------------------------------------------------
  5540. procedure SetBrushOrigin(Canvas: TCanvas; X, Y: Integer);
  5541. // Set the brush origin of a given canvas.
  5542. var
  5543. P: TPoint;
  5544. begin
  5545. P := Point(X, Y);
  5546. LPtoDP(Canvas.Handle, P, 1);
  5547. SetBrushOrgEx(Canvas.Handle, P.X, P.Y, nil);
  5548. end;
  5549. //----------------------------------------------------------------------------------------------------------------------
  5550. procedure InitializeGlobalStructures;
  5551. // initialization of stuff global to the unit
  5552. var
  5553. Flags: Cardinal;
  5554. begin
  5555. Initialized := True;
  5556. // For the drag image a fast MMX blend routine is used. We have to make sure MMX is available.
  5557. MMXAvailable := HasMMX;
  5558. IsWinVistaOrAbove := (Win32MajorVersion >= 6);
  5559. // Initialize OLE subsystem for drag'n drop and clipboard operations.
  5560. NeedToUnitialize := not IsLibrary and Succeeded(OleInitialize(nil));
  5561. // Register the tree reference clipboard format. Others will be handled in InternalClipboarFormats.
  5562. CF_VTREFERENCE := RegisterClipboardFormat(CFSTR_VTREFERENCE);
  5563. // Load all internal image lists and convert their colors to current desktop color scheme.
  5564. // In order to use high color images we have to create the image list handle ourselves.
  5565. Flags := ILC_COLOR32 or ILC_MASK;
  5566. LightCheckImages := TImageList.Create(nil);
  5567. with LightCheckImages do
  5568. Handle := ImageList_Create(16, 16, Flags, 0, AllocBy);
  5569. ConvertImageList(LightCheckImages, 'VT_CHECK_LIGHT');
  5570. DarkCheckImages := TImageList.CreateSize(16, 16);
  5571. with DarkCheckImages do
  5572. Handle := ImageList_Create(16, 16, Flags, 0, AllocBy);
  5573. ConvertImageList(DarkCheckImages, 'VT_CHECK_DARK');
  5574. LightTickImages := TImageList.CreateSize(16, 16);
  5575. with LightTickImages do
  5576. Handle := ImageList_Create(16, 16, Flags, 0, AllocBy);
  5577. ConvertImageList(LightTickImages, 'VT_TICK_LIGHT');
  5578. DarkTickImages := TImageList.CreateSize(16, 16);
  5579. with DarkTickImages do
  5580. Handle := ImageList_Create(16, 16, Flags, 0, AllocBy);
  5581. ConvertImageList(DarkTickImages, 'VT_TICK_DARK');
  5582. FlatImages := TImageList.CreateSize(16, 16);
  5583. with FlatImages do
  5584. Handle := ImageList_Create(16, 16, Flags, 0, AllocBy);
  5585. ConvertImageList(FlatImages, 'VT_FLAT');
  5586. XPImages := TImageList.CreateSize(16, 16);
  5587. with XPImages do
  5588. Handle := ImageList_Create(16, 16, Flags, 0, AllocBy);
  5589. ConvertImageList(XPImages, 'VT_XP', False);
  5590. UtilityImages := TImageList.CreateSize(UtilityImageSize, UtilityImageSize);
  5591. with UtilityImages do
  5592. Handle := ImageList_Create(UtilityImageSize, UtilityImageSize, Flags, 0, AllocBy);
  5593. ConvertImageList(UtilityImages, 'VT_UTILITIES');
  5594. CreateSystemImageSet(SystemCheckImages, Flags, False);
  5595. CreateSystemImageSet(SystemFlatCheckImages, Flags, True);
  5596. // Delphi (at least version 6 and lower) does not provide a standard split cursor.
  5597. // Hence we have to load our own.
  5598. Screen.Cursors[crHeaderSplit] := LoadCursor(HInstance, 'VT_HEADERSPLIT');
  5599. Screen.Cursors[crVertSplit] := LoadCursor(HInstance, 'VT_VERTSPLIT');
  5600. // Clipboard format registration.
  5601. // Native clipboard format. Needs a new identifier and has an average priority to allow other formats to take over.
  5602. // This format is supposed to use the IStream storage format but unfortunately this does not work when
  5603. // OLEFlushClipboard is used. Hence it is disabled until somebody finds a solution.
  5604. CF_VIRTUALTREE := RegisterVTClipboardFormat(CFSTR_VIRTUALTREE, TBaseVirtualTree, 50, TYMED_HGLOBAL {or TYMED_ISTREAM});
  5605. // Specialized string tree formats.
  5606. CF_HTML := RegisterVTClipboardFormat(CFSTR_HTML, TCustomVirtualStringTree, 80);
  5607. CF_VRTFNOOBJS := RegisterVTClipboardFormat(CFSTR_RTFNOOBJS, TCustomVirtualStringTree, 84);
  5608. CF_VRTF := RegisterVTClipboardFormat(CFSTR_RTF, TCustomVirtualStringTree, 85);
  5609. CF_CSV := RegisterVTClipboardFormat(CFSTR_CSV, TCustomVirtualStringTree, 90);
  5610. // Predefined clipboard formats. Just add them to the internal list.
  5611. RegisterVTClipboardFormat(CF_TEXT, TCustomVirtualStringTree, 100);
  5612. RegisterVTClipboardFormat(CF_UNICODETEXT, TCustomVirtualStringTree, 95);
  5613. end;
  5614. //----------------------------------------------------------------------------------------------------------------------
  5615. procedure FinalizeGlobalStructures;
  5616. var
  5617. HintWasEnabled: Boolean;
  5618. begin
  5619. LightCheckImages.Free;
  5620. LightCheckImages := nil;
  5621. DarkCheckImages.Free;
  5622. DarkCheckImages := nil;
  5623. LightTickImages.Free;
  5624. LightTickImages := nil;
  5625. DarkTickImages.Free;
  5626. DarkTickImages := nil;
  5627. FlatImages.Free;
  5628. FlatImages := nil;
  5629. XPImages.Free;
  5630. XPImages := nil;
  5631. UtilityImages.Free;
  5632. UtilityImages := nil;
  5633. SystemCheckImages.Free;
  5634. SystemCheckImages := nil;
  5635. SystemFlatCheckImages.Free;
  5636. SystemFlatCheckImages := nil;
  5637. if NeedToUnitialize then
  5638. OleUninitialize;
  5639. // If VT is used in a package and its special hint window was used then the last instance of this
  5640. // window is not freed correctly (bug in the VCL). We explicitely tell the application to free it
  5641. // otherwise an AV is raised due to access to an invalid memory area.
  5642. if ModuleIsPackage then
  5643. begin
  5644. HintWasEnabled := Application.ShowHint;
  5645. Application.ShowHint := False;
  5646. if HintWasEnabled then
  5647. Application.ShowHint := True;
  5648. end;
  5649. end;
  5650. //----------------- TCriticalSection -----------------------------------------------------------------------------------
  5651. constructor TCriticalSection.Create;
  5652. begin
  5653. inherited Create;
  5654. InitializeCriticalSection(FSection);
  5655. end;
  5656. //----------------------------------------------------------------------------------------------------------------------
  5657. destructor TCriticalSection.Destroy;
  5658. begin
  5659. DeleteCriticalSection(FSection);
  5660. inherited Destroy;
  5661. end;
  5662. //----------------------------------------------------------------------------------------------------------------------
  5663. procedure TCriticalSection.Enter;
  5664. begin
  5665. EnterCriticalSection(FSection);
  5666. end;
  5667. //----------------------------------------------------------------------------------------------------------------------
  5668. procedure TCriticalSection.Leave;
  5669. begin
  5670. LeaveCriticalSection(FSection);
  5671. end;
  5672. //----------------- TWorkerThread --------------------------------------------------------------------------------------
  5673. procedure AddThreadReference;
  5674. begin
  5675. if not Assigned(WorkerThread) then
  5676. begin
  5677. // Create an event used to trigger our worker thread when something is to do.
  5678. WorkEvent := CreateEvent(nil, False, False, nil);
  5679. if WorkEvent = 0 then
  5680. RaiseLastOSError;
  5681. // Create worker thread, initialize it and send it to its wait loop.
  5682. WorkerThread := TWorkerThread.Create(False);
  5683. end;
  5684. Inc(WorkerThread.FRefCount);
  5685. end;
  5686. //----------------------------------------------------------------------------------------------------------------------
  5687. procedure ReleaseThreadReference(Tree: TBaseVirtualTree);
  5688. begin
  5689. if Assigned(WorkerThread) then
  5690. begin
  5691. Dec(WorkerThread.FRefCount);
  5692. // Make sure there is no reference remaining to the releasing tree.
  5693. Tree.InterruptValidation;
  5694. if WorkerThread.FRefCount = 0 then
  5695. begin
  5696. with WorkerThread do
  5697. begin
  5698. Terminate;
  5699. SetEvent(WorkEvent);
  5700. end;
  5701. FreeAndNil(WorkerThread);
  5702. CloseHandle(WorkEvent);
  5703. end;
  5704. end;
  5705. end;
  5706. //----------------------------------------------------------------------------------------------------------------------
  5707. constructor TWorkerThread.Create(CreateSuspended: Boolean);
  5708. begin
  5709. inherited Create(CreateSuspended);
  5710. FWaiterList := TThreadList.Create;
  5711. end;
  5712. //----------------------------------------------------------------------------------------------------------------------
  5713. destructor TWorkerThread.Destroy;
  5714. begin
  5715. // First let the ancestor stop the thread before freeing our resources.
  5716. inherited;
  5717. FWaiterList.Free;
  5718. end;
  5719. //----------------------------------------------------------------------------------------------------------------------
  5720. procedure TWorkerThread.CancelValidation(Tree: TBaseVirtualTree);
  5721. var
  5722. Msg: TMsg;
  5723. begin
  5724. // Wait for any references to this tree to be released.
  5725. // Pump WM_CHANGESTATE messages so the thread doesn't block on SendMessage calls.
  5726. while FCurrentTree = Tree do
  5727. begin
  5728. if Tree.HandleAllocated and PeekMessage(Msg, Tree.Handle, WM_CHANGESTATE, WM_CHANGESTATE, PM_REMOVE) then
  5729. begin
  5730. TranslateMessage(Msg);
  5731. DispatchMessage(Msg);
  5732. Continue;
  5733. end;
  5734. if (toVariableNodeHeight in Tree.TreeOptions.MiscOptions) then
  5735. CheckSynchronize(); // We need to call CheckSynchronize here because we are using TThread.Synchronize in TBaseVirtualTree.MeasureItemHeight()
  5736. end;
  5737. end;
  5738. //----------------------------------------------------------------------------------------------------------------------
  5739. procedure TWorkerThread.Execute;
  5740. // Does some background tasks, like validating tree caches.
  5741. var
  5742. EnterStates,
  5743. LeaveStates: TChangeStates;
  5744. lCurrentTree: TBaseVirtualTree;
  5745. begin
  5746. {$if CompilerVersion >= 21} TThread.NameThreadForDebugging('VirtualTrees.TWorkerThread');{$ifend}
  5747. while not Terminated do
  5748. begin
  5749. WaitForSingleObject(WorkEvent, INFINITE);
  5750. if not Terminated then
  5751. begin
  5752. // Get the next waiting tree.
  5753. with FWaiterList.LockList do
  5754. try
  5755. if Count > 0 then
  5756. begin
  5757. FCurrentTree := Items[0];
  5758. // Remove this tree from waiter list.
  5759. Delete(0);
  5760. // If there is yet another tree to work on then set the work event to keep looping.
  5761. if Count > 0 then
  5762. SetEvent(WorkEvent);
  5763. end
  5764. else
  5765. FCurrentTree := nil;
  5766. finally
  5767. FWaiterList.UnlockList;
  5768. end;
  5769. // Something to do?
  5770. if Assigned(FCurrentTree) then
  5771. begin
  5772. try
  5773. FCurrentTree.ChangeTreeStatesAsync([csValidating], [csUseCache, csValidationNeeded]);
  5774. EnterStates := [];
  5775. if not (tsStopValidation in FCurrentTree.FStates) and FCurrentTree.DoValidateCache then
  5776. EnterStates := [csUseCache];
  5777. finally
  5778. LeaveStates := [csValidating, csStopValidation];
  5779. FCurrentTree.ChangeTreeStatesAsync(EnterStates, LeaveStates);
  5780. lCurrentTree := FCurrentTree; // Save reference in a local variable for later use
  5781. FCurrentTree := nil; //Clear variable to prevent deadlock in CancelValidation. See #434
  5782. {$if CompilerVersion < 20}Synchronize{$else}Queue{$ifend}(lCurrentTree.UpdateEditBounds);
  5783. end;
  5784. end;
  5785. end;
  5786. end;
  5787. end;
  5788. //----------------------------------------------------------------------------------------------------------------------
  5789. procedure TWorkerThread.AddTree(Tree: TBaseVirtualTree);
  5790. begin
  5791. Assert(Assigned(Tree), 'Tree must not be nil.');
  5792. // Remove validation stop flag, just in case it is still set.
  5793. Tree.DoStateChange([], [tsStopValidation]);
  5794. with FWaiterList.LockList do
  5795. try
  5796. if IndexOf(Tree) = -1 then
  5797. Add(Tree);
  5798. finally
  5799. FWaiterList.UnlockList;
  5800. end;
  5801. end;
  5802. //----------------------------------------------------------------------------------------------------------------------
  5803. procedure TWorkerThread.RemoveTree(Tree: TBaseVirtualTree);
  5804. begin
  5805. Assert(Assigned(Tree), 'Tree must not be nil.');
  5806. with FWaiterList.LockList do
  5807. try
  5808. Remove(Tree);
  5809. finally
  5810. FWaiterList.UnlockList; // Seen several AVs in this line, was called from TWorkerThrea.Destroy. Joachim Marder.
  5811. end;
  5812. CancelValidation(Tree);
  5813. end;
  5814. //----------------- TBufferedAnsiString ------------------------------------------------------------------------------------
  5815. const
  5816. AllocIncrement = 2 shl 11; // Must be a power of 2.
  5817. destructor TBufferedAnsiString.Destroy;
  5818. begin
  5819. FreeMem(FStart);
  5820. inherited;
  5821. end;
  5822. //----------------------------------------------------------------------------------------------------------------------
  5823. function TBufferedAnsiString.GetAsString: RawByteString;
  5824. begin
  5825. SetString(Result, FStart, FPosition - FStart);
  5826. end;
  5827. //----------------------------------------------------------------------------------------------------------------------
  5828. procedure TBufferedAnsiString.Add(const S: RawByteString);
  5829. var
  5830. NewLen,
  5831. LastOffset,
  5832. Len: NativeInt;
  5833. begin
  5834. Len := Length(S);
  5835. // Make room for the new string.
  5836. if FEnd - FPosition <= Len then
  5837. begin
  5838. // Round up NewLen so it is always a multiple of AllocIncrement.
  5839. NewLen := FEnd - FStart + (Len + AllocIncrement - 1) and not (AllocIncrement - 1);
  5840. // Keep last offset to restore it correctly in the case that FStart gets a new memory block assigned.
  5841. LastOffset := FPosition - FStart;
  5842. ReallocMem(FStart, NewLen);
  5843. FPosition := FStart + LastOffset;
  5844. FEnd := FStart + NewLen;
  5845. end;
  5846. Move(PAnsiChar(S)^, FPosition^, Len);
  5847. Inc(FPosition, Len);
  5848. end;
  5849. //----------------------------------------------------------------------------------------------------------------------
  5850. procedure TBufferedAnsiString.AddNewLine;
  5851. var
  5852. NewLen,
  5853. LastOffset: NativeInt;
  5854. begin
  5855. // Make room for the CR/LF characters.
  5856. if FEnd - FPosition <= 2 then
  5857. begin
  5858. // Round up NewLen so it is always a multiple of AllocIncrement.
  5859. NewLen := FEnd - FStart + (2 + AllocIncrement - 1) and not (AllocIncrement - 1);
  5860. // Keep last offset to restore it correctly in the case that FStart gets a new memory block assigned.
  5861. LastOffset := FPosition - FStart;
  5862. ReallocMem(FStart, NewLen);
  5863. FPosition := FStart + LastOffset;
  5864. FEnd := FStart + NewLen;
  5865. end;
  5866. FPosition^ := #13;
  5867. Inc(FPosition);
  5868. FPosition^ := #10;
  5869. Inc(FPosition);
  5870. end;
  5871. //----------------- TWideBufferedString --------------------------------------------------------------------------------
  5872. destructor TWideBufferedString.Destroy;
  5873. begin
  5874. FreeMem(FStart);
  5875. inherited;
  5876. end;
  5877. //----------------------------------------------------------------------------------------------------------------------
  5878. function TWideBufferedString.GetAsString: UnicodeString;
  5879. begin
  5880. SetString(Result, FStart, FPosition - FStart);
  5881. end;
  5882. //----------------------------------------------------------------------------------------------------------------------
  5883. procedure TWideBufferedString.Add(const S: UnicodeString);
  5884. var
  5885. NewLen,
  5886. LastOffset,
  5887. Len: Integer;
  5888. begin
  5889. Len := Length(S);
  5890. // Make room for the new string.
  5891. if FEnd - FPosition <= Len then
  5892. begin
  5893. // Round up NewLen so it is always a multiple of AllocIncrement.
  5894. NewLen := FEnd - FStart + (Len + AllocIncrement - 1) and not (AllocIncrement - 1);
  5895. // Keep last offset to restore it correctly in the case that FStart gets a new memory block assigned.
  5896. LastOffset := FPosition - FStart;
  5897. ReallocMem(FStart, 2 * NewLen);
  5898. FPosition := FStart + LastOffset;
  5899. FEnd := FStart + NewLen;
  5900. end;
  5901. Move(PWideChar(S)^, FPosition^, 2 * Len);
  5902. Inc(FPosition, Len);
  5903. end;
  5904. //----------------------------------------------------------------------------------------------------------------------
  5905. procedure TWideBufferedString.AddNewLine;
  5906. var
  5907. NewLen,
  5908. LastOffset: Integer;
  5909. begin
  5910. // Make room for the CR/LF characters.
  5911. if FEnd - FPosition <= 4 then
  5912. begin
  5913. // Round up NewLen so it is always a multiple of AllocIncrement.
  5914. NewLen := FEnd - FStart + (2 + AllocIncrement - 1) and not (AllocIncrement - 1);
  5915. // Keep last offset to restore it correctly in the case that FStart gets a new memory block assigned.
  5916. LastOffset := FPosition - FStart;
  5917. ReallocMem(FStart, 2 * NewLen);
  5918. FPosition := FStart + LastOffset;
  5919. FEnd := FStart + NewLen;
  5920. end;
  5921. FPosition^ := #13;
  5922. Inc(FPosition);
  5923. FPosition^ := #10;
  5924. Inc(FPosition);
  5925. end;
  5926. //----------------- TCustomVirtualTreeOptions --------------------------------------------------------------------------
  5927. constructor TCustomVirtualTreeOptions.Create(AOwner: TBaseVirtualTree);
  5928. begin
  5929. FOwner := AOwner;
  5930. FPaintOptions := DefaultPaintOptions;
  5931. FAnimationOptions := DefaultAnimationOptions;
  5932. FAutoOptions := DefaultAutoOptions;
  5933. FSelectionOptions := DefaultSelectionOptions;
  5934. FMiscOptions := DefaultMiscOptions;
  5935. end;
  5936. //----------------------------------------------------------------------------------------------------------------------
  5937. procedure TCustomVirtualTreeOptions.SetAnimationOptions(const Value: TVTAnimationOptions);
  5938. begin
  5939. FAnimationOptions := Value;
  5940. end;
  5941. //----------------------------------------------------------------------------------------------------------------------
  5942. procedure TCustomVirtualTreeOptions.SetAutoOptions(const Value: TVTAutoOptions);
  5943. var
  5944. ChangedOptions: TVTAutoOptions;
  5945. begin
  5946. if FAutoOptions <> Value then
  5947. begin
  5948. // Exclusive ORing to get all entries wich are in either set but not in both.
  5949. ChangedOptions := FAutoOptions + Value - (FAutoOptions * Value);
  5950. FAutoOptions := Value;
  5951. with FOwner do
  5952. if (toAutoSpanColumns in ChangedOptions) and not (csLoading in ComponentState) and HandleAllocated then
  5953. Invalidate;
  5954. end;
  5955. end;
  5956. //----------------------------------------------------------------------------------------------------------------------
  5957. procedure TCustomVirtualTreeOptions.SetMiscOptions(const Value: TVTMiscOptions);
  5958. var
  5959. ToBeSet,
  5960. ToBeCleared: TVTMiscOptions;
  5961. begin
  5962. if FMiscOptions <> Value then
  5963. begin
  5964. ToBeSet := Value - FMiscOptions;
  5965. ToBeCleared := FMiscOptions - Value;
  5966. FMiscOptions := Value;
  5967. with FOwner do
  5968. if not (csLoading in ComponentState) and HandleAllocated then
  5969. begin
  5970. if toCheckSupport in ToBeSet + ToBeCleared then
  5971. Invalidate;
  5972. if not (csDesigning in ComponentState) then
  5973. begin
  5974. if toFullRepaintOnResize in ToBeSet + ToBeCleared then
  5975. RecreateWnd;
  5976. if toAcceptOLEDrop in ToBeSet then
  5977. RegisterDragDrop(Handle, DragManager as IDropTarget);
  5978. if toAcceptOLEDrop in ToBeCleared then
  5979. RevokeDragDrop(Handle);
  5980. end;
  5981. end;
  5982. end;
  5983. end;
  5984. //----------------------------------------------------------------------------------------------------------------------
  5985. procedure TCustomVirtualTreeOptions.SetPaintOptions(const Value: TVTPaintOptions);
  5986. var
  5987. ToBeSet,
  5988. ToBeCleared: TVTPaintOptions;
  5989. Run: PVirtualNode;
  5990. HandleWasAllocated: Boolean;
  5991. begin
  5992. if FPaintOptions <> Value then
  5993. begin
  5994. ToBeSet := Value - FPaintOptions;
  5995. ToBeCleared := FPaintOptions - Value;
  5996. FPaintOptions := Value;
  5997. if (toFixedIndent in ToBeSet) then
  5998. begin
  5999. // Fixes issue #388
  6000. Include(FPaintOptions, toShowRoot);
  6001. Include(ToBeSet, toShowRoot);
  6002. end;//if
  6003. with FOwner do
  6004. begin
  6005. HandleWasAllocated := HandleAllocated;
  6006. if not (csLoading in ComponentState) and (toShowFilteredNodes in ToBeSet + ToBeCleared) then
  6007. begin
  6008. if HandleWasAllocated then
  6009. BeginUpdate;
  6010. InterruptValidation;
  6011. Run := GetFirstNoInit;
  6012. while Assigned(Run) do
  6013. begin
  6014. if (vsFiltered in Run.States) then
  6015. begin
  6016. if FullyVisible[Run] then
  6017. begin
  6018. if toShowFilteredNodes in ToBeSet then
  6019. Inc(FVisibleCount)
  6020. else
  6021. Dec(FVisibleCount);
  6022. end;
  6023. if toShowFilteredNodes in ToBeSet then
  6024. AdjustTotalHeight(Run, Run.NodeHeight, True)
  6025. else
  6026. AdjustTotalHeight(Run, -Run.NodeHeight, True);
  6027. end;
  6028. Run := GetNextNoInit(Run);
  6029. end;
  6030. if HandleWasAllocated then
  6031. EndUpdate;
  6032. end;
  6033. if HandleAllocated then
  6034. begin
  6035. if IsWinVistaOrAbove and ((tsUseThemes in FStates) or
  6036. ((toThemeAware in ToBeSet) and StyleServices.Enabled)) and
  6037. (toUseExplorerTheme in (ToBeSet + ToBeCleared)) and not VclStyleEnabled then
  6038. if (toUseExplorerTheme in ToBeSet) then
  6039. begin
  6040. SetWindowTheme('explorer');
  6041. DoStateChange([tsUseExplorerTheme]);
  6042. end
  6043. else
  6044. if toUseExplorerTheme in ToBeCleared then
  6045. begin
  6046. SetWindowTheme('');
  6047. DoStateChange([], [tsUseExplorerTheme]);
  6048. end;
  6049. if not (csLoading in ComponentState) then
  6050. begin
  6051. if ((toThemeAware in ToBeSet + ToBeCleared) or (toUseExplorerTheme in ToBeSet + ToBeCleared) or VclStyleEnabled) then
  6052. begin
  6053. if ((toThemeAware in ToBeSet) and StyleServices.Enabled) or VclStyleEnabled then
  6054. DoStateChange([tsUseThemes])
  6055. else
  6056. if (toThemeAware in ToBeCleared) then
  6057. DoStateChange([], [tsUseThemes]);
  6058. PrepareBitmaps(True, False);
  6059. RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_VALIDATE or RDW_FRAME);
  6060. end;
  6061. if toChildrenAbove in ToBeSet + ToBeCleared then
  6062. begin
  6063. InvalidateCache;
  6064. if FUpdateCount = 0 then
  6065. begin
  6066. ValidateCache;
  6067. Invalidate;
  6068. end;
  6069. end;
  6070. Invalidate;
  6071. end;
  6072. end;
  6073. end;
  6074. end;
  6075. end;
  6076. //----------------------------------------------------------------------------------------------------------------------
  6077. procedure TCustomVirtualTreeOptions.SetSelectionOptions(const Value: TVTSelectionOptions);
  6078. var
  6079. ToBeSet,
  6080. ToBeCleared: TVTSelectionOptions;
  6081. begin
  6082. if FSelectionOptions <> Value then
  6083. begin
  6084. ToBeSet := Value - FSelectionOptions;
  6085. ToBeCleared := FSelectionOptions - Value;
  6086. FSelectionOptions := Value;
  6087. with FOwner do
  6088. begin
  6089. if (toMultiSelect in (ToBeCleared + ToBeSet)) or
  6090. ([toLevelSelectConstraint, toSiblingSelectConstraint] * ToBeSet <> []) then
  6091. ClearSelection;
  6092. if (toExtendedFocus in ToBeCleared) and (FFocusedColumn > 0) and HandleAllocated then
  6093. begin
  6094. FFocusedColumn := FHeader.MainColumn;
  6095. Invalidate;
  6096. end;
  6097. if not (toExtendedFocus in FSelectionOptions) then
  6098. FFocusedColumn := FHeader.MainColumn;
  6099. end;
  6100. end;
  6101. end;
  6102. //----------------------------------------------------------------------------------------------------------------------
  6103. procedure TCustomVirtualTreeOptions.AssignTo(Dest: TPersistent);
  6104. begin
  6105. if Dest is TCustomVirtualTreeOptions then
  6106. begin
  6107. with Dest as TCustomVirtualTreeOptions do
  6108. begin
  6109. PaintOptions := Self.PaintOptions;
  6110. AnimationOptions := Self.AnimationOptions;
  6111. AutoOptions := Self.AutoOptions;
  6112. SelectionOptions := Self.SelectionOptions;
  6113. MiscOptions := Self.MiscOptions;
  6114. end;
  6115. end
  6116. else
  6117. inherited;
  6118. end;
  6119. //----------------------------------------------------------------------------------------------------------------------
  6120. // OLE drag and drop support classes
  6121. // This is quite heavy stuff (compared with the VCL implementation) but is much better suited to fit the needs
  6122. // of DD'ing various kinds of virtual data and works also between applications.
  6123. //----------------- TEnumFormatEtc -------------------------------------------------------------------------------------
  6124. constructor TEnumFormatEtc.Create(Tree: TBaseVirtualTree; AFormatEtcArray: TFormatEtcArray);
  6125. var
  6126. I: Integer;
  6127. begin
  6128. inherited Create;
  6129. FTree := Tree;
  6130. // Make a local copy of the format data.
  6131. SetLength(FFormatEtcArray, Length(AFormatEtcArray));
  6132. for I := 0 to High(AFormatEtcArray) do
  6133. FFormatEtcArray[I] := AFormatEtcArray[I];
  6134. end;
  6135. //----------------------------------------------------------------------------------------------------------------------
  6136. function TEnumFormatEtc.Clone(out Enum: IEnumFormatEtc): HResult;
  6137. var
  6138. AClone: TEnumFormatEtc;
  6139. begin
  6140. Result := S_OK;
  6141. try
  6142. AClone := TEnumFormatEtc.Create(nil, FFormatEtcArray);
  6143. AClone.FCurrentIndex := FCurrentIndex;
  6144. Enum := AClone as IEnumFormatEtc;
  6145. except
  6146. Result := E_FAIL;
  6147. end;
  6148. end;
  6149. //----------------------------------------------------------------------------------------------------------------------
  6150. function TEnumFormatEtc.Next(celt: Integer; out elt; pceltFetched: PLongint): HResult;
  6151. var
  6152. CopyCount: Integer;
  6153. begin
  6154. Result := S_FALSE;
  6155. CopyCount := Length(FFormatEtcArray) - FCurrentIndex;
  6156. if celt < CopyCount then
  6157. CopyCount := celt;
  6158. if CopyCount > 0 then
  6159. begin
  6160. Move(FFormatEtcArray[FCurrentIndex], elt, CopyCount * SizeOf(TFormatEtc));
  6161. Inc(FCurrentIndex, CopyCount);
  6162. Result := S_OK;
  6163. end;
  6164. if Assigned(pceltFetched) then
  6165. pceltFetched^ := CopyCount;
  6166. end;
  6167. //----------------------------------------------------------------------------------------------------------------------
  6168. function TEnumFormatEtc.Reset: HResult;
  6169. begin
  6170. FCurrentIndex := 0;
  6171. Result := S_OK;
  6172. end;
  6173. //----------------------------------------------------------------------------------------------------------------------
  6174. function TEnumFormatEtc.Skip(celt: Integer): HResult;
  6175. begin
  6176. if FCurrentIndex + celt < High(FFormatEtcArray) then
  6177. begin
  6178. Inc(FCurrentIndex, celt);
  6179. Result := S_Ok;
  6180. end
  6181. else
  6182. Result := S_FALSE;
  6183. end;
  6184. //----------------- TVTDataObject --------------------------------------------------------------------------------------
  6185. constructor TVTDataObject.Create(AOwner: TBaseVirtualTree; ForClipboard: Boolean);
  6186. begin
  6187. inherited Create;
  6188. FOwner := AOwner;
  6189. FForClipboard := ForClipboard;
  6190. FOwner.GetNativeClipboardFormats(FFormatEtcArray);
  6191. end;
  6192. //----------------------------------------------------------------------------------------------------------------------
  6193. destructor TVTDataObject.Destroy;
  6194. var
  6195. I: Integer;
  6196. StgMedium: PStgMedium;
  6197. begin
  6198. // Cancel a pending clipboard operation if this data object was created for the clipboard and
  6199. // is freed because something else is placed there.
  6200. if FForClipboard and not (tsClipboardFlushing in FOwner.FStates) then
  6201. FOwner.CancelCutOrCopy;
  6202. // Release any internal clipboard formats
  6203. for I := 0 to High(FormatEtcArray) do
  6204. begin
  6205. StgMedium := FindInternalStgMedium(FormatEtcArray[I].cfFormat);
  6206. if Assigned(StgMedium) then
  6207. ReleaseStgMedium(StgMedium^);
  6208. end;
  6209. FormatEtcArray := nil;
  6210. inherited;
  6211. end;
  6212. //----------------------------------------------------------------------------------------------------------------------
  6213. function TVTDataObject.CanonicalIUnknown(TestUnknown: IUnknown): IUnknown;
  6214. // Uses COM object identity: An explicit call to the IUnknown::QueryInterface method, requesting the IUnknown
  6215. // interface, will always return the same pointer.
  6216. begin
  6217. if Assigned(TestUnknown) then
  6218. begin
  6219. if TestUnknown.QueryInterface(IUnknown, Result) = 0 then
  6220. Result._Release // Don't actually need it just need the pointer value
  6221. else
  6222. Result := TestUnknown;
  6223. end
  6224. else
  6225. Result := TestUnknown;
  6226. end;
  6227. //----------------------------------------------------------------------------------------------------------------------
  6228. function TVTDataObject.EqualFormatEtc(FormatEtc1, FormatEtc2: TFormatEtc): Boolean;
  6229. begin
  6230. Result := (FormatEtc1.cfFormat = FormatEtc2.cfFormat) and (FormatEtc1.ptd = FormatEtc2.ptd) and
  6231. (FormatEtc1.dwAspect = FormatEtc2.dwAspect) and (FormatEtc1.lindex = FormatEtc2.lindex) and
  6232. (FormatEtc1.tymed and FormatEtc2.tymed <> 0);
  6233. end;
  6234. //----------------------------------------------------------------------------------------------------------------------
  6235. function TVTDataObject.FindFormatEtc(TestFormatEtc: TFormatEtc; const FormatEtcArray: TFormatEtcArray): integer;
  6236. var
  6237. I: integer;
  6238. begin
  6239. Result := -1;
  6240. for I := 0 to High(FormatEtcArray) do
  6241. begin
  6242. if EqualFormatEtc(TestFormatEtc, FormatEtcArray[I]) then
  6243. begin
  6244. Result := I;
  6245. Break;
  6246. end;
  6247. end;
  6248. end;
  6249. //----------------------------------------------------------------------------------------------------------------------
  6250. function TVTDataObject.FindInternalStgMedium(Format: TClipFormat): PStgMedium;
  6251. var
  6252. I: integer;
  6253. begin
  6254. Result := nil;
  6255. for I := 0 to High(InternalStgMediumArray) do
  6256. begin
  6257. if Format = InternalStgMediumArray[I].Format then
  6258. begin
  6259. Result := @InternalStgMediumArray[I].Medium;
  6260. Break;
  6261. end;
  6262. end;
  6263. end;
  6264. //----------------------------------------------------------------------------------------------------------------------
  6265. function TVTDataObject.HGlobalClone(HGlobal: THandle): THandle;
  6266. // Returns a global memory block that is a copy of the passed memory block.
  6267. var
  6268. Size: Cardinal;
  6269. Data,
  6270. NewData: PByte;
  6271. begin
  6272. Size := GlobalSize(HGlobal);
  6273. Result := GlobalAlloc(GPTR, Size);
  6274. Data := GlobalLock(hGlobal);
  6275. try
  6276. NewData := GlobalLock(Result);
  6277. try
  6278. Move(Data^, NewData^, Size);
  6279. finally
  6280. GlobalUnLock(Result);
  6281. end;
  6282. finally
  6283. GlobalUnLock(hGlobal);
  6284. end;
  6285. end;
  6286. //----------------------------------------------------------------------------------------------------------------------
  6287. function TVTDataObject.RenderInternalOLEData(const FormatEtcIn: TFormatEtc; var Medium: TStgMedium;
  6288. var OLEResult: HResult): Boolean;
  6289. // Tries to render one of the formats which have been stored via the SetData method.
  6290. // Since this data is already there it is just copied or its reference count is increased (depending on storage medium).
  6291. var
  6292. InternalMedium: PStgMedium;
  6293. begin
  6294. Result := True;
  6295. InternalMedium := FindInternalStgMedium(FormatEtcIn.cfFormat);
  6296. if Assigned(InternalMedium) then
  6297. OLEResult := StgMediumIncRef(InternalMedium^, Medium, False, Self as IDataObject)
  6298. else
  6299. Result := False;
  6300. end;
  6301. //----------------------------------------------------------------------------------------------------------------------
  6302. function TVTDataObject.StgMediumIncRef(const InStgMedium: TStgMedium; var OutStgMedium: TStgMedium;
  6303. CopyInMedium: Boolean; DataObject: IDataObject): HRESULT;
  6304. // InStgMedium is the data that is requested, OutStgMedium is the data that we are to return either a copy of or
  6305. // increase the IDataObject's reference and send ourselves back as the data (unkForRelease). The InStgMedium is usually
  6306. // the result of a call to find a particular FormatEtc that has been stored locally through a call to SetData.
  6307. // If CopyInMedium is not true we already have a local copy of the data when the SetData function was called (during
  6308. // that call the CopyInMedium must be true). Then as the caller asks for the data through GetData we do not have to make
  6309. // copy of the data for the caller only to have them destroy it then need us to copy it again if necessary.
  6310. // This way we increase the reference count to ourselves and pass the STGMEDIUM structure initially stored in SetData.
  6311. // This way when the caller frees the structure it sees the unkForRelease is not nil and calls Release on the object
  6312. // instead of destroying the actual data.
  6313. var
  6314. Len: Integer;
  6315. begin
  6316. Result := S_OK;
  6317. // Simply copy all fields to start with.
  6318. OutStgMedium := InStgMedium;
  6319. // The data handled here always results from a call of SetData we got. This ensures only one storage format
  6320. // is indicated and hence the case statement below is safe (IDataObject.GetData can optionally use several
  6321. // storage formats).
  6322. case InStgMedium.tymed of
  6323. TYMED_HGLOBAL:
  6324. begin
  6325. if CopyInMedium then
  6326. begin
  6327. // Generate a unique copy of the data passed
  6328. OutStgMedium.hGlobal := HGlobalClone(InStgMedium.hGlobal);
  6329. if OutStgMedium.hGlobal = 0 then
  6330. Result := E_OUTOFMEMORY;
  6331. end
  6332. else
  6333. // Don't generate a copy just use ourselves and the copy previously saved.
  6334. OutStgMedium.unkForRelease := Pointer(DataObject); // Does not increase RefCount.
  6335. end;
  6336. TYMED_FILE:
  6337. begin
  6338. Len := lstrLenW(InStgMedium.lpszFileName) + 1; // Don't forget the terminating null character.
  6339. OutStgMedium.lpszFileName := CoTaskMemAlloc(2 * Len);
  6340. Move(InStgMedium.lpszFileName^, OutStgMedium.lpszFileName^, 2 * Len);
  6341. end;
  6342. TYMED_ISTREAM:
  6343. IUnknown(OutStgMedium.stm)._AddRef;
  6344. TYMED_ISTORAGE:
  6345. IUnknown(OutStgMedium.stg)._AddRef;
  6346. TYMED_GDI:
  6347. if not CopyInMedium then
  6348. // Don't generate a copy just use ourselves and the previously saved data.
  6349. OutStgMedium.unkForRelease := Pointer(DataObject) // Does not increase RefCount.
  6350. else
  6351. Result := DV_E_TYMED; // Don't know how to copy GDI objects right now.
  6352. TYMED_MFPICT:
  6353. if not CopyInMedium then
  6354. // Don't generate a copy just use ourselves and the previously saved data.
  6355. OutStgMedium.unkForRelease := Pointer(DataObject) // Does not increase RefCount.
  6356. else
  6357. Result := DV_E_TYMED; // Don't know how to copy MetaFile objects right now.
  6358. TYMED_ENHMF:
  6359. if not CopyInMedium then
  6360. // Don't generate a copy just use ourselves and the previously saved data.
  6361. OutStgMedium.unkForRelease := Pointer(DataObject) // Does not increase RefCount.
  6362. else
  6363. Result := DV_E_TYMED; // Don't know how to copy enhanced metafiles objects right now.
  6364. else
  6365. Result := DV_E_TYMED;
  6366. end;
  6367. if (Result = S_OK) and Assigned(OutStgMedium.unkForRelease) then
  6368. IUnknown(OutStgMedium.unkForRelease)._AddRef;
  6369. end;
  6370. //----------------------------------------------------------------------------------------------------------------------
  6371. function TVTDataObject.DAdvise(const FormatEtc: TFormatEtc; advf: Integer; const advSink: IAdviseSink;
  6372. out dwConnection: Integer): HResult;
  6373. // Advise sink management is greatly simplified by the IDataAdviseHolder interface.
  6374. // We use this interface and forward all concerning calls to it.
  6375. begin
  6376. Result := S_OK;
  6377. if FAdviseHolder = nil then
  6378. Result := CreateDataAdviseHolder(FAdviseHolder);
  6379. if Result = S_OK then
  6380. Result := FAdviseHolder.Advise(Self as IDataObject, FormatEtc, advf, advSink, dwConnection);
  6381. end;
  6382. //----------------------------------------------------------------------------------------------------------------------
  6383. function TVTDataObject.DUnadvise(dwConnection: Integer): HResult;
  6384. begin
  6385. if FAdviseHolder = nil then
  6386. Result := E_NOTIMPL
  6387. else
  6388. Result := FAdviseHolder.Unadvise(dwConnection);
  6389. end;
  6390. //----------------------------------------------------------------------------------------------------------------------
  6391. function TVTDataObject.EnumDAdvise(out enumAdvise: IEnumStatData): HResult;
  6392. begin
  6393. if FAdviseHolder = nil then
  6394. Result := OLE_E_ADVISENOTSUPPORTED
  6395. else
  6396. Result := FAdviseHolder.EnumAdvise(enumAdvise);
  6397. end;
  6398. //----------------------------------------------------------------------------------------------------------------------
  6399. function TVTDataObject.EnumFormatEtc(Direction: Integer; out EnumFormatEtc: IEnumFormatEtc): HResult;
  6400. var
  6401. NewList: TEnumFormatEtc;
  6402. begin
  6403. Result := E_FAIL;
  6404. if Direction = DATADIR_GET then
  6405. begin
  6406. NewList := TEnumFormatEtc.Create(FOwner, FormatEtcArray);
  6407. EnumFormatEtc := NewList as IEnumFormatEtc;
  6408. Result := S_OK;
  6409. end
  6410. else
  6411. EnumFormatEtc := nil;
  6412. if EnumFormatEtc = nil then
  6413. Result := OLE_S_USEREG;
  6414. end;
  6415. //----------------------------------------------------------------------------------------------------------------------
  6416. function TVTDataObject.GetCanonicalFormatEtc(const FormatEtc: TFormatEtc; out FormatEtcOut: TFormatEtc): HResult;
  6417. begin
  6418. Result := DATA_S_SAMEFORMATETC;
  6419. end;
  6420. //----------------------------------------------------------------------------------------------------------------------
  6421. function TVTDataObject.GetData(const FormatEtcIn: TFormatEtc; out Medium: TStgMedium): HResult;
  6422. // Data is requested by clipboard or drop target. This method dispatchs the call
  6423. // depending on the data being requested.
  6424. var
  6425. I: Integer;
  6426. Data: PVTReference;
  6427. begin
  6428. // The tree reference format is always supported and returned from here.
  6429. if FormatEtcIn.cfFormat = CF_VTREFERENCE then
  6430. begin
  6431. // Note: this format is not used while flushing the clipboard to avoid a dangling reference
  6432. // when the owner tree is destroyed before the clipboard data is replaced with something else.
  6433. if tsClipboardFlushing in FOwner.FStates then
  6434. Result := E_FAIL
  6435. else
  6436. begin
  6437. Medium.hGlobal := GlobalAlloc(GHND or GMEM_SHARE, SizeOf(TVTReference));
  6438. Data := GlobalLock(Medium.hGlobal);
  6439. Data.Process := GetCurrentProcessID;
  6440. Data.Tree := FOwner;
  6441. GlobalUnlock(Medium.hGlobal);
  6442. Medium.tymed := TYMED_HGLOBAL;
  6443. Medium.unkForRelease := nil;
  6444. Result := S_OK;
  6445. end;
  6446. end
  6447. else
  6448. begin
  6449. try
  6450. // See if we accept this type and if not get the correct return value.
  6451. Result := QueryGetData(FormatEtcIn);
  6452. if Result = S_OK then
  6453. begin
  6454. for I := 0 to High(FormatEtcArray) do
  6455. begin
  6456. if EqualFormatEtc(FormatEtcIn, FormatEtcArray[I]) then
  6457. begin
  6458. if not RenderInternalOLEData(FormatEtcIn, Medium, Result) then
  6459. Result := FOwner.RenderOLEData(FormatEtcIn, Medium, FForClipboard);
  6460. Break;
  6461. end;
  6462. end;
  6463. end;
  6464. except
  6465. ZeroMemory (@Medium, SizeOf(Medium));
  6466. Result := E_FAIL;
  6467. end;
  6468. end;
  6469. end;
  6470. //----------------------------------------------------------------------------------------------------------------------
  6471. function TVTDataObject.GetDataHere(const FormatEtc: TFormatEtc; out Medium: TStgMedium): HResult;
  6472. begin
  6473. Result := E_NOTIMPL;
  6474. end;
  6475. //----------------------------------------------------------------------------------------------------------------------
  6476. function TVTDataObject.QueryGetData(const FormatEtc: TFormatEtc): HResult;
  6477. var
  6478. I: Integer;
  6479. begin
  6480. Result := DV_E_CLIPFORMAT;
  6481. for I := 0 to High(FFormatEtcArray) do
  6482. begin
  6483. if FormatEtc.cfFormat = FFormatEtcArray[I].cfFormat then
  6484. begin
  6485. if (FormatEtc.tymed and FFormatEtcArray[I].tymed) <> 0 then
  6486. begin
  6487. if FormatEtc.dwAspect = FFormatEtcArray[I].dwAspect then
  6488. begin
  6489. if FormatEtc.lindex = FFormatEtcArray[I].lindex then
  6490. begin
  6491. Result := S_OK;
  6492. Break;
  6493. end
  6494. else
  6495. Result := DV_E_LINDEX;
  6496. end
  6497. else
  6498. Result := DV_E_DVASPECT;
  6499. end
  6500. else
  6501. Result := DV_E_TYMED;
  6502. end;
  6503. end;
  6504. end;
  6505. //----------------------------------------------------------------------------------------------------------------------
  6506. function TVTDataObject.SetData(const FormatEtc: TFormatEtc; var Medium: TStgMedium; DoRelease: BOOL): HResult;
  6507. // Allows dynamic adding to the IDataObject during its existance. Most noteably it is used to implement
  6508. // IDropSourceHelper and allows to set a special format for optimized moves during a shell transfer.
  6509. var
  6510. Index: Integer;
  6511. LocalStgMedium: PStgMedium;
  6512. begin
  6513. // See if we already have a format of that type available.
  6514. Index := FindFormatEtc(FormatEtc, FormatEtcArray);
  6515. if Index > - 1 then
  6516. begin
  6517. // Just use the TFormatEct in the array after releasing the data.
  6518. LocalStgMedium := FindInternalStgMedium(FormatEtcArray[Index].cfFormat);
  6519. if Assigned(LocalStgMedium) then
  6520. begin
  6521. ReleaseStgMedium(LocalStgMedium^);
  6522. ZeroMemory(LocalStgMedium, SizeOf(LocalStgMedium^));
  6523. end;
  6524. end
  6525. else
  6526. begin
  6527. // It is a new format so create a new TFormatCollectionItem, copy the
  6528. // FormatEtc parameter into the new object and and put it in the list.
  6529. SetLength(FFormatEtcArray, Length(FormatEtcArray) + 1);
  6530. FormatEtcArray[High(FormatEtcArray)] := FormatEtc;
  6531. // Create a new InternalStgMedium and initialize it and associate it with the format.
  6532. SetLength(FInternalStgMediumArray, Length(InternalStgMediumArray) + 1);
  6533. InternalStgMediumArray[High(InternalStgMediumArray)].Format := FormatEtc.cfFormat;
  6534. LocalStgMedium := @InternalStgMediumArray[High(InternalStgMediumArray)].Medium;
  6535. ZeroMemory(LocalStgMedium, SizeOf(LocalStgMedium^));
  6536. end;
  6537. if DoRelease then
  6538. begin
  6539. // We are simply being given the data and we take control of it.
  6540. LocalStgMedium^ := Medium;
  6541. Result := S_OK;
  6542. end
  6543. else
  6544. begin
  6545. // We need to reference count or copy the data and keep our own references to it.
  6546. Result := StgMediumIncRef(Medium, LocalStgMedium^, True, Self as IDataObject);
  6547. // Can get a circular reference if the client calls GetData then calls SetData with the same StgMedium.
  6548. // Because the unkForRelease for the IDataObject can be marshalled it is necessary to get pointers that
  6549. // can be correctly compared. See the IDragSourceHelper article by Raymond Chen at MSDN.
  6550. if Assigned(LocalStgMedium.unkForRelease) then
  6551. begin
  6552. if CanonicalIUnknown(Self) = CanonicalIUnknown(IUnknown(LocalStgMedium.unkForRelease)) then
  6553. IUnknown(LocalStgMedium.unkForRelease) := nil; // release the interface
  6554. end;
  6555. end;
  6556. // Tell all registered advice sinks about the data change.
  6557. if Assigned(FAdviseHolder) then
  6558. FAdviseHolder.SendOnDataChange(Self as IDataObject, 0, 0);
  6559. end;
  6560. //----------------- TVTDragManager -------------------------------------------------------------------------------------
  6561. constructor TVTDragManager.Create(AOwner: TBaseVirtualTree);
  6562. begin
  6563. inherited Create;
  6564. FOwner := AOwner;
  6565. // Create an instance of the drop target helper interface. This will fail but not harm on systems which do
  6566. // not support this interface (everything below Windows 2000);
  6567. CoCreateInstance(CLSID_DragDropHelper, nil, CLSCTX_INPROC_SERVER, IID_IDropTargetHelper, FDropTargetHelper);
  6568. end;
  6569. //----------------------------------------------------------------------------------------------------------------------
  6570. destructor TVTDragManager.Destroy;
  6571. begin
  6572. // Set the owner's reference to us to nil otherwise it will access an invalid pointer
  6573. // after our desctruction is complete.
  6574. Pointer(FOwner.FDragManager) := nil;
  6575. inherited;
  6576. end;
  6577. //----------------------------------------------------------------------------------------------------------------------
  6578. function TVTDragManager.GetDataObject: IDataObject;
  6579. begin
  6580. // When the owner tree starts a drag operation then it gets a data object here to pass it to the OLE subsystem.
  6581. // In this case there is no local reference to a data object and one is created (but not stored).
  6582. // If there is a local reference then the owner tree is currently the drop target and the stored interface is
  6583. // that of the drag initiator.
  6584. if Assigned(FDataObject) then
  6585. Result := FDataObject
  6586. else
  6587. begin
  6588. Result := FOwner.DoCreateDataObject;
  6589. if Result = nil then
  6590. Result := TVTDataObject.Create(FOwner, False) as IDataObject;
  6591. end;
  6592. end;
  6593. //----------------------------------------------------------------------------------------------------------------------
  6594. function TVTDragManager.GetDragSource: TBaseVirtualTree;
  6595. begin
  6596. Result := FDragSource;
  6597. end;
  6598. //----------------------------------------------------------------------------------------------------------------------
  6599. function TVTDragManager.GetDropTargetHelperSupported: Boolean;
  6600. begin
  6601. Result := Assigned(FDropTargetHelper);
  6602. end;
  6603. //----------------------------------------------------------------------------------------------------------------------
  6604. function TVTDragManager.GetIsDropTarget: Boolean;
  6605. begin
  6606. Result := FIsDropTarget;
  6607. end;
  6608. //----------------------------------------------------------------------------------------------------------------------
  6609. function TVTDragManager.DragEnter(const DataObject: IDataObject; KeyState: Integer; Pt: TPoint;
  6610. var Effect: Integer): HResult;
  6611. begin
  6612. FDataObject := DataObject;
  6613. FIsDropTarget := True;
  6614. SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, @FFullDragging, 0);
  6615. // If full dragging of window contents is disabled in the system then our tree windows will be locked
  6616. // and cannot be updated during a drag operation. With the following call painting is again enabled.
  6617. if not FFullDragging then
  6618. LockWindowUpdate(0);
  6619. if Assigned(FDropTargetHelper) and FFullDragging then
  6620. FDropTargetHelper.DragEnter(FOwner.Handle, DataObject, Pt, Effect);
  6621. FDragSource := FOwner.GetTreeFromDataObject(DataObject);
  6622. Result := FOwner.DragEnter(KeyState, Pt, Effect);
  6623. end;
  6624. //----------------------------------------------------------------------------------------------------------------------
  6625. function TVTDragManager.DragLeave: HResult;
  6626. begin
  6627. if Assigned(FDropTargetHelper) and FFullDragging then
  6628. FDropTargetHelper.DragLeave;
  6629. FOwner.DragLeave;
  6630. FIsDropTarget := False;
  6631. FDragSource := nil;
  6632. FDataObject := nil;
  6633. Result := NOERROR;
  6634. end;
  6635. //----------------------------------------------------------------------------------------------------------------------
  6636. function TVTDragManager.DragOver(KeyState: Integer; Pt: TPoint; var Effect: LongInt): HResult;
  6637. begin
  6638. if Assigned(FDropTargetHelper) and FFullDragging then
  6639. FDropTargetHelper.DragOver(Pt, Effect);
  6640. Result := FOwner.DragOver(FDragSource, KeyState, dsDragMove, Pt, Effect);
  6641. end;
  6642. //----------------------------------------------------------------------------------------------------------------------
  6643. function TVTDragManager.Drop(const DataObject: IDataObject; KeyState: Integer; Pt: TPoint;
  6644. var Effect: Integer): HResult;
  6645. begin
  6646. if Assigned(FDropTargetHelper) and FFullDragging then
  6647. FDropTargetHelper.Drop(DataObject, Pt, Effect);
  6648. Result := FOwner.DragDrop(DataObject, KeyState, Pt, Effect);
  6649. FIsDropTarget := False;
  6650. FDataObject := nil;
  6651. end;
  6652. //----------------------------------------------------------------------------------------------------------------------
  6653. procedure TVTDragManager.ForceDragLeave;
  6654. // Some drop targets, e.g. Internet Explorer leave a drag image on screen instead removing it when they receive
  6655. // a drop action. This method calls the drop target helper's DragLeave method to ensure it removes the drag image from
  6656. // screen. Unfortunately, sometimes not even this does help (e.g. when dragging text from VT to a text field in IE).
  6657. begin
  6658. if Assigned(FDropTargetHelper) and FFullDragging then
  6659. FDropTargetHelper.DragLeave;
  6660. end;
  6661. //----------------------------------------------------------------------------------------------------------------------
  6662. function TVTDragManager.GiveFeedback(Effect: Integer): HResult;
  6663. begin
  6664. Result := DRAGDROP_S_USEDEFAULTCURSORS;
  6665. end;
  6666. //----------------------------------------------------------------------------------------------------------------------
  6667. function TVTDragManager.QueryContinueDrag(EscapePressed: BOOL; KeyState: Integer): HResult;
  6668. var
  6669. RButton,
  6670. LButton: Boolean;
  6671. begin
  6672. LButton := (KeyState and MK_LBUTTON) <> 0;
  6673. RButton := (KeyState and MK_RBUTTON) <> 0;
  6674. // Drag'n drop canceled by pressing both mouse buttons or Esc?
  6675. if (LButton and RButton) or EscapePressed then
  6676. Result := DRAGDROP_S_CANCEL
  6677. else
  6678. // Drag'n drop finished?
  6679. if not (LButton or RButton) then
  6680. Result := DRAGDROP_S_DROP
  6681. else
  6682. Result := S_OK;
  6683. end;
  6684. //----------------- TVirtualTreeHintWindow -----------------------------------------------------------------------------
  6685. var
  6686. // This variable is necessary to coordinate the complex interaction between different hints in the application
  6687. // and animated hints in our own class. Under certain conditions it can happen that our hint window is destroyed
  6688. // while it is still in the animation loop.
  6689. FHintWindowDestroyed: Boolean = True;
  6690. constructor TVirtualTreeHintWindow.Create(AOwner: TComponent);
  6691. begin
  6692. inherited;
  6693. FBackground := TBitmap.Create;
  6694. FBackground.PixelFormat := pf32Bit;
  6695. FDrawBuffer := TBitmap.Create;
  6696. FDrawBuffer.PixelFormat := pf32Bit;
  6697. FTarget := TBitmap.Create;
  6698. FTarget.PixelFormat := pf32Bit;
  6699. DoubleBuffered := False; // we do our own buffering
  6700. FHintWindowDestroyed := False;
  6701. end;
  6702. //----------------------------------------------------------------------------------------------------------------------
  6703. destructor TVirtualTreeHintWindow.Destroy;
  6704. begin
  6705. FHintWindowDestroyed := True;
  6706. FTarget.Free;
  6707. FDrawBuffer.Free;
  6708. FBackground.Free;
  6709. inherited;
  6710. end;
  6711. //----------------------------------------------------------------------------------------------------------------------
  6712. function TVirtualTreeHintWindow.AnimationCallback(Step, StepSize: Integer; Data: Pointer): Boolean;
  6713. begin
  6714. Result := not FHintWindowDestroyed and HandleAllocated and IsWindowVisible(Handle) and
  6715. Assigned(FHintData.Tree) and not (tsCancelHintAnimation in FHintData.Tree.FStates);
  6716. if Result then
  6717. begin
  6718. InternalPaint(Step, StepSize);
  6719. // We have to allow certain messages to be processed normally for various reasons.
  6720. // This introduces another problem however if this hint window is destroyed
  6721. // while it is still in the animation loop. A global variable keeps track of
  6722. // that case. This is reliable because we can only have one (internal) hint window.
  6723. Application.ProcessMessages;
  6724. end;
  6725. end;
  6726. //----------------------------------------------------------------------------------------------------------------------
  6727. procedure TVirtualTreeHintWindow.CMTextChanged(var Message: TMessage);
  6728. begin
  6729. // swallow this message to prevent the ancestor from resizing the window (we don't use the caption anyway)
  6730. end;
  6731. //----------------------------------------------------------------------------------------------------------------------
  6732. function TVirtualTreeHintWindow.GetHintWindowDestroyed;
  6733. // This function exists to inform descendants if the hint window has been destroyed.
  6734. begin
  6735. Result := FHintWindowDestroyed;
  6736. end;
  6737. //----------------------------------------------------------------------------------------------------------------------
  6738. procedure TVirtualTreeHintWindow.WMEraseBkgnd(var Message: TWMEraseBkgnd);
  6739. // The control is fully painted by own code so don't erase its background as this causes flickering.
  6740. begin
  6741. Message.Result := 1;
  6742. end;
  6743. //----------------------------------------------------------------------------------------------------------------------
  6744. procedure TVirtualTreeHintWindow.WMNCPaint(var Message: TMessage);
  6745. // The control is fully painted by own code so don't paint any borders.
  6746. begin
  6747. Message.Result := 0;
  6748. end;
  6749. //----------------------------------------------------------------------------------------------------------------------
  6750. procedure TVirtualTreeHintWindow.WMShowWindow(var Message: TWMShowWindow);
  6751. // Clear hint data when the window becomes hidden.
  6752. begin
  6753. if not Message.Show then
  6754. begin
  6755. // Don't touch the last hint rectangle stored in the associated tree to avoid flickering in certain situations.
  6756. Finalize(FHintData);
  6757. ZeroMemory (@FHintData, SizeOf(FHintData));
  6758. // If the hint window destruction flag to stop any hint window animation was set by a tree
  6759. // during its destruction then reset it here to allow other tree instances to still use
  6760. // this hint window.
  6761. FHintWindowDestroyed := False;
  6762. end;
  6763. end;
  6764. //----------------------------------------------------------------------------------------------------------------------
  6765. procedure TVirtualTreeHintWindow.CreateParams(var Params: TCreateParams);
  6766. begin
  6767. inherited CreateParams(Params);
  6768. with Params do
  6769. begin
  6770. Style := WS_POPUP;
  6771. ExStyle := ExStyle and not WS_EX_CLIENTEDGE;
  6772. end;
  6773. end;
  6774. //----------------------------------------------------------------------------------------------------------------------
  6775. procedure TVirtualTreeHintWindow.InternalPaint(Step, StepSize: Integer);
  6776. //--------------- local functions -------------------------------------------
  6777. procedure DoShadowBlend(DC: HDC; R: TRect; Alpha: Integer);
  6778. // Helper routine for shadow blending to shorten the parameter list in frequent calls.
  6779. begin
  6780. AlphaBlend(0, DC, R, Point(0, 0), bmConstantAlphaAndColor, Alpha, clBlack);
  6781. end;
  6782. //---------------------------------------------------------------------------
  6783. procedure DrawHintShadow(Canvas: TCanvas; ShadowSize: Integer);
  6784. var
  6785. R: TRect;
  6786. begin
  6787. // Bottom shadow.
  6788. R := Rect(ShadowSize, Height - ShadowSize, Width, Height);
  6789. DoShadowBlend(Canvas.Handle, R, 5);
  6790. Inc(R.Left);
  6791. Dec(R.Right);
  6792. Dec(R.Bottom);
  6793. DoShadowBlend(Canvas.Handle, R, 10);
  6794. Inc(R.Left);
  6795. Dec(R.Right);
  6796. Dec(R.Bottom);
  6797. DoShadowBlend(Canvas.Handle, R, 20);
  6798. Inc(R.Left);
  6799. Dec(R.Right);
  6800. Dec(R.Bottom);
  6801. DoShadowBlend(Canvas.Handle, R, 35);
  6802. Inc(R.Left);
  6803. Dec(R.Right);
  6804. Dec(R.Bottom);
  6805. DoShadowBlend(Canvas.Handle, R, 50);
  6806. // Right shadow.
  6807. R := Rect(Width - ShadowSize, ShadowSize, Width, Height - ShadowSize);
  6808. DoShadowBlend(Canvas.Handle, R, 5);
  6809. Inc(R.Top);
  6810. Dec(R.Right);
  6811. DoShadowBlend(Canvas.Handle, R, 10);
  6812. Inc(R.Top);
  6813. Dec(R.Right);
  6814. DoShadowBlend(Canvas.Handle, R, 20);
  6815. Inc(R.Top);
  6816. Dec(R.Right);
  6817. DoShadowBlend(Canvas.Handle, R, 35);
  6818. Inc(R.Top);
  6819. Dec(R.Right);
  6820. DoShadowBlend(Canvas.Handle, R, 50);
  6821. end;
  6822. //--------------- end local functions ---------------------------------------
  6823. var
  6824. R: TRect;
  6825. Y: Integer;
  6826. S: UnicodeString;
  6827. DrawFormat: Cardinal;
  6828. Shadow: Integer;
  6829. HintKind: TVTHintKind;
  6830. LClipRect: TRect;
  6831. {$IF CompilerVersion >= 23 }
  6832. LColor: TColor;
  6833. LDetails: TThemedElementDetails;
  6834. LGradientStart: TColor;
  6835. LGradientEnd: TColor;
  6836. {$IFEND}
  6837. begin
  6838. Shadow := 0;
  6839. with FHintData, FDrawBuffer do
  6840. begin
  6841. // Do actual painting only in the very first run.
  6842. if Step = 0 then
  6843. begin
  6844. // If the given node is nil then we have to display a header hint.
  6845. if (Node = nil) or (Tree.FHintMode <> hmToolTip) then
  6846. begin
  6847. Canvas.Font := Screen.HintFont;
  6848. Y := 2;
  6849. end
  6850. else
  6851. begin
  6852. Tree.GetTextInfo(Node, Column, Canvas.Font, R, S);
  6853. if LineBreakStyle = hlbForceMultiLine then
  6854. Y := 1
  6855. else
  6856. Y := (R.Top - R.Bottom - Shadow + Self.Height) div 2;
  6857. end;
  6858. R := Rect(0, 0, Width - Shadow, Height - Shadow);
  6859. HintKind := vhkText;
  6860. if Assigned(Node) then
  6861. Tree.DoGetHintKind(Node, Column, HintKind);
  6862. if HintKind = vhkOwnerDraw then
  6863. begin
  6864. Tree.DoDrawHint(Canvas, Node, R, Column);
  6865. end
  6866. else
  6867. with Canvas do
  6868. begin
  6869. {$IF CompilerVersion >= 23 }
  6870. if Tree.VclStyleEnabled then
  6871. begin
  6872. LDetails := StyleServices.GetElementDetails(thHintNormal);
  6873. if StyleServices.GetElementColor(LDetails, ecGradientColor1, LColor) and (LColor <> clNone) then
  6874. LGradientStart := LColor
  6875. else
  6876. LGradientStart := clInfoBk;
  6877. if StyleServices.GetElementColor(LDetails, ecGradientColor2, LColor) and (LColor <> clNone) then
  6878. LGradientEnd := LColor
  6879. else
  6880. LGradientEnd := clInfoBk;
  6881. if StyleServices.GetElementColor(LDetails, ecTextColor, LColor) and (LColor <> clNone) then
  6882. Font.Color := LColor
  6883. else
  6884. Font.Color := Screen.HintFont.Color;
  6885. GradientFillCanvas(Canvas, LGradientStart, LGradientEnd, R, gdVertical);
  6886. end
  6887. else
  6888. {$IFEND}
  6889. begin
  6890. // Still force tooltip back and text color.
  6891. Font.Color := clInfoText;
  6892. Pen.Color := clBlack;
  6893. Brush.Color := clInfoBk;
  6894. if IsWinVistaOrAbove and StyleServices.Enabled and ((toThemeAware in Tree.TreeOptions.PaintOptions) or
  6895. (toUseExplorerTheme in Tree.TreeOptions.PaintOptions)) then
  6896. begin
  6897. if toUseExplorerTheme in Tree.TreeOptions.PaintOptions then // ToolTip style
  6898. StyleServices.DrawElement(Canvas.Handle, StyleServices.GetElementDetails(tttStandardNormal), R)
  6899. else
  6900. begin // Hint style
  6901. LClipRect := R;
  6902. InflateRect(R, 4, 4);
  6903. StyleServices.DrawElement(Handle, StyleServices.GetElementDetails(tttStandardNormal), R, @LClipRect);
  6904. R := LClipRect;
  6905. StyleServices.DrawEdge(Handle, StyleServices.GetElementDetails(twWindowRoot), R, [eeRaisedOuter], [efRect]);
  6906. end;
  6907. end
  6908. else
  6909. if Tree.VclStyleEnabled then
  6910. StyleServices.DrawElement(Canvas.Handle, StyleServices.GetElementDetails(tttStandardNormal), R)
  6911. else
  6912. Rectangle(R);
  6913. end;
  6914. // Determine text position and don't forget the border.
  6915. InflateRect(R, -1, -1);
  6916. DrawFormat := DT_TOP or DT_NOPREFIX;
  6917. SetBkMode(Handle, Windows.TRANSPARENT);
  6918. R.Top := Y;
  6919. R.Left := R.Left + 3; // Make the text more centered
  6920. if Assigned(Node) and (LineBreakStyle = hlbForceMultiLine) then
  6921. DrawFormat := DrawFormat or DT_WORDBREAK;
  6922. Windows.DrawTextW(Handle, PWideChar(HintText), Length(HintText), R, DrawFormat);
  6923. end;
  6924. end;
  6925. end;
  6926. if StepSize > 0 then
  6927. begin
  6928. if FHintData.Tree.DoGetAnimationType = hatFade then
  6929. begin
  6930. with FTarget do
  6931. BitBlt(Canvas.Handle, 0, 0, Width, Height, FBackground.Canvas.Handle, 0, 0, SRCCOPY);
  6932. // Main image.
  6933. AlphaBlend(FDrawBuffer.Canvas.Handle, FTarget.Canvas.Handle, Rect(0, 0, Width - Shadow, Height - Shadow),
  6934. Point(0, 0), bmConstantAlpha, MulDiv(Step, 256, FadeAnimationStepCount), 0);
  6935. if Shadow > 0 then
  6936. DrawHintShadow(FTarget.Canvas, Shadow);
  6937. BitBlt(Canvas.Handle, 0, 0, Width, Height, FTarget.Canvas.Handle, 0, 0, SRCCOPY);
  6938. end
  6939. else
  6940. begin
  6941. // Slide is done by blitting "step" lines of the lower part of the hint window
  6942. // and fill the rest with the screen background.
  6943. // 1) blit hint bitmap to the hint canvas
  6944. BitBlt(Canvas.Handle, 0, 0, Width - Shadow, Step, FDrawBuffer.Canvas.Handle, 0, Height - Step, SRCCOPY);
  6945. // 2) blit background rest to hint canvas
  6946. if Step <= Shadow then
  6947. Step := 0
  6948. else
  6949. Dec(Step, Shadow);
  6950. BitBlt(Canvas.Handle, 0, Step, Width, Height - Step, FBackground.Canvas.Handle, 0, Step, SRCCOPY);
  6951. end;
  6952. end
  6953. else
  6954. // Last step during slide or the only step without animation.
  6955. if FHintData.Tree.DoGetAnimationType <> hatFade then
  6956. begin
  6957. if Shadow > 0 then
  6958. begin
  6959. with FBackground do
  6960. BitBlt(Canvas.Handle, 0, 0, Width - Shadow, Height - Shadow, FDrawBuffer.Canvas.Handle, 0, 0, SRCCOPY);
  6961. DrawHintShadow(FBackground.Canvas, Shadow);
  6962. BitBlt(Canvas.Handle, 0, 0, Width, Height, FBackground.Canvas.Handle, 0, 0, SRCCOPY);
  6963. end
  6964. else
  6965. BitBlt(Canvas.Handle, 0, 0, Width, Height, FDrawBuffer.Canvas.Handle, 0, 0, SRCCOPY);
  6966. end;
  6967. end;
  6968. //----------------------------------------------------------------------------------------------------------------------
  6969. procedure TVirtualTreeHintWindow.Paint;
  6970. begin
  6971. InternalPaint(0, 0);
  6972. end;
  6973. //----------------------------------------------------------------------------------------------------------------------
  6974. procedure TVirtualTreeHintWindow.ActivateHint(Rect: TRect; const AHint: string);
  6975. var
  6976. DC: HDC;
  6977. StopLastAnimation: Boolean;
  6978. lCursorPos: TPoint;
  6979. begin
  6980. if IsRectEmpty(Rect) or not Assigned(FHintData.Tree) or
  6981. not GetCursorPos(lCursorPos) or not PtInRect(FHintData.Tree.FLastHintRect, FHintData.Tree.ScreenToClient(lCursorPos))
  6982. then
  6983. Application.CancelHint
  6984. else
  6985. begin
  6986. // There is already an animation. Start a new one but do not continue the old one once we are finished here.
  6987. StopLastAnimation := (tsInAnimation in FHintData.Tree.FStates);
  6988. if StopLastAnimation then
  6989. FHintData.Tree.DoStateChange([], [tsInAnimation]);
  6990. SetWindowPos(Handle, 0, Rect.Left, Rect.Top, Width, Height, SWP_HIDEWINDOW or SWP_NOACTIVATE or SWP_NOZORDER);
  6991. UpdateBoundsRect(Rect);
  6992. // Make sure the whole hint is visible on the monitor. Don't forget multi-monitor systems with the
  6993. // primary monitor not being at the top-left corner.
  6994. if Rect.Top - Screen.DesktopTop + Height > Screen.DesktopHeight then
  6995. Rect.Top := Screen.DesktopHeight - Height + Screen.DesktopTop;
  6996. if Rect.Left - Screen.DesktopLeft + Width > Screen.DesktopWidth then
  6997. Rect.Left := Screen.DesktopWidth - Width + Screen.DesktopLeft;
  6998. if Rect.Bottom - Screen.DesktopTop < Screen.DesktopTop then
  6999. Rect.Bottom := Screen.DesktopTop + Screen.DesktopTop;
  7000. if Rect.Left - Screen.DesktopLeft < Screen.DesktopLeft then
  7001. Rect.Left := Screen.DesktopLeft + Screen.DesktopLeft;
  7002. // adjust sizes of bitmaps
  7003. FDrawBuffer.Width := Width;
  7004. FDrawBuffer.Height := Height;
  7005. FBackground.Width := Width;
  7006. FBackground.Height := Height;
  7007. FTarget.Width := Width;
  7008. FTarget.Height := Height;
  7009. FHintData.Tree.Update;
  7010. // capture screen
  7011. DC := GetDC(0);
  7012. try
  7013. with TWithSafeRect(Rect) do
  7014. BitBlt(FBackground.Canvas.Handle, 0, 0, Width, Height, DC, Left, Top, SRCCOPY);
  7015. finally
  7016. ReleaseDC(0, DC);
  7017. end;
  7018. SetWindowPos(Handle, HWND_TOPMOST, Rect.Left, Rect.Top, Width, Height, SWP_SHOWWINDOW or SWP_NOACTIVATE);
  7019. with FHintData.Tree do
  7020. case DoGetAnimationType of
  7021. hatNone:
  7022. InvalidateRect(Self.Handle, nil, False);
  7023. hatFade:
  7024. begin
  7025. // Make sure the window is not drawn unanimated.
  7026. ValidateRect(Self.Handle, nil);
  7027. // Empirically determined animation duration shows that fading needs about twice as much time as
  7028. // sliding to show a comparable visual effect.
  7029. Animate(FadeAnimationStepCount, 2 * FAnimationDuration, AnimationCallback, nil);
  7030. end;
  7031. hatSlide:
  7032. begin
  7033. // Make sure the window is not drawn unanimated.
  7034. ValidateRect(Self.Handle, nil);
  7035. Animate(Self.Height, FAnimationDuration, AnimationCallback, nil);
  7036. end;
  7037. end;
  7038. if not FHintWindowDestroyed and StopLastAnimation and Assigned(FHintData.Tree) then
  7039. FHintData.Tree.DoStateChange([tsCancelHintAnimation]);
  7040. end;
  7041. end;
  7042. //----------------------------------------------------------------------------------------------------------------------
  7043. function TVirtualTreeHintWindow.CalcHintRect(MaxWidth: Integer; const AHint: string; AData: Pointer): TRect;
  7044. var
  7045. TM: TTextMetric;
  7046. R: TRect;
  7047. begin
  7048. if AData = nil then
  7049. // Defensive approach, it *can* happen that AData is nil. Maybe when several user defined hint classes are used.
  7050. Result := Rect(0, 0, 0, 0)
  7051. else
  7052. begin
  7053. // The hint window does not need any bidi mode setting but the caller of this method (TApplication.ActivateHint)
  7054. // does some unneccessary actions if the hint window is not left-to-right.
  7055. // The text alignment is based on the bidi mode passed in the hint data, hence we can
  7056. // simply set the window's mode to left-to-right (it might have been modified by the caller, if the
  7057. // tree window is right-to-left aligned).
  7058. BidiMode := bdLeftToRight;
  7059. FHintData := PVTHintData(AData)^;
  7060. with FHintData do
  7061. begin
  7062. // The draw tree gets its hint size by the application (but only if not a header hint is about to show). // If the user will be drawing the hint, it gets its hint size by the application
  7063. // (but only if not a header hint is about to show).
  7064. // This size has already been determined in CMHintShow.
  7065. if Assigned(Node) and (not IsRectEmpty(HintRect)) then
  7066. Result := HintRect
  7067. else
  7068. begin
  7069. if Column <= NoColumn then
  7070. begin
  7071. BidiMode := Tree.BidiMode;
  7072. Alignment := Tree.Alignment;
  7073. end
  7074. else
  7075. begin
  7076. BidiMode := Tree.Header.Columns[Column].BidiMode;
  7077. Alignment := Tree.Header.Columns[Column].Alignment;
  7078. end;
  7079. if BidiMode <> bdLeftToRight then
  7080. ChangeBidiModeAlignment(Alignment);
  7081. if (Node = nil) or (Tree.FHintMode <> hmToolTip) then
  7082. Canvas.Font := Screen.HintFont
  7083. else
  7084. begin
  7085. Canvas.Font := Tree.Font;
  7086. if Tree is TCustomVirtualStringTree then
  7087. with TCustomVirtualStringTree(Tree) do
  7088. DoPaintText(Node, Self.Canvas, Column, ttNormal);
  7089. end;
  7090. GetTextMetrics(Canvas.Handle, TM);
  7091. FTextHeight := TM.tmHeight;
  7092. LineBreakStyle := hlbDefault;
  7093. if Length(DefaultHint) > 0 then
  7094. HintText := DefaultHint
  7095. else
  7096. if Tree.HintMode = hmToolTip then
  7097. HintText := Tree.DoGetNodeToolTip(Node, Column, LineBreakStyle)
  7098. else
  7099. HintText := Tree.DoGetNodeHint(Node, Column, LineBreakStyle);
  7100. if Length(HintText) = 0 then
  7101. Result := Rect(0, 0, 0, 0)
  7102. else
  7103. begin
  7104. if Assigned(Node) and (Tree.FHintMode = hmToolTip) then
  7105. begin
  7106. // Determine actual line break style depending on what was returned by the methods and what's in the node.
  7107. if LineBreakStyle = hlbDefault then
  7108. if vsMultiline in Node.States then
  7109. LineBreakStyle := hlbForceMultiLine
  7110. else
  7111. LineBreakStyle := hlbForceSingleLine;
  7112. // Hint for a node.
  7113. if LineBreakStyle = hlbForceMultiLine then
  7114. begin
  7115. // Multiline tooltips use the columns width but extend the bottom border to fit the whole caption.
  7116. Result := Tree.GetDisplayRect(Node, Column, True, False);
  7117. R := Result;
  7118. // On Windows NT/2K/XP the behavior of the tooltip is slightly different to that on Windows 9x/Me.
  7119. // We don't have Unicode word wrap on the latter so the tooltip gets as wide as the largest line
  7120. // in the caption (limited by carriage return), which results in unoptimal overlay of the tooltip.
  7121. Windows.DrawTextW(Canvas.Handle, PWideChar(HintText), Length(HintText), R, DT_CALCRECT or DT_WORDBREAK);
  7122. if BidiMode = bdLeftToRight then
  7123. Result.Right := R.Right + Tree.FTextMargin
  7124. else
  7125. Result.Left := R.Left - Tree.FTextMargin + 1;
  7126. Result.Bottom := R.Bottom;
  7127. Inc(Result.Right);
  7128. // If the node height and the column width are both already large enough to cover the entire text,
  7129. // then we don't need the hint, though.
  7130. // However if the text is partially scrolled out of the client area then a hint is useful as well.
  7131. if (Tree.Header.Columns.Count > 0) and ((Integer(Tree.NodeHeight[Node]) + 2) >= (Result.Bottom - Result.Top)) and
  7132. ((Tree.Header.Columns[Column].Width + 2) >= (Result.Right - Result.Left)) and not
  7133. ((Result.Left < 0) or (Result.Right > Tree.ClientWidth + 3) or
  7134. (Result.Top < 0) or (Result.Bottom > Tree.ClientHeight + 3)) then
  7135. begin
  7136. Result := Rect(0, 0, 0, 0);
  7137. Exit;
  7138. end;
  7139. end
  7140. else
  7141. begin
  7142. Result := Tree.FLastHintRect; // = Tree.GetDisplayRect(Node, Column, True, True, True); see TBaseVirtualTree.CMHintShow
  7143. if toShowHorzGridLines in Tree.TreeOptions.PaintOptions then
  7144. Dec(Result.Bottom);
  7145. end;
  7146. // Include a one pixel border.
  7147. InflateRect(Result, 1, 1);
  7148. // Make the coordinates relative. They will again be offset by the caller code.
  7149. OffsetRect(Result, -Result.Left - 1, -Result.Top - 1);
  7150. end
  7151. else
  7152. begin
  7153. // Hint for a header or non-tooltip hint.
  7154. // Start with the base size of the hint in client coordinates.
  7155. Result := Rect(0, 0, MaxWidth, FTextHeight);
  7156. // Calculate the true size of the text rectangle.
  7157. Windows.DrawTextW(Canvas.Handle, PWideChar(HintText), Length(HintText), Result, DT_CALCRECT or DT_TOP or DT_NOPREFIX or DT_WORDBREAK);
  7158. // The height of the text plus 2 pixels vertical margin plus the border determine the hint window height.
  7159. Inc(Result.Bottom, 6);
  7160. // The text is centered horizontally with usual text margin for left and right borders (plus border).
  7161. if not Assigned(Tree) then
  7162. Exit; // Workaround, because we have seen several exceptions here caught by Eurekalog. Submitted as issue #114 to http://code.google.com/p/virtual-treeview/
  7163. Inc(Result.Right, Tree.FTextMargin + FTextHeight); // We are extending the width here, but the text height scales with the text width and has a similar value as AveCharWdith * 2.
  7164. end;
  7165. end;
  7166. end;
  7167. end;
  7168. end;
  7169. end;
  7170. //----------------------------------------------------------------------------------------------------------------------
  7171. function TVirtualTreeHintWindow.IsHintMsg(var Msg: TMsg): Boolean;
  7172. // The VCL is a bit too generous when telling that an existing hint can be cancelled. Need to specify further here.
  7173. begin
  7174. Result := inherited IsHintMsg(Msg) and HandleAllocated and IsWindowVisible(Handle);
  7175. // Avoid that mouse moves over the non-client area or key presses cancel the current hint.
  7176. if Result and ((Msg.Message = WM_NCMOUSEMOVE) or ((Msg.Message >= WM_KEYFIRST) and (Msg.Message <= WM_KEYLAST))) then
  7177. Result := False
  7178. else
  7179. // Work around problems with keypresses while doing hint animation.
  7180. if HandleAllocated and IsWindowVisible(Handle) and (Msg.Message >= WM_KEYFIRST) and (Msg.Message <= WM_KEYLAST) and
  7181. (tsInAnimation in FHintData.Tree.FStates) and TranslateMessage(Msg) then
  7182. DispatchMessage(Msg);
  7183. end;
  7184. //----------------- TVTDragImage ---------------------------------------------------------------------------------------
  7185. constructor TVTDragImage.Create(AOwner: TBaseVirtualTree);
  7186. begin
  7187. FOwner := AOwner;
  7188. FTransparency := 128;
  7189. FPreBlendBias := 0;
  7190. FPostBlendBias := 0;
  7191. FFade := False;
  7192. FRestriction := dmrNone;
  7193. FColorKey := clNone;
  7194. end;
  7195. //----------------------------------------------------------------------------------------------------------------------
  7196. destructor TVTDragImage.Destroy;
  7197. begin
  7198. EndDrag;
  7199. inherited;
  7200. end;
  7201. //----------------------------------------------------------------------------------------------------------------------
  7202. function TVTDragImage.GetVisible: Boolean;
  7203. // Returns True if the internal drag image is used (i.e. the system does not natively support drag images) and
  7204. // the internal image is currently visible on screen.
  7205. begin
  7206. Result := FStates * [disHidden, disInDrag, disPrepared, disSystemSupport] = [disInDrag, disPrepared];
  7207. end;
  7208. //----------------------------------------------------------------------------------------------------------------------
  7209. procedure TVTDragImage.InternalShowDragImage(ScreenDC: HDC);
  7210. // Frequently called helper routine to actually do the blend and put it onto the screen.
  7211. // Only used if the system does not support drag images.
  7212. var
  7213. BlendMode: TBlendMode;
  7214. begin
  7215. with FAlphaImage do
  7216. BitBlt(Canvas.Handle, 0, 0, Width, Height, FBackImage.Canvas.Handle, 0, 0, SRCCOPY);
  7217. if not FFade and (FColorKey = clNone) then
  7218. BlendMode := bmConstantAlpha
  7219. else
  7220. BlendMode := bmMasterAlpha;
  7221. with FDragImage do
  7222. AlphaBlend(Canvas.Handle, FAlphaImage.Canvas.Handle, Rect(0, 0, Width, Height), Point(0, 0), BlendMode,
  7223. FTransparency, FPostBlendBias);
  7224. with FAlphaImage do
  7225. BitBlt(ScreenDC, FImagePosition.X, FImagePosition.Y, Width, Height, Canvas.Handle, 0, 0, SRCCOPY);
  7226. end;
  7227. //----------------------------------------------------------------------------------------------------------------------
  7228. procedure TVTDragImage.MakeAlphaChannel(Source, Target: TBitmap);
  7229. // Helper method to create a proper alpha channel in Target (which must be in 32 bit pixel format), depending
  7230. // on the settings for the drag image and the color values in Source.
  7231. // Only used if the system does not support drag images.
  7232. type
  7233. PBGRA = ^TBGRA;
  7234. TBGRA = packed record
  7235. case Boolean of
  7236. False:
  7237. (Color: Cardinal);
  7238. True:
  7239. (BGR: array[0..2] of Byte;
  7240. Alpha: Byte);
  7241. end;
  7242. var
  7243. Color,
  7244. ColorKeyRef: COLORREF;
  7245. UseColorKey: Boolean;
  7246. SourceRun,
  7247. TargetRun: PBGRA;
  7248. X, Y,
  7249. MaxDimension,
  7250. HalfWidth,
  7251. HalfHeight: Integer;
  7252. T: Extended;
  7253. begin
  7254. UseColorKey := ColorKey <> clNone;
  7255. ColorKeyRef := ColorToRGB(ColorKey) and $FFFFFF;
  7256. // Color values are in the form BGR (red on LSB) while bitmap colors are in the form ARGB (blue on LSB)
  7257. // hence we have to swap red and blue in the color key.
  7258. with TBGRA(ColorKeyRef) do
  7259. begin
  7260. X := BGR[0];
  7261. BGR[0] := BGR[2];
  7262. BGR[2] := X;
  7263. end;
  7264. with Target do
  7265. begin
  7266. MaxDimension := Max(Width, Height);
  7267. HalfWidth := Width div 2;
  7268. HalfHeight := Height div 2;
  7269. for Y := 0 to Height - 1 do
  7270. begin
  7271. TargetRun := Scanline[Y];
  7272. SourceRun := Source.Scanline[Y];
  7273. for X := 0 to Width - 1 do
  7274. begin
  7275. Color := SourceRun.Color and $FFFFFF;
  7276. if UseColorKey and (Color = ColorKeyRef) then
  7277. TargetRun.Alpha := 0
  7278. else
  7279. begin
  7280. // If the color is not the given color key (or none is used) then do full calculation of a bell curve.
  7281. T := Exp(-8 * Sqrt(Sqr((X - HalfWidth) / MaxDimension) + Sqr((Y - HalfHeight) / MaxDimension)));
  7282. TargetRun.Alpha := Round(255 * T);
  7283. end;
  7284. Inc(SourceRun);
  7285. Inc(TargetRun);
  7286. end;
  7287. end;
  7288. end;
  7289. end;
  7290. //----------------------------------------------------------------------------------------------------------------------
  7291. function TVTDragImage.DragTo(P: TPoint; ForceRepaint: Boolean): Boolean;
  7292. // Moves the drag image to a new position, which is determined from the passed point P and the previous
  7293. // mouse position.
  7294. // ForceRepaint is True if something on the screen changed and the back image must be refreshed.
  7295. var
  7296. ScreenDC: HDC;
  7297. DeltaX,
  7298. DeltaY: Integer;
  7299. // optimized drag image move support
  7300. RSamp1,
  7301. RSamp2, // newly added parts from screen which will be overwritten
  7302. RDraw1,
  7303. RDraw2, // parts to be restored to screen
  7304. RScroll,
  7305. RClip: TRect; // ScrollDC of the existent background
  7306. begin
  7307. // Determine distances to move the drag image. Take care for restrictions.
  7308. case FRestriction of
  7309. dmrHorizontalOnly:
  7310. begin
  7311. DeltaX := FLastPosition.X - P.X;
  7312. DeltaY := 0;
  7313. end;
  7314. dmrVerticalOnly:
  7315. begin
  7316. DeltaX := 0;
  7317. DeltaY := FLastPosition.Y - P.Y;
  7318. end;
  7319. else // dmrNone
  7320. DeltaX := FLastPosition.X - P.X;
  7321. DeltaY := FLastPosition.Y - P.Y;
  7322. end;
  7323. Result := (DeltaX <> 0) or (DeltaY <> 0) or ForceRepaint;
  7324. if Result then
  7325. begin
  7326. if Visible then
  7327. begin
  7328. // All this stuff is only called if we have to handle the drag image ourselves. If the system supports
  7329. // drag image then this is all never executed.
  7330. ScreenDC := GetDC(0);
  7331. try
  7332. if (Abs(DeltaX) >= FDragImage.Width) or (Abs(DeltaY) >= FDragImage.Height) or ForceRepaint then
  7333. begin
  7334. // If moved more than image size then just restore old screen and blit image to new position.
  7335. BitBlt(ScreenDC, FImagePosition.X, FImagePosition.Y, FBackImage.Width, FBackImage.Height,
  7336. FBackImage.Canvas.Handle, 0, 0, SRCCOPY);
  7337. if ForceRepaint then
  7338. UpdateWindow(FOwner.Handle);
  7339. Inc(FImagePosition.X, -DeltaX);
  7340. Inc(FImagePosition.Y, -DeltaY);
  7341. BitBlt(FBackImage.Canvas.Handle, 0, 0, FBackImage.Width, FBackImage.Height, ScreenDC, FImagePosition.X,
  7342. FImagePosition.Y, SRCCOPY);
  7343. end
  7344. else
  7345. begin
  7346. // overlapping copy
  7347. FillDragRectangles(FDragImage.Width, FDragImage.Height, DeltaX, DeltaY, RClip, RScroll, RSamp1, RSamp2, RDraw1,
  7348. RDraw2);
  7349. with FBackImage.Canvas do
  7350. begin
  7351. // restore uncovered areas of the screen
  7352. if DeltaX = 0 then
  7353. begin
  7354. with TWithSafeRect(RDraw2) do
  7355. BitBlt(ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top, Right, Bottom, Handle, Left, Top,
  7356. SRCCOPY);
  7357. end
  7358. else
  7359. begin
  7360. if DeltaY = 0 then
  7361. begin
  7362. with TWithSafeRect(RDraw1) do
  7363. BitBlt(ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top, Right, Bottom, Handle, Left, Top,
  7364. SRCCOPY);
  7365. end
  7366. else
  7367. begin
  7368. with TWithSafeRect(RDraw1) do
  7369. BitBlt(ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top, Right, Bottom, Handle, Left, Top,
  7370. SRCCOPY);
  7371. with TWithSafeRect(RDraw2) do
  7372. BitBlt(ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top, Right, Bottom, Handle, Left, Top,
  7373. SRCCOPY);
  7374. end;
  7375. end;
  7376. // move existent background
  7377. ScrollDC(Handle, DeltaX, DeltaY, RScroll, RClip, 0, nil);
  7378. Inc(FImagePosition.X, -DeltaX);
  7379. Inc(FImagePosition.Y, -DeltaY);
  7380. // Get first and second additional rectangle from screen.
  7381. if DeltaX = 0 then
  7382. begin
  7383. with TWithSafeRect(RSamp2) do
  7384. BitBlt(Handle, Left, Top, Right, Bottom, ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top,
  7385. SRCCOPY);
  7386. end
  7387. else
  7388. if DeltaY = 0 then
  7389. begin
  7390. with TWithSafeRect(RSamp1) do
  7391. BitBlt(Handle, Left, Top, Right, Bottom, ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top,
  7392. SRCCOPY);
  7393. end
  7394. else
  7395. begin
  7396. with TWithSafeRect(RSamp1) do
  7397. BitBlt(Handle, Left, Top, Right, Bottom, ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top,
  7398. SRCCOPY);
  7399. with TWithSafeRect(RSamp2) do
  7400. BitBlt(Handle, Left, Top, Right, Bottom, ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top,
  7401. SRCCOPY);
  7402. end;
  7403. end;
  7404. end;
  7405. InternalShowDragImage(ScreenDC);
  7406. finally
  7407. ReleaseDC(0, ScreenDC);
  7408. end;
  7409. end;
  7410. FLastPosition.X := P.X;
  7411. FLastPosition.Y := P.Y;
  7412. end;
  7413. end;
  7414. //----------------------------------------------------------------------------------------------------------------------
  7415. procedure TVTDragImage.EndDrag;
  7416. begin
  7417. HideDragImage;
  7418. FStates := FStates - [disInDrag, disPrepared];
  7419. FBackImage.Free;
  7420. FBackImage := nil;
  7421. FDragImage.Free;
  7422. FDragImage := nil;
  7423. FAlphaImage.Free;
  7424. FAlphaImage := nil;
  7425. end;
  7426. //----------------------------------------------------------------------------------------------------------------------
  7427. function TVTDragImage.GetDragImageRect: TRect;
  7428. // Returns the current size and position of the drag image (screen coordinates).
  7429. begin
  7430. if Visible then
  7431. begin
  7432. with FBackImage do
  7433. Result := Rect(FImagePosition.X, FImagePosition.Y, FImagePosition.X + Width, FImagePosition.Y + Height);
  7434. end
  7435. else
  7436. Result := Rect(0, 0, 0, 0);
  7437. end;
  7438. //----------------------------------------------------------------------------------------------------------------------
  7439. procedure TVTDragImage.HideDragImage;
  7440. var
  7441. ScreenDC: HDC;
  7442. begin
  7443. if Visible then
  7444. begin
  7445. Include(FStates, disHidden);
  7446. ScreenDC := GetDC(0);
  7447. try
  7448. // restore screen
  7449. with FBackImage do
  7450. BitBlt(ScreenDC, FImagePosition.X, FImagePosition.Y, Width, Height, Canvas.Handle, 0, 0, SRCCOPY);
  7451. finally
  7452. ReleaseDC(0, ScreenDC);
  7453. end;
  7454. end;
  7455. end;
  7456. //----------------------------------------------------------------------------------------------------------------------
  7457. procedure TVTDragImage.PrepareDrag(DragImage: TBitmap; ImagePosition, HotSpot: TPoint; const DataObject: IDataObject);
  7458. // Creates all necessary structures to do alpha blended dragging using the given image.
  7459. // ImagePostion and HotSpot are given in screen coordinates. The first determines where to place the drag image while
  7460. // the second is the initial mouse position.
  7461. // This method also determines whether the system supports drag images natively. If so then only minimal structures
  7462. // are created.
  7463. var
  7464. Width,
  7465. Height: Integer;
  7466. DragSourceHelper: IDragSourceHelper;
  7467. DragInfo: TSHDragImage;
  7468. lDragSourceHelper2: IDragSourceHelper2;// Needed to get Windows Vista+ style drag hints.
  7469. lNullPoint: TPoint;
  7470. begin
  7471. Width := DragImage.Width;
  7472. Height := DragImage.Height;
  7473. // Determine whether the system supports the drag helper interfaces.
  7474. if Assigned(DataObject) and Succeeded(CoCreateInstance(CLSID_DragDropHelper, nil, CLSCTX_INPROC_SERVER,
  7475. IDragSourceHelper, DragSourceHelper)) then
  7476. begin
  7477. Include(FStates, disSystemSupport);
  7478. lNullPoint := Point(0,0);
  7479. if Supports(DragSourceHelper, IDragSourceHelper2, lDragSourceHelper2) then
  7480. lDragSourceHelper2.SetFlags(DSH_ALLOWDROPDESCRIPTIONTEXT);// Show description texts
  7481. // First let the system try to initialze the DragSourceHelper, this works fine for file system objects (CF_HDROP)
  7482. StandardOLEFormat.cfFormat := CF_HDROP;
  7483. if not Succeeded(DataObject.QueryGetData(StandardOLEFormat)) or not Succeeded(DragSourceHelper.InitializeFromWindow(0, lNullPoint, DataObject)) then
  7484. begin
  7485. // Supply the drag source helper with our drag image.
  7486. DragInfo.sizeDragImage.cx := Width;
  7487. DragInfo.sizeDragImage.cy := Height;
  7488. DragInfo.ptOffset.x := Width div 2;
  7489. DragInfo.ptOffset.y := Height div 2;
  7490. DragInfo.hbmpDragImage := CopyImage(DragImage.Handle, IMAGE_BITMAP, Width, Height, LR_COPYRETURNORG);
  7491. DragInfo.crColorKey := ColorToRGB(FColorKey);
  7492. if not Succeeded(DragSourceHelper.InitializeFromBitmap(@DragInfo, DataObject)) then
  7493. begin
  7494. DeleteObject(DragInfo.hbmpDragImage);
  7495. Exclude(FStates, disSystemSupport);
  7496. end;
  7497. end;
  7498. end
  7499. else
  7500. Exclude(FStates, disSystemSupport);
  7501. if MMXAvailable and not (disSystemSupport in FStates) then
  7502. begin
  7503. FLastPosition := HotSpot;
  7504. FDragImage := TBitmap.Create;
  7505. FDragImage.PixelFormat := pf32Bit;
  7506. FDragImage.Width := Width;
  7507. FDragImage.Height := Height;
  7508. FAlphaImage := TBitmap.Create;
  7509. FAlphaImage.PixelFormat := pf32Bit;
  7510. FAlphaImage.Width := Width;
  7511. FAlphaImage.Height := Height;
  7512. FBackImage := TBitmap.Create;
  7513. FBackImage.PixelFormat := pf32Bit;
  7514. FBackImage.Width := Width;
  7515. FBackImage.Height := Height;
  7516. // Copy the given drag image and apply pre blend bias if required.
  7517. if FPreBlendBias = 0 then
  7518. with FDragImage do
  7519. BitBlt(Canvas.Handle, 0, 0, Width, Height, DragImage.Canvas.Handle, 0, 0, SRCCOPY)
  7520. else
  7521. AlphaBlend(DragImage.Canvas.Handle, FDragImage.Canvas.Handle, Rect(0, 0, Width, Height), Point(0, 0),
  7522. bmConstantAlpha, 255, FPreBlendBias);
  7523. // Create a proper alpha channel also if no fading is required (transparent parts).
  7524. MakeAlphaChannel(DragImage, FDragImage);
  7525. FImagePosition := ImagePosition;
  7526. // Initially the drag image is hidden and will be shown during the immediately following DragEnter event.
  7527. FStates := FStates + [disInDrag, disHidden, disPrepared];
  7528. end;
  7529. end;
  7530. //----------------------------------------------------------------------------------------------------------------------
  7531. procedure TVTDragImage.RecaptureBackground(Tree: TBaseVirtualTree; R: TRect; VisibleRegion: HRGN;
  7532. CaptureNCArea, ReshowDragImage: Boolean);
  7533. // Notification by the drop target tree to update the background image because something in the tree has changed.
  7534. // Note: The passed rectangle is given in client coordinates of the current drop target tree (given in Tree).
  7535. // The caller does not check if the given rectangle is actually within the drag image. Hence this method must do
  7536. // all the checks.
  7537. // This method does nothing if the system manages the drag image.
  7538. var
  7539. DragRect,
  7540. ClipRect: TRect;
  7541. PaintTarget: TPoint;
  7542. PaintOptions: TVTInternalPaintOptions;
  7543. ScreenDC: HDC;
  7544. begin
  7545. // Recapturing means we want the tree to paint the new part into our back bitmap instead to the screen.
  7546. if Visible then
  7547. begin
  7548. // Create the minimum rectangle to be recaptured.
  7549. MapWindowPoints(Tree.Handle, 0, R, 2);
  7550. DragRect := GetDragImageRect;
  7551. IntersectRect(R, R, DragRect);
  7552. OffsetRgn(VisibleRegion, -DragRect.Left, -DragRect.Top);
  7553. // The target position for painting in the drag image is relative and can be determined from screen coordinates too.
  7554. PaintTarget.X := R.Left - DragRect.Left;
  7555. PaintTarget.Y := R.Top - DragRect.Top;
  7556. // The source rectangle is determined by the offsets in the tree.
  7557. MapWindowPoints(0, Tree.Handle, R, 2);
  7558. OffsetRect(R, -Tree.FOffsetX, -Tree.FOffsetY);
  7559. // Finally let the tree paint the relevant part and upate the drag image on screen.
  7560. PaintOptions := [poBackground, poColumnColor, poDrawFocusRect, poDrawDropMark, poDrawSelection, poGridLines];
  7561. with FBackImage do
  7562. begin
  7563. ClipRect.TopLeft := PaintTarget;
  7564. ClipRect.Right := ClipRect.Left + R.Right - R.Left;
  7565. ClipRect.Bottom := ClipRect.Top + R.Bottom - R.Top;
  7566. ClipCanvas(Canvas, ClipRect, VisibleRegion);
  7567. Tree.PaintTree(Canvas, R, PaintTarget, PaintOptions);
  7568. if CaptureNCArea then
  7569. begin
  7570. // For the non-client area we only need the visible region of the window as limit for painting.
  7571. SelectClipRgn(Canvas.Handle, VisibleRegion);
  7572. // Since WM_PRINT cannot be given a position where to draw we simply move the window origin and
  7573. // get the same effect.
  7574. GetWindowRect(Tree.Handle, ClipRect);
  7575. SetCanvasOrigin(Canvas, DragRect.Left - ClipRect.Left, DragRect.Top - ClipRect.Top);
  7576. Tree.Perform(WM_PRINT, WPARAM(Canvas.Handle), PRF_NONCLIENT);
  7577. SetCanvasOrigin(Canvas, 0, 0);
  7578. end;
  7579. SelectClipRgn(Canvas.Handle, 0);
  7580. if ReshowDragImage then
  7581. begin
  7582. GDIFlush;
  7583. ScreenDC := GetDC(0);
  7584. try
  7585. InternalShowDragImage(ScreenDC);
  7586. finally
  7587. ReleaseDC(0, ScreenDC);
  7588. end;
  7589. end;
  7590. end;
  7591. end;
  7592. end;
  7593. //----------------------------------------------------------------------------------------------------------------------
  7594. procedure TVTDragImage.ShowDragImage;
  7595. // Shows the drag image after it has been hidden by HideDragImage.
  7596. // Note: there might be a new background now.
  7597. // Also this method does nothing if the system manages the drag image.
  7598. var
  7599. ScreenDC: HDC;
  7600. begin
  7601. if FStates * [disInDrag, disHidden, disPrepared, disSystemSupport] = [disInDrag, disHidden, disPrepared] then
  7602. begin
  7603. Exclude(FStates, disHidden);
  7604. GDIFlush;
  7605. ScreenDC := GetDC(0);
  7606. try
  7607. BitBlt(FBackImage.Canvas.Handle, 0, 0, FBackImage.Width, FBackImage.Height, ScreenDC, FImagePosition.X,
  7608. FImagePosition.Y, SRCCOPY);
  7609. InternalShowDragImage(ScreenDC);
  7610. finally
  7611. ReleaseDC(0, ScreenDC);
  7612. end;
  7613. end;
  7614. end;
  7615. //----------------------------------------------------------------------------------------------------------------------
  7616. function TVTDragImage.WillMove(P: TPoint): Boolean;
  7617. // This method determines whether the drag image would "physically" move when DragTo would be called with the same
  7618. // target point.
  7619. // Always returns False if the system drag image support is available.
  7620. begin
  7621. Result := Visible;
  7622. if Result then
  7623. begin
  7624. // Determine distances to move the drag image. Take care for restrictions.
  7625. case FRestriction of
  7626. dmrHorizontalOnly:
  7627. Result := FLastPosition.X <> P.X;
  7628. dmrVerticalOnly:
  7629. Result := FLastPosition.Y <> P.Y;
  7630. else // dmrNone
  7631. Result := (FLastPosition.X <> P.X) or (FLastPosition.Y <> P.Y);
  7632. end;
  7633. end;
  7634. end;
  7635. //----------------- TVTVirtualNodeEnumerator ---------------------------------------------------------------------------
  7636. function TVTVirtualNodeEnumerator.GetCurrent: PVirtualNode;
  7637. begin
  7638. Result := FNode;
  7639. end;
  7640. //----------------------------------------------------------------------------------------------------------------------
  7641. function TVTVirtualNodeEnumerator.MoveNext: Boolean;
  7642. begin
  7643. Result := FCanModeNext;
  7644. if Result then
  7645. begin
  7646. FNode := FEnumeration.GetNext(FNode);
  7647. Result := FNode <> nil;
  7648. FCanModeNext := Result;
  7649. end;
  7650. end;
  7651. //----------------- TVTVirtualNodeEnumeration --------------------------------------------------------------------------
  7652. function TVTVirtualNodeEnumeration.GetEnumerator: TVTVirtualNodeEnumerator;
  7653. begin
  7654. {$if CompilerVersion >= 18}
  7655. {$else}
  7656. Result := TVTVirtualNodeEnumerator.Create;
  7657. {$ifend}
  7658. Result.FNode := nil;
  7659. Result.FCanModeNext := True;
  7660. Result.FEnumeration := @Self;
  7661. end;
  7662. //----------------------------------------------------------------------------------------------------------------------
  7663. function TVTVirtualNodeEnumeration.GetNext(Node: PVirtualNode): PVirtualNode;
  7664. begin
  7665. case FMode of
  7666. vneAll:
  7667. if Node = nil then
  7668. Result := FTree.GetFirst(FConsiderChildrenAbove)
  7669. else
  7670. Result := FTree.GetNext(Node, FConsiderChildrenAbove);
  7671. vneChecked:
  7672. if Node = nil then
  7673. Result := FTree.GetFirstChecked(FState, FConsiderChildrenAbove)
  7674. else
  7675. Result := FTree.GetNextChecked(Node, FState, FConsiderChildrenAbove);
  7676. vneChild:
  7677. if Node = nil then
  7678. Result := FTree.GetFirstChild(FNode)
  7679. else
  7680. Result := FTree.GetNextSibling(Node);
  7681. vneCutCopy:
  7682. if Node = nil then
  7683. Result := FTree.GetFirstCutCopy(FConsiderChildrenAbove)
  7684. else
  7685. Result := FTree.GetNextCutCopy(Node, FConsiderChildrenAbove);
  7686. vneInitialized:
  7687. if Node = nil then
  7688. Result := FTree.GetFirstInitialized(FConsiderChildrenAbove)
  7689. else
  7690. Result := FTree.GetNextInitialized(Node, FConsiderChildrenAbove);
  7691. vneLeaf:
  7692. if Node = nil then
  7693. Result := FTree.GetFirstLeaf
  7694. else
  7695. Result := FTree.GetNextLeaf(Node);
  7696. vneLevel:
  7697. if Node = nil then
  7698. Result := FTree.GetFirstLevel(FNodeLevel)
  7699. else
  7700. Result := FTree.GetNextLevel(Node, FNodeLevel);
  7701. vneNoInit:
  7702. if Node = nil then
  7703. Result := FTree.GetFirstNoInit(FConsiderChildrenAbove)
  7704. else
  7705. Result := FTree.GetNextNoInit(Node, FConsiderChildrenAbove);
  7706. vneSelected:
  7707. if Node = nil then
  7708. Result := FTree.GetFirstSelected(FConsiderChildrenAbove)
  7709. else
  7710. Result := FTree.GetNextSelected(Node, FConsiderChildrenAbove);
  7711. vneVisible:
  7712. begin
  7713. if Node = nil then
  7714. begin
  7715. Result := FTree.GetFirstVisible(FNode, FConsiderChildrenAbove, FIncludeFiltered);
  7716. if FIncludeFiltered or not FTree.IsEffectivelyFiltered[Result] then
  7717. Exit;
  7718. end;
  7719. repeat
  7720. Result := FTree.GetNextVisible(Node{, FConsiderChildrenAbove});
  7721. until not Assigned(Result) or FIncludeFiltered or not FTree.IsEffectivelyFiltered[Result];
  7722. end;
  7723. vneVisibleChild:
  7724. if Node = nil then
  7725. Result := FTree.GetFirstVisibleChild(FNode, FIncludeFiltered)
  7726. else
  7727. Result := FTree.GetNextVisibleSibling(Node, FIncludeFiltered);
  7728. vneVisibleNoInitChild:
  7729. if Node = nil then
  7730. Result := FTree.GetFirstVisibleChildNoInit(FNode, FIncludeFiltered)
  7731. else
  7732. Result := FTree.GetNextVisibleSiblingNoInit(Node, FIncludeFiltered);
  7733. vneVisibleNoInit:
  7734. begin
  7735. if Node = nil then
  7736. begin
  7737. Result := FTree.GetFirstVisibleNoInit(FNode, FConsiderChildrenAbove, FIncludeFiltered);
  7738. if FIncludeFiltered or not FTree.IsEffectivelyFiltered[Result] then
  7739. Exit;
  7740. end;
  7741. repeat
  7742. Result := FTree.GetNextVisibleNoInit(Node, FConsiderChildrenAbove);
  7743. until not Assigned(Result) or FIncludeFiltered or not FTree.IsEffectivelyFiltered[Result];
  7744. end;
  7745. else
  7746. Result := nil;
  7747. end;
  7748. end;
  7749. //----------------- TVirtualTreeColumn ---------------------------------------------------------------------------------
  7750. constructor TVirtualTreeColumn.Create(Collection: TCollection);
  7751. begin
  7752. FMinWidth := 10;
  7753. FMaxWidth := 10000;
  7754. FImageIndex := -1;
  7755. FMargin := 4;
  7756. FSpacing := 3;
  7757. FText := '';
  7758. FOptions := DefaultColumnOptions;
  7759. FAlignment := taLeftJustify;
  7760. FBiDiMode := bdLeftToRight;
  7761. FColor := clWindow;
  7762. FLayout := blGlyphLeft;
  7763. FBonusPixel := False;
  7764. FCaptionAlignment := taLeftJustify;
  7765. FCheckType := ctCheckBox;
  7766. FCheckState := csUncheckedNormal;
  7767. FCheckBox := False;
  7768. FHasImage := False;
  7769. FDefaultSortDirection := sdAscending;
  7770. inherited Create(Collection);
  7771. FWidth := Owner.FDefaultWidth;
  7772. FLastWidth := Owner.FDefaultWidth;
  7773. FPosition := Owner.Count - 1;
  7774. // Read parent bidi mode and color values as default values.
  7775. ParentBiDiModeChanged;
  7776. ParentColorChanged;
  7777. end;
  7778. //----------------------------------------------------------------------------------------------------------------------
  7779. destructor TVirtualTreeColumn.Destroy;
  7780. var
  7781. I: Integer;
  7782. //--------------- local function ---------------------------------------------
  7783. procedure AdjustColumnIndex(var ColumnIndex: TColumnIndex);
  7784. begin
  7785. if Index = ColumnIndex then
  7786. ColumnIndex := NoColumn
  7787. else
  7788. if Index < ColumnIndex then
  7789. Dec(ColumnIndex);
  7790. end;
  7791. //--------------- end local function -----------------------------------------
  7792. begin
  7793. // Check if this column is somehow referenced by its collection parent or the header.
  7794. with Owner do
  7795. begin
  7796. // If the columns collection object is currently deleting all columns
  7797. // then we don't need to check the various cached indices individually.
  7798. if not FClearing then
  7799. begin
  7800. Header.Treeview.CancelEditNode;
  7801. IndexChanged(Index, -1);
  7802. AdjustColumnIndex(FHoverIndex);
  7803. AdjustColumnIndex(FDownIndex);
  7804. AdjustColumnIndex(FTrackIndex);
  7805. AdjustColumnIndex(FClickIndex);
  7806. with Header do
  7807. begin
  7808. AdjustColumnIndex(FAutoSizeIndex);
  7809. if Index = FMainColumn then
  7810. begin
  7811. // If the current main column is about to be destroyed then we have to find a new main column.
  7812. FMainColumn := NoColumn;
  7813. for I := 0 to Count - 1 do
  7814. if I <> Index then
  7815. begin
  7816. FMainColumn := I;
  7817. Break;
  7818. end;
  7819. end;
  7820. AdjustColumnIndex(FSortColumn);
  7821. end;
  7822. end;
  7823. end;
  7824. inherited;
  7825. end;
  7826. //----------------------------------------------------------------------------------------------------------------------
  7827. function TVirtualTreeColumn.GetCaptionAlignment: TAlignment;
  7828. begin
  7829. if coUseCaptionAlignment in FOptions then
  7830. Result := FCaptionAlignment
  7831. else
  7832. Result := FAlignment;
  7833. end;
  7834. //----------------------------------------------------------------------------------------------------------------------
  7835. function TVirtualTreeColumn.GetLeft: Integer;
  7836. begin
  7837. Result := FLeft;
  7838. if [coVisible, coFixed] * FOptions <> [coVisible, coFixed] then
  7839. Dec(Result, Owner.Header.Treeview.FEffectiveOffsetX);
  7840. end;
  7841. //----------------------------------------------------------------------------------------------------------------------
  7842. function TVirtualTreeColumn.IsBiDiModeStored: Boolean;
  7843. begin
  7844. Result := not (coParentBiDiMode in FOptions);
  7845. end;
  7846. //----------------------------------------------------------------------------------------------------------------------
  7847. function TVirtualTreeColumn.IsCaptionAlignmentStored: Boolean;
  7848. begin
  7849. Result := coUseCaptionAlignment in FOptions;
  7850. end;
  7851. //----------------------------------------------------------------------------------------------------------------------
  7852. function TVirtualTreeColumn.IsColorStored: Boolean;
  7853. begin
  7854. Result := not (coParentColor in FOptions);
  7855. end;
  7856. //----------------------------------------------------------------------------------------------------------------------
  7857. procedure TVirtualTreeColumn.SetAlignment(const Value: TAlignment);
  7858. begin
  7859. if FAlignment <> Value then
  7860. begin
  7861. FAlignment := Value;
  7862. Changed(False);
  7863. // Setting the alignment affects also the tree, hence invalidate it too.
  7864. Owner.Header.TreeView.Invalidate;
  7865. end;
  7866. end;
  7867. //----------------------------------------------------------------------------------------------------------------------
  7868. procedure TVirtualTreeColumn.SetBiDiMode(Value: TBiDiMode);
  7869. begin
  7870. if Value <> FBiDiMode then
  7871. begin
  7872. FBiDiMode := Value;
  7873. Exclude(FOptions, coParentBiDiMode);
  7874. Changed(False);
  7875. // Setting the alignment affects also the tree, hence invalidate it too.
  7876. Owner.Header.TreeView.Invalidate;
  7877. end;
  7878. end;
  7879. //----------------------------------------------------------------------------------------------------------------------
  7880. procedure TVirtualTreeColumn.SetCaptionAlignment(const Value: TAlignment);
  7881. begin
  7882. if not (coUseCaptionAlignment in FOptions) or (FCaptionAlignment <> Value) then
  7883. begin
  7884. FCaptionAlignment := Value;
  7885. Include(FOptions, coUseCaptionAlignment);
  7886. // Setting the alignment affects also the tree, hence invalidate it too.
  7887. Owner.Header.Invalidate(Self);
  7888. end;
  7889. end;
  7890. //----------------------------------------------------------------------------------------------------------------------
  7891. procedure TVirtualTreeColumn.SetColor(const Value: TColor);
  7892. begin
  7893. if FColor <> Value then
  7894. begin
  7895. FColor := Value;
  7896. Exclude(FOptions, coParentColor);
  7897. Changed(False);
  7898. Owner.Header.TreeView.Invalidate;
  7899. end;
  7900. end;
  7901. //----------------------------------------------------------------------------------------------------------------------
  7902. procedure TVirtualTreeColumn.SetCheckBox(Value: Boolean);
  7903. begin
  7904. if Value <> FCheckBox then
  7905. begin
  7906. FCheckBox := Value;
  7907. if Value and (csDesigning in Owner.Header.Treeview.ComponentState) then
  7908. Owner.Header.Options := Owner.Header.Options + [hoShowImages];
  7909. Changed(False);
  7910. end;
  7911. end;
  7912. //----------------------------------------------------------------------------------------------------------------------
  7913. procedure TVirtualTreeColumn.SetCheckState(Value: TCheckState);
  7914. begin
  7915. if Value <> FCheckState then
  7916. begin
  7917. FCheckState := Value;
  7918. Changed(False);
  7919. end;
  7920. end;
  7921. //----------------------------------------------------------------------------------------------------------------------
  7922. procedure TVirtualTreeColumn.SetCheckType(Value: TCheckType);
  7923. begin
  7924. if Value <> FCheckType then
  7925. begin
  7926. FCheckType := Value;
  7927. Changed(False);
  7928. end;
  7929. end;
  7930. //----------------------------------------------------------------------------------------------------------------------
  7931. procedure TVirtualTreeColumn.SetImageIndex(Value: TImageIndex);
  7932. begin
  7933. if Value <> FImageIndex then
  7934. begin
  7935. FImageIndex := Value;
  7936. Changed(False);
  7937. end;
  7938. end;
  7939. //----------------------------------------------------------------------------------------------------------------------
  7940. procedure TVirtualTreeColumn.SetLayout(Value: TVTHeaderColumnLayout);
  7941. begin
  7942. if FLayout <> Value then
  7943. begin
  7944. FLayout := Value;
  7945. Changed(False);
  7946. end;
  7947. end;
  7948. //----------------------------------------------------------------------------------------------------------------------
  7949. procedure TVirtualTreeColumn.SetMargin(Value: Integer);
  7950. begin
  7951. // Compatibility setting for -1.
  7952. if Value < 0 then
  7953. Value := 4;
  7954. if FMargin <> Value then
  7955. begin
  7956. FMargin := Value;
  7957. Changed(False);
  7958. end;
  7959. end;
  7960. //----------------------------------------------------------------------------------------------------------------------
  7961. procedure TVirtualTreeColumn.SetMaxWidth(Value: Integer);
  7962. begin
  7963. if Value < FMinWidth then
  7964. Value := FMinWidth;
  7965. FMaxWidth := Value;
  7966. SetWidth(FWidth);
  7967. end;
  7968. //----------------------------------------------------------------------------------------------------------------------
  7969. procedure TVirtualTreeColumn.SetMinWidth(Value: Integer);
  7970. begin
  7971. if Value < 0 then
  7972. Value := 0;
  7973. if Value > FMaxWidth then
  7974. Value := FMaxWidth;
  7975. FMinWidth := Value;
  7976. SetWidth(FWidth);
  7977. end;
  7978. //----------------------------------------------------------------------------------------------------------------------
  7979. procedure TVirtualTreeColumn.SetOptions(Value: TVTColumnOptions);
  7980. var
  7981. ToBeSet,
  7982. ToBeCleared: TVTColumnOptions;
  7983. VisibleChanged,
  7984. ColorChanged: Boolean;
  7985. begin
  7986. if FOptions <> Value then
  7987. begin
  7988. ToBeCleared := FOptions - Value;
  7989. ToBeSet := Value - FOptions;
  7990. FOptions := Value;
  7991. VisibleChanged := coVisible in (ToBeSet + ToBeCleared);
  7992. ColorChanged := coParentColor in ToBeSet;
  7993. if coParentBidiMode in ToBeSet then
  7994. ParentBiDiModeChanged;
  7995. if ColorChanged then
  7996. ParentColorChanged;
  7997. if coAutoSpring in ToBeSet then
  7998. FSpringRest := 0;
  7999. if ((coFixed in ToBeSet) or (coFixed in ToBeCleared)) and (coVisible in FOptions) then
  8000. Owner.Header.RescaleHeader;
  8001. Changed(False);
  8002. // Need to repaint and adjust the owner tree too.
  8003. with Owner, Header.Treeview do
  8004. if not (csLoading in ComponentState) and (VisibleChanged or ColorChanged) and (UpdateCount = 0) and
  8005. HandleAllocated then
  8006. begin
  8007. Invalidate;
  8008. if VisibleChanged then
  8009. UpdateHorizontalScrollBar(False);
  8010. end;
  8011. end;
  8012. end;
  8013. //----------------------------------------------------------------------------------------------------------------------
  8014. procedure TVirtualTreeColumn.SetPosition(Value: TColumnPosition);
  8015. var
  8016. Temp: TColumnIndex;
  8017. begin
  8018. if csLoading in Owner.Header.Treeview.ComponentState then
  8019. // Only cache the position for final fixup when loading from DFM.
  8020. FPosition := Value
  8021. else
  8022. begin
  8023. if Value >= TColumnPosition(Collection.Count) then
  8024. Value := Collection.Count - 1;
  8025. if FPosition <> Value then
  8026. begin
  8027. with Owner do
  8028. begin
  8029. InitializePositionArray;
  8030. Header.Treeview.CancelEditNode;
  8031. AdjustPosition(Self, Value);
  8032. Self.Changed(False);
  8033. // Need to repaint.
  8034. with Header do
  8035. begin
  8036. if (UpdateCount = 0) and Treeview.HandleAllocated then
  8037. begin
  8038. Invalidate(Self);
  8039. Treeview.Invalidate;
  8040. end;
  8041. end;
  8042. end;
  8043. // If the moved column is now within the fixed columns then we make it fixed as well. If it's not
  8044. // we clear the fixed state (in case that fixed column is moved outside fixed area).
  8045. if (coFixed in FOptions) and (FPosition > 0) then
  8046. Temp := Owner.ColumnFromPosition(FPosition - 1)
  8047. else
  8048. Temp := Owner.ColumnFromPosition(FPosition + 1);
  8049. if Temp <> NoColumn then
  8050. begin
  8051. if coFixed in Owner[Temp].Options then
  8052. Options := Options + [coFixed]
  8053. else
  8054. Options := Options - [coFixed];
  8055. end;
  8056. end;
  8057. end;
  8058. end;
  8059. //----------------------------------------------------------------------------------------------------------------------
  8060. procedure TVirtualTreeColumn.SetSpacing(Value: Integer);
  8061. begin
  8062. if FSpacing <> Value then
  8063. begin
  8064. FSpacing := Value;
  8065. Changed(False);
  8066. end;
  8067. end;
  8068. //----------------------------------------------------------------------------------------------------------------------
  8069. procedure TVirtualTreeColumn.SetStyle(Value: TVirtualTreeColumnStyle);
  8070. begin
  8071. if FStyle <> Value then
  8072. begin
  8073. FStyle := Value;
  8074. Changed(False);
  8075. end;
  8076. end;
  8077. //----------------------------------------------------------------------------------------------------------------------
  8078. procedure TVirtualTreeColumn.SetText(const Value: UnicodeString);
  8079. begin
  8080. if FText <> Value then
  8081. begin
  8082. FText := Value;
  8083. FCaptionText := '';
  8084. Changed(False);
  8085. end;
  8086. end;
  8087. //----------------------------------------------------------------------------------------------------------------------
  8088. procedure TVirtualTreeColumn.SetWidth(Value: Integer);
  8089. var
  8090. EffectiveMaxWidth,
  8091. EffectiveMinWidth,
  8092. TotalFixedMaxWidth,
  8093. TotalFixedMinWidth: Integer;
  8094. I: TColumnIndex;
  8095. begin
  8096. if not (hsScaling in Owner.FHeader.FStates) then
  8097. if ([coVisible, coFixed] * FOptions = [coVisible, coFixed]) then
  8098. begin
  8099. with Owner, FHeader, FFixedAreaConstraints, TreeView do
  8100. begin
  8101. TotalFixedMinWidth := 0;
  8102. TotalFixedMaxWidth := 0;
  8103. for I := 0 to FColumns.Count - 1 do
  8104. if ([coVisible, coFixed] * FColumns[I].FOptions = [coVisible, coFixed]) then
  8105. begin
  8106. Inc(TotalFixedMaxWidth, FColumns[I].FMaxWidth);
  8107. Inc(TotalFixedMinWidth, FColumns[I].FMinWidth);
  8108. end;
  8109. // The percentage values have precedence over the pixel values.
  8110. TotalFixedMinWidth := IfThen(FMaxWidthPercent > 0,
  8111. Min((ClientWidth * FMaxWidthPercent) div 100, TotalFixedMinWidth),
  8112. TotalFixedMinWidth);
  8113. TotalFixedMaxWidth := IfThen(FMinWidthPercent > 0,
  8114. Max((ClientWidth * FMinWidthPercent) div 100, TotalFixedMaxWidth),
  8115. TotalFixedMaxWidth);
  8116. EffectiveMaxWidth := Min(TotalFixedMaxWidth - (GetVisibleFixedWidth - Self.FWidth), FMaxWidth);
  8117. EffectiveMinWidth := Max(TotalFixedMinWidth - (GetVisibleFixedWidth - Self.FWidth), FMinWidth);
  8118. Value := Min(Max(Value, EffectiveMinWidth), EffectiveMaxWidth);
  8119. if FMinWidthPercent > 0 then
  8120. Value := Max((ClientWidth * FMinWidthPercent) div 100 - GetVisibleFixedWidth + Self.FWidth, Value);
  8121. if FMaxWidthPercent > 0 then
  8122. Value := Min((ClientWidth * FMaxWidthPercent) div 100 - GetVisibleFixedWidth + Self.FWidth, Value);
  8123. end;
  8124. end
  8125. else
  8126. Value := Min(Max(Value, FMinWidth), FMaxWidth);
  8127. if FWidth <> Value then
  8128. begin
  8129. FLastWidth := FWidth;
  8130. if not (hsResizing in Owner.Header.States) then
  8131. FBonusPixel := False;
  8132. with Owner, Header do
  8133. begin
  8134. if not (hoAutoResize in FOptions) or (Index <> FAutoSizeIndex) then
  8135. begin
  8136. FWidth := Value;
  8137. UpdatePositions;
  8138. end;
  8139. if not (csLoading in Treeview.ComponentState) and (UpdateCount = 0) then
  8140. begin
  8141. if hoAutoResize in FOptions then
  8142. AdjustAutoSize(Index);
  8143. Treeview.DoColumnResize(Index);
  8144. end;
  8145. end;
  8146. end;
  8147. end;
  8148. //----------------------------------------------------------------------------------------------------------------------
  8149. procedure TVirtualTreeColumn.ComputeHeaderLayout(DC: HDC; Client: TRect; UseHeaderGlyph, UseSortGlyph: Boolean;
  8150. var HeaderGlyphPos, SortGlyphPos: TPoint; var SortGlyphSize: TSize; var TextBounds: TRect; DrawFormat: Cardinal;
  8151. CalculateTextRect: Boolean = False);
  8152. // The layout of a column header is determined by a lot of factors. This method takes them all into account and
  8153. // determines all necessary positions and bounds:
  8154. // - for the header text
  8155. // - the header glyph
  8156. // - the sort glyph
  8157. var
  8158. TextSize: TSize;
  8159. TextPos,
  8160. ClientSize,
  8161. HeaderGlyphSize: TPoint;
  8162. CurrentAlignment: TAlignment;
  8163. MinLeft,
  8164. MaxRight,
  8165. TextSpacing: Integer;
  8166. UseText: Boolean;
  8167. R: TRect;
  8168. Theme: HTHEME;
  8169. begin
  8170. UseText := Length(FText) > 0;
  8171. // If nothing is to show then don't waste time with useless preparation.
  8172. if not (UseText or UseHeaderGlyph or UseSortGlyph) then
  8173. Exit;
  8174. CurrentAlignment := CaptionAlignment;
  8175. if FBiDiMode <> bdLeftToRight then
  8176. ChangeBiDiModeAlignment(CurrentAlignment);
  8177. // Calculate sizes of the involved items.
  8178. ClientSize := Point(Client.Right - Client.Left, Client.Bottom - Client.Top);
  8179. with Owner, Header do
  8180. begin
  8181. if UseHeaderGlyph then
  8182. if not FCheckBox then
  8183. HeaderGlyphSize := Point(FImages.Width, FImages.Height)
  8184. else
  8185. with Self.Owner.Header.Treeview do
  8186. begin
  8187. if Assigned(FCheckImages) then
  8188. HeaderGlyphSize := Point(FCheckImages.Width, FCheckImages.Height);
  8189. end
  8190. else
  8191. HeaderGlyphSize := Point(0, 0);
  8192. if UseSortGlyph then
  8193. begin
  8194. if tsUseExplorerTheme in FHeader.Treeview.FStates then
  8195. begin
  8196. R := Rect(0, 0, 100, 100);
  8197. Theme := OpenThemeData(FHeader.Treeview.Handle, 'HEADER');
  8198. GetThemePartSize(Theme, DC, HP_HEADERSORTARROW, HSAS_SORTEDUP, @R, TS_TRUE, SortGlyphSize);
  8199. CloseThemeData(Theme);
  8200. end
  8201. else
  8202. begin
  8203. SortGlyphSize.cx := UtilityImages.Width;
  8204. SortGlyphSize.cy := UtilityImages.Height;
  8205. end;
  8206. // In any case, the sort glyph is vertically centered.
  8207. SortGlyphPos.Y := (ClientSize.Y - SortGlyphSize.cy) div 2;
  8208. end
  8209. else
  8210. begin
  8211. SortGlyphSize.cx := 0;
  8212. SortGlyphSize.cy := 0;
  8213. end;
  8214. end;
  8215. if UseText then
  8216. begin
  8217. if not (coWrapCaption in FOptions) then
  8218. begin
  8219. FCaptionText := FText;
  8220. GetTextExtentPoint32W(DC, PWideChar(FText), Length(FText), TextSize);
  8221. Inc(TextSize.cx, 2);
  8222. TextBounds := Rect(0, 0, TextSize.cx, TextSize.cy);
  8223. end
  8224. else
  8225. begin
  8226. R := Client;
  8227. if FCaptionText = '' then
  8228. FCaptionText := WrapString(DC, FText, R, DT_RTLREADING and DrawFormat <> 0, DrawFormat);
  8229. GetStringDrawRect(DC, FCaptionText, R, DrawFormat);
  8230. TextSize.cx := Client.Right - Client.Left;
  8231. TextSize.cy := R.Bottom - R.Top;
  8232. TextBounds := Rect(0, 0, TextSize.cx, TextSize.cy);
  8233. end;
  8234. TextSpacing := FSpacing;
  8235. end
  8236. else
  8237. begin
  8238. TextSpacing := 0;
  8239. TextSize.cx := 0;
  8240. TextSize.cy := 0;
  8241. end;
  8242. // Check first for the special case where nothing is shown except the sort glyph.
  8243. if UseSortGlyph and not (UseText or UseHeaderGlyph) then
  8244. begin
  8245. // Center the sort glyph in the available area if nothing else is there.
  8246. SortGlyphPos := Point((ClientSize.X - SortGlyphSize.cx) div 2, (ClientSize.Y - SortGlyphSize.cy) div 2);
  8247. end
  8248. else
  8249. begin
  8250. // Determine extents of text and glyph and calculate positions which are clear from the layout.
  8251. if (Layout in [blGlyphLeft, blGlyphRight]) or not UseHeaderGlyph then
  8252. begin
  8253. HeaderGlyphPos.Y := (ClientSize.Y - HeaderGlyphSize.Y) div 2;
  8254. // If the text is taller than the given height, perform no vertical centration as this
  8255. // would make the text even less readable.
  8256. //Using Max() fixes badly positioned text if Extra Large fonts have been activated in the Windows display options
  8257. TextPos.Y := Max(-5, (ClientSize.Y - TextSize.cy) div 2);
  8258. end
  8259. else
  8260. begin
  8261. if Layout = blGlyphTop then
  8262. begin
  8263. HeaderGlyphPos.Y := (ClientSize.Y - HeaderGlyphSize.Y - TextSize.cy - TextSpacing) div 2;
  8264. TextPos.Y := HeaderGlyphPos.Y + HeaderGlyphSize.Y + TextSpacing;
  8265. end
  8266. else
  8267. begin
  8268. TextPos.Y := (ClientSize.Y - HeaderGlyphSize.Y - TextSize.cy - TextSpacing) div 2;
  8269. HeaderGlyphPos.Y := TextPos.Y + TextSize.cy + TextSpacing;
  8270. end;
  8271. end;
  8272. // Each alignment needs special consideration.
  8273. case CurrentAlignment of
  8274. taLeftJustify:
  8275. begin
  8276. MinLeft := FMargin;
  8277. if UseSortGlyph and (FBiDiMode <> bdLeftToRight) then
  8278. begin
  8279. // In RTL context is the sort glyph placed on the left hand side.
  8280. SortGlyphPos.X := MinLeft;
  8281. Inc(MinLeft, SortGlyphSize.cx + FSpacing);
  8282. end;
  8283. if Layout in [blGlyphTop, blGlyphBottom] then
  8284. begin
  8285. // Header glyph is above or below text, so both must be considered when calculating
  8286. // the left positition of the sort glyph (if it is on the right hand side).
  8287. TextPos.X := MinLeft;
  8288. if UseHeaderGlyph then
  8289. begin
  8290. HeaderGlyphPos.X := (ClientSize.X - HeaderGlyphSize.X) div 2;
  8291. if HeaderGlyphPos.X < MinLeft then
  8292. HeaderGlyphPos.X := MinLeft;
  8293. MinLeft := Max(TextPos.X + TextSize.cx + TextSpacing, HeaderGlyphPos.X + HeaderGlyphSize.X + FSpacing);
  8294. end
  8295. else
  8296. MinLeft := TextPos.X + TextSize.cx + TextSpacing;
  8297. end
  8298. else
  8299. begin
  8300. // Everything is lined up. TextSpacing might be 0 if there is no text.
  8301. // This simplifies the calculation because no extra tests are necessary.
  8302. if UseHeaderGlyph and (Layout = blGlyphLeft) then
  8303. begin
  8304. HeaderGlyphPos.X := MinLeft;
  8305. Inc(MinLeft, HeaderGlyphSize.X + FSpacing);
  8306. end;
  8307. TextPos.X := MinLeft;
  8308. Inc(MinLeft, TextSize.cx + TextSpacing);
  8309. if UseHeaderGlyph and (Layout = blGlyphRight) then
  8310. begin
  8311. HeaderGlyphPos.X := MinLeft;
  8312. Inc(MinLeft, HeaderGlyphSize.X + FSpacing);
  8313. end;
  8314. end;
  8315. if UseSortGlyph and (FBiDiMode = bdLeftToRight) then
  8316. SortGlyphPos.X := MinLeft;
  8317. end;
  8318. taCenter:
  8319. begin
  8320. if Layout in [blGlyphTop, blGlyphBottom] then
  8321. begin
  8322. HeaderGlyphPos.X := (ClientSize.X - HeaderGlyphSize.X) div 2;
  8323. TextPos.X := (ClientSize.X - TextSize.cx) div 2;
  8324. if UseSortGlyph then
  8325. Dec(TextPos.X, SortGlyphSize.cx div 2);
  8326. end
  8327. else
  8328. begin
  8329. MinLeft := (ClientSize.X - HeaderGlyphSize.X - TextSpacing - TextSize.cx) div 2;
  8330. if UseHeaderGlyph and (Layout = blGlyphLeft) then
  8331. begin
  8332. HeaderGlyphPos.X := MinLeft;
  8333. Inc(MinLeft, HeaderGlyphSize.X + TextSpacing);
  8334. end;
  8335. TextPos.X := MinLeft;
  8336. Inc(MinLeft, TextSize.cx + TextSpacing);
  8337. if UseHeaderGlyph and (Layout = blGlyphRight) then
  8338. HeaderGlyphPos.X := MinLeft;
  8339. end;
  8340. if UseHeaderGlyph then
  8341. begin
  8342. MinLeft := Min(HeaderGlyphPos.X, TextPos.X);
  8343. MaxRight := Max(HeaderGlyphPos.X + HeaderGlyphSize.X, TextPos.X + TextSize.cx);
  8344. end
  8345. else
  8346. begin
  8347. MinLeft := TextPos.X;
  8348. MaxRight := TextPos.X + TextSize.cx;
  8349. end;
  8350. // Place the sort glyph directly to the left or right of the larger item.
  8351. if UseSortGlyph then
  8352. if FBiDiMode = bdLeftToRight then
  8353. begin
  8354. // Sort glyph on the right hand side.
  8355. SortGlyphPos.X := MaxRight + FSpacing;
  8356. end
  8357. else
  8358. begin
  8359. // Sort glyph on the left hand side.
  8360. SortGlyphPos.X := MinLeft - FSpacing - SortGlyphSize.cx;
  8361. end;
  8362. end;
  8363. else
  8364. // taRightJustify
  8365. MaxRight := ClientSize.X - FMargin;
  8366. if UseSortGlyph and (FBiDiMode = bdLeftToRight) then
  8367. begin
  8368. // In LTR context is the sort glyph placed on the right hand side.
  8369. Dec(MaxRight, SortGlyphSize.cx);
  8370. SortGlyphPos.X := MaxRight;
  8371. Dec(MaxRight, FSpacing);
  8372. end;
  8373. if Layout in [blGlyphTop, blGlyphBottom] then
  8374. begin
  8375. TextPos.X := MaxRight - TextSize.cx;
  8376. if UseHeaderGlyph then
  8377. begin
  8378. HeaderGlyphPos.X := (ClientSize.X - HeaderGlyphSize.X) div 2;
  8379. if HeaderGlyphPos.X + HeaderGlyphSize.X + FSpacing > MaxRight then
  8380. HeaderGlyphPos.X := MaxRight - HeaderGlyphSize.X - FSpacing;
  8381. MaxRight := Min(TextPos.X - TextSpacing, HeaderGlyphPos.X - FSpacing);
  8382. end
  8383. else
  8384. MaxRight := TextPos.X - TextSpacing;
  8385. end
  8386. else
  8387. begin
  8388. // Everything is lined up. TextSpacing might be 0 if there is no text.
  8389. // This simplifies the calculation because no extra tests are necessary.
  8390. if UseHeaderGlyph and (Layout = blGlyphRight) then
  8391. begin
  8392. HeaderGlyphPos.X := MaxRight - HeaderGlyphSize.X;
  8393. MaxRight := HeaderGlyphPos.X - FSpacing;
  8394. end;
  8395. TextPos.X := MaxRight - TextSize.cx;
  8396. MaxRight := TextPos.X - TextSpacing;
  8397. if UseHeaderGlyph and (Layout = blGlyphLeft) then
  8398. begin
  8399. HeaderGlyphPos.X := MaxRight - HeaderGlyphSize.X;
  8400. MaxRight := HeaderGlyphPos.X - FSpacing;
  8401. end;
  8402. end;
  8403. if UseSortGlyph and (FBiDiMode <> bdLeftToRight) then
  8404. SortGlyphPos.X := MaxRight - SortGlyphSize.cx;
  8405. end;
  8406. end;
  8407. // Once the position of each element is determined there remains only one but important step.
  8408. // The horizontal positions of every element must be adjusted so that it always fits into the
  8409. // given header area. This is accomplished by shorten the text appropriately.
  8410. // These are the maximum bounds. Nothing goes beyond them.
  8411. MinLeft := FMargin;
  8412. MaxRight := ClientSize.X - FMargin;
  8413. if UseSortGlyph then
  8414. begin
  8415. if FBiDiMode = bdLeftToRight then
  8416. begin
  8417. // Sort glyph on the right hand side.
  8418. if SortGlyphPos.X + SortGlyphSize.cx > MaxRight then
  8419. SortGlyphPos.X := MaxRight - SortGlyphSize.cx;
  8420. MaxRight := SortGlyphPos.X - FSpacing;
  8421. end;
  8422. // Consider also the left side of the sort glyph regardless of the bidi mode.
  8423. if SortGlyphPos.X < MinLeft then
  8424. SortGlyphPos.X := MinLeft;
  8425. // Left border needs only adjustment if the sort glyph marks the left border.
  8426. if FBiDiMode <> bdLeftToRight then
  8427. MinLeft := SortGlyphPos.X + SortGlyphSize.cx + FSpacing;
  8428. // Finally transform sort glyph to its actual position.
  8429. Inc(SortGlyphPos.X, Client.Left);
  8430. Inc(SortGlyphPos.Y, Client.Top);
  8431. end;
  8432. if UseHeaderGlyph then
  8433. begin
  8434. if HeaderGlyphPos.X + HeaderGlyphSize.X > MaxRight then
  8435. HeaderGlyphPos.X := MaxRight - HeaderGlyphSize.X;
  8436. if Layout = blGlyphRight then
  8437. MaxRight := HeaderGlyphPos.X - FSpacing;
  8438. if HeaderGlyphPos.X < MinLeft then
  8439. HeaderGlyphPos.X := MinLeft;
  8440. if Layout = blGlyphLeft then
  8441. MinLeft := HeaderGlyphPos.X + HeaderGlyphSize.X + FSpacing;
  8442. if FCheckBox and (Owner.Header.MainColumn = Self.Index) then
  8443. Dec(HeaderGlyphPos.X, 2)
  8444. else
  8445. if Owner.Header.MainColumn <> Self.Index then
  8446. Dec(HeaderGlyphPos.X, 2);
  8447. // Finally transform header glyph to its actual position.
  8448. Inc(HeaderGlyphPos.X, Client.Left);
  8449. Inc(HeaderGlyphPos.Y, Client.Top);
  8450. end;
  8451. if UseText then
  8452. begin
  8453. if TextPos.X < MinLeft then
  8454. TextPos.X := MinLeft;
  8455. OffsetRect(TextBounds, TextPos.X, TextPos.Y);
  8456. if TextBounds.Right > MaxRight then
  8457. TextBounds.Right := MaxRight;
  8458. OffsetRect(TextBounds, Client.Left, Client.Top);
  8459. if coWrapCaption in FOptions then
  8460. begin
  8461. // Wrap the column caption if necessary.
  8462. R := TextBounds;
  8463. FCaptionText := WrapString(DC, FText, R, DT_RTLREADING and DrawFormat <> 0, DrawFormat);
  8464. GetStringDrawRect(DC, FCaptionText, R, DrawFormat);
  8465. end;
  8466. end;
  8467. end;
  8468. //----------------------------------------------------------------------------------------------------------------------
  8469. procedure TVirtualTreeColumn.DefineProperties(Filer: TFiler);
  8470. begin
  8471. inherited;
  8472. // Must define a new name for the properties otherwise the VCL will try to load the wide string
  8473. // without asking us and screws it completely up.
  8474. Filer.DefineProperty('WideText', ReadText, WriteText, FText <> '');
  8475. Filer.DefineProperty('WideHint', ReadHint, WriteHint, FHint <> '');
  8476. end;
  8477. //----------------------------------------------------------------------------------------------------------------------
  8478. procedure TVirtualTreeColumn.GetAbsoluteBounds(var Left, Right: Integer);
  8479. // Returns the column's left and right bounds in header coordinates, that is, independant of the scrolling position.
  8480. begin
  8481. Left := FLeft;
  8482. Right := FLeft + FWidth;
  8483. end;
  8484. //----------------------------------------------------------------------------------------------------------------------
  8485. function TVirtualTreeColumn.GetDisplayName: string;
  8486. // Returns the column text if it only contains ANSI characters, otherwise the column id is returned because the IDE
  8487. // still cannot handle Unicode strings.
  8488. var
  8489. I: Integer;
  8490. begin
  8491. // Check if the text of the column contains characters > 255
  8492. I := 1;
  8493. while I <= Length(FText) do
  8494. begin
  8495. if Ord(FText[I]) > 255 then
  8496. Break;
  8497. Inc(I);
  8498. end;
  8499. if I > Length(FText) then
  8500. Result := FText // implicit conversion
  8501. else
  8502. Result := Format('Column %d', [Index]);
  8503. end;
  8504. //----------------------------------------------------------------------------------------------------------------------
  8505. function TVirtualTreeColumn.GetOwner: TVirtualTreeColumns;
  8506. begin
  8507. Result := Collection as TVirtualTreeColumns;
  8508. end;
  8509. //----------------------------------------------------------------------------------------------------------------------
  8510. procedure TVirtualTreeColumn.ReadText(Reader: TReader);
  8511. begin
  8512. case Reader.NextValue of
  8513. vaLString, vaString:
  8514. SetText(Reader.ReadString);
  8515. else
  8516. SetText(Reader.{$if CompilerVersion >= 23}ReadString{$else}ReadWideString{$ifend});
  8517. end;
  8518. end;
  8519. //----------------------------------------------------------------------------------------------------------------------
  8520. procedure TVirtualTreeColumn.ReadHint(Reader: TReader);
  8521. begin
  8522. case Reader.NextValue of
  8523. vaLString, vaString:
  8524. FHint := Reader.ReadString;
  8525. else
  8526. FHint := Reader.{$if CompilerVersion >= 23}ReadString{$else}ReadWideString{$ifend};
  8527. end;
  8528. end;
  8529. //----------------------------------------------------------------------------------------------------------------------
  8530. procedure TVirtualTreeColumn.WriteHint(Writer: TWriter);
  8531. begin
  8532. Writer.{$IF CompilerVersion >= 20}WriteString{$else}WriteWideString{$ifend}(FHint);
  8533. end;
  8534. //----------------------------------------------------------------------------------------------------------------------
  8535. procedure TVirtualTreeColumn.WriteText(Writer: TWriter);
  8536. begin
  8537. Writer.{$IF CompilerVersion >= 20}WriteString{$else}WriteWideString{$ifend}(FText);
  8538. end;
  8539. //----------------------------------------------------------------------------------------------------------------------
  8540. procedure TVirtualTreeColumn.Assign(Source: TPersistent);
  8541. var
  8542. OldOptions: TVTColumnOptions;
  8543. begin
  8544. if Source is TVirtualTreeColumn then
  8545. begin
  8546. OldOptions := FOptions;
  8547. FOptions := [];
  8548. BiDiMode := TVirtualTreeColumn(Source).BiDiMode;
  8549. ImageIndex := TVirtualTreeColumn(Source).ImageIndex;
  8550. Layout := TVirtualTreeColumn(Source).Layout;
  8551. Margin := TVirtualTreeColumn(Source).Margin;
  8552. MaxWidth := TVirtualTreeColumn(Source).MaxWidth;
  8553. MinWidth := TVirtualTreeColumn(Source).MinWidth;
  8554. Position := TVirtualTreeColumn(Source).Position;
  8555. Spacing := TVirtualTreeColumn(Source).Spacing;
  8556. Style := TVirtualTreeColumn(Source).Style;
  8557. Text := TVirtualTreeColumn(Source).Text;
  8558. Hint := TVirtualTreeColumn(Source).Hint;
  8559. Width := TVirtualTreeColumn(Source).Width;
  8560. Alignment := TVirtualTreeColumn(Source).Alignment;
  8561. CaptionAlignment := TVirtualTreeColumn(Source).CaptionAlignment;
  8562. Color := TVirtualTreeColumn(Source).Color;
  8563. Tag := TVirtualTreeColumn(Source).Tag;
  8564. // Order is important. Assign options last.
  8565. FOptions := OldOptions;
  8566. Options := TVirtualTreeColumn(Source).Options;
  8567. Changed(False);
  8568. end
  8569. else
  8570. inherited Assign(Source);
  8571. end;
  8572. //----------------------------------------------------------------------------------------------------------------------
  8573. function TVirtualTreeColumn.Equals(OtherColumnObj: TObject): Boolean;
  8574. var
  8575. OtherColumn : TVirtualTreeColumn;
  8576. begin
  8577. if OtherColumnObj is TVirtualTreeColumn then
  8578. begin
  8579. OtherColumn := TVirtualTreeColumn (OtherColumnObj);
  8580. Result := (BiDiMode = OtherColumn.BiDiMode) and
  8581. (ImageIndex = OtherColumn.ImageIndex) and
  8582. (Layout = OtherColumn.Layout) and
  8583. (Margin = OtherColumn.Margin) and
  8584. (MaxWidth = OtherColumn.MaxWidth) and
  8585. (MinWidth = OtherColumn.MinWidth) and
  8586. (Position = OtherColumn.Position) and
  8587. (Spacing = OtherColumn.Spacing) and
  8588. (Style = OtherColumn.Style) and
  8589. (Text = OtherColumn.Text) and
  8590. (Hint = OtherColumn.Hint) and
  8591. (Width = OtherColumn.Width) and
  8592. (Alignment = OtherColumn.Alignment) and
  8593. (CaptionAlignment = OtherColumn.CaptionAlignment) and
  8594. (Color = OtherColumn.Color) and
  8595. (Tag = OtherColumn.Tag) and
  8596. (Options = OtherColumn.Options);
  8597. end
  8598. else
  8599. Result := False;
  8600. end;
  8601. //----------------------------------------------------------------------------------------------------------------------
  8602. function TVirtualTreeColumn.GetRect: TRect;
  8603. // Returns the rectangle this column occupies in the header (relative to (0, 0) of the non-client area).
  8604. begin
  8605. with TVirtualTreeColumns(GetOwner).FHeader do
  8606. Result := Treeview.FHeaderRect;
  8607. Inc(Result.Left, FLeft);
  8608. Result.Right := Result.Left + FWidth;
  8609. end;
  8610. //----------------------------------------------------------------------------------------------------------------------
  8611. // [IPK]
  8612. function TVirtualTreeColumn.GetText: UnicodeString;
  8613. begin
  8614. Result := FText;
  8615. end;
  8616. //----------------------------------------------------------------------------------------------------------------------
  8617. procedure TVirtualTreeColumn.LoadFromStream(const Stream: TStream; Version: Integer);
  8618. //--------------- local function --------------------------------------------
  8619. function ConvertOptions(Value: Cardinal): TVTColumnOptions;
  8620. // Converts the given raw value which represents column options for possibly older
  8621. // formats to the current format.
  8622. begin
  8623. if Version >= 3 then
  8624. Result := TVTColumnOptions(Word(Value and $FFFF))
  8625. else
  8626. if Version = 2 then
  8627. Result := TVTColumnOptions(Word(Value and $FF))
  8628. else
  8629. begin
  8630. // In version 2 coParentColor has been added. This needs an option shift for older stream formats.
  8631. // The first (lower) 4 options remain as they are.
  8632. Result := TVTColumnOptions(Word(Value) and $F);
  8633. Value := (Value and not $F) shl 1;
  8634. Result := Result + TVTColumnOptions(Word(Value and $FF));
  8635. end;
  8636. end;
  8637. //--------------- end local function ----------------------------------------
  8638. var
  8639. Dummy: Integer;
  8640. S: UnicodeString;
  8641. begin
  8642. with Stream do
  8643. begin
  8644. ReadBuffer(Dummy, SizeOf(Dummy));
  8645. SetLength(S, Dummy);
  8646. ReadBuffer(PWideChar(S)^, 2 * Dummy);
  8647. Text := S;
  8648. ReadBuffer(Dummy, SizeOf(Dummy));
  8649. SetLength(FHint, Dummy);
  8650. ReadBuffer(PWideChar(FHint)^, 2 * Dummy);
  8651. ReadBuffer(Dummy, SizeOf(Dummy));
  8652. Width := Dummy;
  8653. ReadBuffer(Dummy, SizeOf(Dummy));
  8654. MinWidth := Dummy;
  8655. ReadBuffer(Dummy, SizeOf(Dummy));
  8656. MaxWidth := Dummy;
  8657. ReadBuffer(Dummy, SizeOf(Dummy));
  8658. Style := TVirtualTreeColumnStyle(Dummy);
  8659. ReadBuffer(Dummy, SizeOf(Dummy));
  8660. ImageIndex := Dummy;
  8661. ReadBuffer(Dummy, SizeOf(Dummy));
  8662. Layout := TVTHeaderColumnLayout(Dummy);
  8663. ReadBuffer(Dummy, SizeOf(Dummy));
  8664. Margin := Dummy;
  8665. ReadBuffer(Dummy, SizeOf(Dummy));
  8666. Spacing := Dummy;
  8667. ReadBuffer(Dummy, SizeOf(Dummy));
  8668. BiDiMode := TBiDiMode(Dummy);
  8669. ReadBuffer(Dummy, SizeOf(Dummy));
  8670. Options := ConvertOptions(Dummy);
  8671. if Version > 0 then
  8672. begin
  8673. // Parts which have been introduced/changed with header stream version 1+.
  8674. ReadBuffer(Dummy, SizeOf(Dummy));
  8675. Tag := Dummy;
  8676. ReadBuffer(Dummy, SizeOf(Dummy));
  8677. Alignment := TAlignment(Dummy);
  8678. if Version > 1 then
  8679. begin
  8680. ReadBuffer(Dummy, SizeOf(Dummy));
  8681. Color := TColor(Dummy);
  8682. end;
  8683. if Version > 5 then
  8684. begin
  8685. if coUseCaptionAlignment in FOptions then
  8686. begin
  8687. ReadBuffer(Dummy, SizeOf(Dummy));
  8688. CaptionAlignment := TAlignment(Dummy);
  8689. end;
  8690. end;
  8691. end;
  8692. end;
  8693. end;
  8694. //----------------------------------------------------------------------------------------------------------------------
  8695. procedure TVirtualTreeColumn.ParentBiDiModeChanged;
  8696. var
  8697. Columns: TVirtualTreeColumns;
  8698. begin
  8699. if coParentBiDiMode in FOptions then
  8700. begin
  8701. Columns := GetOwner as TVirtualTreeColumns;
  8702. if Assigned(Columns) and (FBiDiMode <> Columns.FHeader.Treeview.BiDiMode) then
  8703. begin
  8704. FBiDiMode := Columns.FHeader.Treeview.BiDiMode;
  8705. Changed(False);
  8706. end;
  8707. end;
  8708. end;
  8709. //----------------------------------------------------------------------------------------------------------------------
  8710. procedure TVirtualTreeColumn.ParentColorChanged;
  8711. var
  8712. Columns: TVirtualTreeColumns;
  8713. begin
  8714. if coParentColor in FOptions then
  8715. begin
  8716. Columns := GetOwner as TVirtualTreeColumns;
  8717. if Assigned(Columns) and (FColor <> Columns.FHeader.Treeview.Color) then
  8718. begin
  8719. FColor := Columns.FHeader.Treeview.Color;
  8720. Changed(False);
  8721. end;
  8722. end;
  8723. end;
  8724. //----------------------------------------------------------------------------------------------------------------------
  8725. procedure TVirtualTreeColumn.RestoreLastWidth;
  8726. begin
  8727. TVirtualTreeColumns(GetOwner).AnimatedResize(Index, FLastWidth);
  8728. end;
  8729. //----------------------------------------------------------------------------------------------------------------------
  8730. procedure TVirtualTreeColumn.SaveToStream(const Stream: TStream);
  8731. var
  8732. Dummy: Integer;
  8733. begin
  8734. with Stream do
  8735. begin
  8736. Dummy := Length(FText);
  8737. WriteBuffer(Dummy, SizeOf(Dummy));
  8738. WriteBuffer(PWideChar(FText)^, 2 * Dummy);
  8739. Dummy := Length(FHint);
  8740. WriteBuffer(Dummy, SizeOf(Dummy));
  8741. WriteBuffer(PWideChar(FHint)^, 2 * Dummy);
  8742. WriteBuffer(FWidth, SizeOf(FWidth));
  8743. WriteBuffer(FMinWidth, SizeOf(FMinWidth));
  8744. WriteBuffer(FMaxWidth, SizeOf(FMaxWidth));
  8745. Dummy := Ord(FStyle);
  8746. WriteBuffer(Dummy, SizeOf(Dummy));
  8747. Dummy := FImageIndex;
  8748. WriteBuffer(Dummy, SizeOf(Dummy));
  8749. Dummy := Ord(FLayout);
  8750. WriteBuffer(Dummy, SizeOf(Dummy));
  8751. WriteBuffer(FMargin, SizeOf(FMargin));
  8752. WriteBuffer(FSpacing, SizeOf(FSpacing));
  8753. Dummy := Ord(FBiDiMode);
  8754. WriteBuffer(Dummy, SizeOf(Dummy));
  8755. Dummy := Word(FOptions);
  8756. WriteBuffer(Dummy, SizeOf(Dummy));
  8757. // parts introduced with stream version 1
  8758. WriteBuffer(FTag, SizeOf(Dummy));
  8759. Dummy := Cardinal(FAlignment);
  8760. WriteBuffer(Dummy, SizeOf(Dummy));
  8761. // parts introduced with stream version 2
  8762. Dummy := Integer(FColor);
  8763. WriteBuffer(Dummy, SizeOf(Dummy));
  8764. // parts introduced with stream version 6
  8765. if coUseCaptionAlignment in FOptions then
  8766. begin
  8767. Dummy := Cardinal(FCaptionAlignment);
  8768. WriteBuffer(Dummy, SizeOf(Dummy));
  8769. end;
  8770. end;
  8771. end;
  8772. //----------------------------------------------------------------------------------------------------------------------
  8773. function TVirtualTreeColumn.UseRightToLeftReading: Boolean;
  8774. begin
  8775. Result := FBiDiMode <> bdLeftToRight;
  8776. end;
  8777. //----------------- TVirtualTreeColumns --------------------------------------------------------------------------------
  8778. constructor TVirtualTreeColumns.Create(AOwner: TVTHeader);
  8779. var
  8780. ColumnClass: TVirtualTreeColumnClass;
  8781. begin
  8782. FHeader := AOwner;
  8783. // Determine column class to be used in the header.
  8784. ColumnClass := AOwner.FOwner.GetColumnClass;
  8785. // The owner tree always returns the default tree column class if not changed by application/descendants.
  8786. inherited Create(ColumnClass);
  8787. FHeaderBitmap := TBitmap.Create;
  8788. FHeaderBitmap.PixelFormat := pf32Bit;
  8789. FHoverIndex := NoColumn;
  8790. FDownIndex := NoColumn;
  8791. FClickIndex := NoColumn;
  8792. FDropTarget := NoColumn;
  8793. FTrackIndex := NoColumn;
  8794. FDefaultWidth := 50;
  8795. end;
  8796. //----------------------------------------------------------------------------------------------------------------------
  8797. destructor TVirtualTreeColumns.Destroy;
  8798. begin
  8799. FHeaderBitmap.Free;
  8800. inherited;
  8801. end;
  8802. //----------------------------------------------------------------------------------------------------------------------
  8803. function TVirtualTreeColumns.GetCount: Integer;
  8804. begin
  8805. Result := inherited Count;
  8806. end;
  8807. //----------------------------------------------------------------------------------------------------------------------
  8808. function TVirtualTreeColumns.GetItem(Index: TColumnIndex): TVirtualTreeColumn;
  8809. begin
  8810. Result := TVirtualTreeColumn(inherited GetItem(Index));
  8811. end;
  8812. //----------------------------------------------------------------------------------------------------------------------
  8813. function TVirtualTreeColumns.GetNewIndex(P: TPoint; var OldIndex: TColumnIndex): Boolean;
  8814. var
  8815. NewIndex: Integer;
  8816. begin
  8817. Result := False;
  8818. // convert to local coordinates
  8819. Inc(P.Y, FHeader.FHeight);
  8820. NewIndex := ColumnFromPosition(P);
  8821. if NewIndex <> OldIndex then
  8822. begin
  8823. if OldIndex > NoColumn then
  8824. FHeader.Invalidate(Items[OldIndex]);
  8825. OldIndex := NewIndex;
  8826. if OldIndex > NoColumn then
  8827. FHeader.Invalidate(Items[OldIndex]);
  8828. Result := True;
  8829. end;
  8830. end;
  8831. //----------------------------------------------------------------------------------------------------------------------
  8832. procedure TVirtualTreeColumns.SetDefaultWidth(Value: Integer);
  8833. begin
  8834. FDefaultWidth := Value;
  8835. end;
  8836. //----------------------------------------------------------------------------------------------------------------------
  8837. procedure TVirtualTreeColumns.SetItem(Index: TColumnIndex; Value: TVirtualTreeColumn);
  8838. begin
  8839. inherited SetItem(Index, Value);
  8840. end;
  8841. //----------------------------------------------------------------------------------------------------------------------
  8842. procedure TVirtualTreeColumns.AdjustAutoSize(CurrentIndex: TColumnIndex; Force: Boolean = False);
  8843. // Called only if the header is in auto-size mode which means a column needs to be so large
  8844. // that it fills all the horizontal space not occupied by the other columns.
  8845. // CurrentIndex (if not InvalidColumn) describes which column has just been resized.
  8846. var
  8847. NewValue,
  8848. AutoIndex,
  8849. Index,
  8850. RestWidth: Integer;
  8851. WasUpdating: Boolean;
  8852. begin
  8853. if Count > 0 then
  8854. begin
  8855. // Determine index to be used for auto resizing. This is usually given by the owner's AutoSizeIndex, but
  8856. // could be different if the column whose resize caused the invokation here is either the auto column itself
  8857. // or visually to the right of the auto size column.
  8858. AutoIndex := FHeader.FAutoSizeIndex;
  8859. if (AutoIndex < 0) or (AutoIndex >= Count) then
  8860. AutoIndex := Count - 1;
  8861. if AutoIndex >= 0 then
  8862. begin
  8863. with FHeader.Treeview do
  8864. begin
  8865. if HandleAllocated then
  8866. RestWidth := ClientWidth
  8867. else
  8868. RestWidth := Width;
  8869. end;
  8870. // Go through all columns and calculate the rest space remaining.
  8871. for Index := 0 to Count - 1 do
  8872. if (Index <> AutoIndex) and (coVisible in Items[Index].FOptions) then
  8873. Dec(RestWidth, Items[Index].Width);
  8874. with Items[AutoIndex] do
  8875. begin
  8876. NewValue := Max(MinWidth, Min(MaxWidth, RestWidth));
  8877. if Force or (FWidth <> NewValue) then
  8878. begin
  8879. FWidth := NewValue;
  8880. UpdatePositions;
  8881. WasUpdating := csUpdating in FHeader.Treeview.ComponentState;
  8882. if not WasUpdating then
  8883. FHeader.Treeview.Updating();// Fixes #398
  8884. try
  8885. FHeader.Treeview.DoColumnResize(AutoIndex);
  8886. finally
  8887. if not WasUpdating then
  8888. FHeader.Treeview.Updated();
  8889. end;
  8890. end;
  8891. end;
  8892. end;
  8893. end;
  8894. end;
  8895. //----------------------------------------------------------------------------------------------------------------------
  8896. function TVirtualTreeColumns.AdjustDownColumn(P: TPoint): TColumnIndex;
  8897. // Determines the column from the given position and returns it. If this column is allowed to be clicked then
  8898. // it is also kept for later use.
  8899. begin
  8900. // Convert to local coordinates.
  8901. Inc(P.Y, FHeader.FHeight);
  8902. Result := ColumnFromPosition(P);
  8903. if (Result > NoColumn) and (Result <> FDownIndex) and (coAllowClick in Items[Result].FOptions) and
  8904. (coEnabled in Items[Result].FOptions) then
  8905. begin
  8906. if FDownIndex > NoColumn then
  8907. FHeader.Invalidate(Items[FDownIndex]);
  8908. FDownIndex := Result;
  8909. FCheckBoxHit := Items[Result].FHasImage and PtInRect(Items[Result].FImageRect, P) and Items[Result].CheckBox;
  8910. FHeader.Invalidate(Items[FDownIndex]);
  8911. end;
  8912. end;
  8913. //----------------------------------------------------------------------------------------------------------------------
  8914. function TVirtualTreeColumns.AdjustHoverColumn(P: TPoint): Boolean;
  8915. // Determines the new hover column index and returns True if the index actually changed else False.
  8916. begin
  8917. Result := GetNewIndex(P, FHoverIndex);
  8918. end;
  8919. //----------------------------------------------------------------------------------------------------------------------
  8920. procedure TVirtualTreeColumns.AdjustPosition(Column: TVirtualTreeColumn; Position: Cardinal);
  8921. // Reorders the column position array so that the given column gets the given position.
  8922. var
  8923. OldPosition: Cardinal;
  8924. begin
  8925. OldPosition := Column.Position;
  8926. if OldPosition <> Position then
  8927. begin
  8928. if OldPosition < Position then
  8929. begin
  8930. // column will be moved up so move down other entries
  8931. Move(FPositionToIndex[OldPosition + 1], FPositionToIndex[OldPosition], (Position - OldPosition) * SizeOf(Cardinal));
  8932. end
  8933. else
  8934. begin
  8935. // column will be moved down so move up other entries
  8936. Move(FPositionToIndex[Position], FPositionToIndex[Position + 1], (OldPosition - Position) * SizeOf(Cardinal));
  8937. end;
  8938. FPositionToIndex[Position] := Column.Index;
  8939. end;
  8940. end;
  8941. //----------------------------------------------------------------------------------------------------------------------
  8942. function TVirtualTreeColumns.CanSplitterResize(P: TPoint; Column: TColumnIndex): Boolean;
  8943. begin
  8944. Result := (Column > NoColumn) and ([coResizable, coVisible] * Items[Column].FOptions = [coResizable, coVisible]);
  8945. DoCanSplitterResize(P, Column, Result);
  8946. end;
  8947. //----------------------------------------------------------------------------------------------------------------------
  8948. procedure TVirtualTreeColumns.DoCanSplitterResize(P: TPoint; Column: TColumnIndex; var Allowed: Boolean);
  8949. begin
  8950. if Assigned(FHeader.Treeview.FOnCanSplitterResizeColumn) then
  8951. FHeader.Treeview.FOnCanSplitterResizeColumn(FHeader, P, Column, Allowed);
  8952. end;
  8953. //----------------------------------------------------------------------------------------------------------------------
  8954. procedure TVirtualTreeColumns.DrawButtonText(DC: HDC; Caption: UnicodeString; Bounds: TRect; Enabled, Hot: Boolean;
  8955. DrawFormat: Cardinal; WrapCaption: Boolean);
  8956. var
  8957. TextSpace: Integer;
  8958. Size: TSize;
  8959. begin
  8960. if not WrapCaption then
  8961. begin
  8962. // Do we need to shorten the caption due to limited space?
  8963. GetTextExtentPoint32W(DC, PWideChar(Caption), Length(Caption), Size);
  8964. TextSpace := Bounds.Right - Bounds.Left;
  8965. if TextSpace < Size.cx then
  8966. Caption := ShortenString(DC, Caption, TextSpace);
  8967. end;
  8968. SetBkMode(DC, TRANSPARENT);
  8969. if not Enabled then
  8970. if FHeader.Treeview.VclStyleEnabled then
  8971. begin
  8972. SetTextColor(DC, ColorToRGB(FHeader.Treeview.FColors.HeaderFontColor));
  8973. Windows.DrawTextW(DC, PWideChar(Caption), Length(Caption), Bounds, DrawFormat);
  8974. end
  8975. else
  8976. begin
  8977. OffsetRect(Bounds, 1, 1);
  8978. SetTextColor(DC, ColorToRGB(clBtnHighlight));
  8979. Windows.DrawTextW(DC, PWideChar(Caption), Length(Caption), Bounds, DrawFormat);
  8980. OffsetRect(Bounds, -1, -1);
  8981. SetTextColor(DC, ColorToRGB(clBtnShadow));
  8982. Windows.DrawTextW(DC, PWideChar(Caption), Length(Caption), Bounds, DrawFormat);
  8983. end
  8984. else
  8985. begin
  8986. if Hot then
  8987. SetTextColor(DC, ColorToRGB(FHeader.Treeview.FColors.HeaderHotColor))
  8988. else
  8989. SetTextColor(DC, ColorToRGB(FHeader.Treeview.FColors.HeaderFontColor));
  8990. Windows.DrawTextW(DC, PWideChar(Caption), Length(Caption), Bounds, DrawFormat);
  8991. end;
  8992. end;
  8993. //----------------------------------------------------------------------------------------------------------------------
  8994. procedure TVirtualTreeColumns.FixPositions;
  8995. // Fixes column positions after loading from DFM or Bidi mode change.
  8996. var
  8997. I: Integer;
  8998. begin
  8999. for I := 0 to Count - 1 do
  9000. FPositionToIndex[Items[I].Position] := I;
  9001. FNeedPositionsFix := False;
  9002. UpdatePositions(True);
  9003. end;
  9004. //----------------------------------------------------------------------------------------------------------------------
  9005. function TVirtualTreeColumns.GetColumnAndBounds(P: TPoint; var ColumnLeft, ColumnRight: Integer;
  9006. Relative: Boolean = True): Integer;
  9007. // Returns the column where the mouse is currently in as well as the left and right bound of
  9008. // this column (Left and Right are undetermined if no column is involved).
  9009. var
  9010. I: Integer;
  9011. begin
  9012. Result := InvalidColumn;
  9013. if Relative and (P.X >= Header.Columns.GetVisibleFixedWidth) then
  9014. ColumnLeft := -FHeader.Treeview.FEffectiveOffsetX
  9015. else
  9016. ColumnLeft := 0;
  9017. if FHeader.Treeview.UseRightToLeftAlignment then
  9018. Inc(ColumnLeft, FHeader.Treeview.ComputeRTLOffset(True));
  9019. for I := 0 to Count - 1 do
  9020. with Items[FPositionToIndex[I]] do
  9021. if coVisible in FOptions then
  9022. begin
  9023. ColumnRight := ColumnLeft + FWidth;
  9024. if P.X < ColumnRight then
  9025. begin
  9026. Result := FPositionToIndex[I];
  9027. Exit;
  9028. end;
  9029. ColumnLeft := ColumnRight;
  9030. end;
  9031. end;
  9032. //----------------------------------------------------------------------------------------------------------------------
  9033. function TVirtualTreeColumns.GetOwner: TPersistent;
  9034. begin
  9035. Result := FHeader;
  9036. end;
  9037. //----------------------------------------------------------------------------------------------------------------------
  9038. procedure TVirtualTreeColumns.HandleClick(P: TPoint; Button: TMouseButton; Force, DblClick: Boolean);
  9039. // Generates a click event if the mouse button has been released over the same column it was pressed first.
  9040. // Alternatively, Force might be set to True to indicate that the down index does not matter (right, middle and
  9041. // double click).
  9042. var
  9043. HitInfo: TVTHeaderHitInfo;
  9044. NewClickIndex: Integer;
  9045. begin
  9046. if (csDesigning in Header.Treeview.ComponentState) then
  9047. exit;
  9048. // Convert vertical position to local coordinates.
  9049. Inc(P.Y, FHeader.FHeight);
  9050. NewClickIndex := ColumnFromPosition(P);
  9051. with HitInfo do
  9052. begin
  9053. X := P.X;
  9054. Y := P.Y;
  9055. Shift := FHeader.GetShiftState;
  9056. if DblClick then
  9057. Shift := Shift + [ssDouble];
  9058. end;
  9059. HitInfo.Button := Button;
  9060. if (NewClickIndex > NoColumn) and (coAllowClick in Items[NewClickIndex].FOptions) and
  9061. ((NewClickIndex = FDownIndex) or Force) then
  9062. begin
  9063. FClickIndex := NewClickIndex;
  9064. HitInfo.Column := NewClickIndex;
  9065. HitInfo.HitPosition := [hhiOnColumn];
  9066. if Items[NewClickIndex].FHasImage and PtInRect(Items[NewClickIndex].FImageRect, P) then
  9067. begin
  9068. Include(HitInfo.HitPosition, hhiOnIcon);
  9069. if Items[NewClickIndex].CheckBox then
  9070. begin
  9071. if Button = mbLeft then
  9072. FHeader.Treeview.UpdateColumnCheckState(Items[NewClickIndex]);
  9073. Include(HitInfo.HitPosition, hhiOnCheckbox);
  9074. end;
  9075. end;
  9076. end
  9077. else
  9078. begin
  9079. FClickIndex := NoColumn;
  9080. HitInfo.Column := NoColumn;
  9081. HitInfo.HitPosition := [hhiNoWhere];
  9082. end;
  9083. if (hoHeaderClickAutoSort in Header.Options) and (HitInfo.Button = mbLeft) and not DblClick and not (hhiOnCheckbox in HitInfo.HitPosition) and (HitInfo.Column >= 0) then
  9084. begin
  9085. // handle automatic setting of SortColumn and toggling of the sort order
  9086. if HitInfo.Column <> Header.SortColumn then
  9087. begin
  9088. // set sort column
  9089. Header.SortColumn := HitInfo.Column;
  9090. Header.SortDirection := Self[Header.SortColumn].DefaultSortDirection;
  9091. end//if
  9092. else
  9093. begin
  9094. // toggle sort direction
  9095. if Header.SortDirection = sdDescending then
  9096. Header.SortDirection := sdAscending
  9097. else
  9098. Header.SortDirection := sdDescending;
  9099. end;//else
  9100. end;//if
  9101. if DblClick then
  9102. FHeader.Treeview.DoHeaderDblClick(HitInfo)
  9103. else
  9104. FHeader.Treeview.DoHeaderClick(HitInfo);
  9105. if not (hhiNoWhere in HitInfo.HitPosition) then
  9106. FHeader.Invalidate(Items[NewClickIndex]);
  9107. if (FClickIndex > NoColumn) and (FClickIndex <> NewClickIndex) then
  9108. FHeader.Invalidate(Items[FClickIndex]);
  9109. end;
  9110. //----------------------------------------------------------------------------------------------------------------------
  9111. procedure TVirtualTreeColumns.IndexChanged(OldIndex, NewIndex: Integer);
  9112. // Called by a column when its index in the collection changes. If NewIndex is -1 then the column is
  9113. // about to be removed, otherwise it is moved to a new index.
  9114. // The method will then update the position array to reflect the change.
  9115. var
  9116. I: Integer;
  9117. Increment: Integer;
  9118. Lower,
  9119. Upper: Integer;
  9120. begin
  9121. if NewIndex = -1 then
  9122. begin
  9123. // Find position in the array with the old index.
  9124. Upper := High(FPositionToIndex);
  9125. for I := 0 to Upper do
  9126. begin
  9127. if FPositionToIndex[I] = OldIndex then
  9128. begin
  9129. // Index found. Move all higher entries one step down and remove the last entry.
  9130. if I < Upper then
  9131. Move(FPositionToIndex[I + 1], FPositionToIndex[I], (Upper - I) * SizeOf(TColumnIndex));
  9132. end;
  9133. // Decrease all indices, which are greater than the index to be deleted.
  9134. if FPositionToIndex[I] > OldIndex then
  9135. Dec(FPositionToIndex[I]);
  9136. end;
  9137. SetLength(FPositionToIndex, High(FPositionToIndex));
  9138. end
  9139. else
  9140. begin
  9141. if OldIndex < NewIndex then
  9142. Increment := -1
  9143. else
  9144. Increment := 1;
  9145. Lower := Min(OldIndex, NewIndex);
  9146. Upper := Max(OldIndex, NewIndex);
  9147. for I := 0 to High(FPositionToIndex) do
  9148. begin
  9149. if (FPositionToIndex[I] >= Lower) and (FPositionToIndex[I] < Upper) then
  9150. Inc(FPositionToIndex[I], Increment)
  9151. else
  9152. if FPositionToIndex[I] = OldIndex then
  9153. FPositionToIndex[I] := NewIndex;
  9154. end;
  9155. end;
  9156. end;
  9157. //----------------------------------------------------------------------------------------------------------------------
  9158. procedure TVirtualTreeColumns.InitializePositionArray;
  9159. // Ensures that the column position array contains as many entries as columns are defined.
  9160. // The array is resized and initialized with default values if needed.
  9161. var
  9162. I, OldSize: Integer;
  9163. Changed: Boolean;
  9164. begin
  9165. if Count <> Length(FPositionToIndex) then
  9166. begin
  9167. OldSize := Length(FPositionToIndex);
  9168. SetLength(FPositionToIndex, Count);
  9169. if Count > OldSize then
  9170. begin
  9171. // New items have been added, just set their position to the same as their index.
  9172. for I := OldSize to Count - 1 do
  9173. FPositionToIndex[I] := I;
  9174. end
  9175. else
  9176. begin
  9177. // Items have been deleted, so reindex remaining entries by decrementing values larger than the highest
  9178. // possible index until no entry is higher than this limit.
  9179. repeat
  9180. Changed := False;
  9181. for I := 0 to Count - 1 do
  9182. if FPositionToIndex[I] >= Count then
  9183. begin
  9184. Dec(FPositionToIndex[I]);
  9185. Changed := True;
  9186. end;
  9187. until not Changed;
  9188. end;
  9189. end;
  9190. end;
  9191. //----------------------------------------------------------------------------------------------------------------------
  9192. procedure TVirtualTreeColumns.Notify(Item: TCollectionItem; Action: TCollectionNotification);
  9193. begin
  9194. if Action in [cnExtracting, cnDeleting] then
  9195. with Header.Treeview do
  9196. if not (csLoading in ComponentState) and (FFocusedColumn = Item.Index) then
  9197. FFocusedColumn := NoColumn;
  9198. end;
  9199. //----------------------------------------------------------------------------------------------------------------------
  9200. procedure TVirtualTreeColumns.ReorderColumns(RTL: Boolean);
  9201. var
  9202. I: Integer;
  9203. begin
  9204. if RTL then
  9205. begin
  9206. for I := 0 to Count - 1 do
  9207. FPositionToIndex[I] := Count - I - 1;
  9208. end
  9209. else
  9210. begin
  9211. for I := 0 to Count - 1 do
  9212. FPositionToIndex[I] := I;
  9213. end;
  9214. UpdatePositions(True);
  9215. end;
  9216. //----------------------------------------------------------------------------------------------------------------------
  9217. procedure TVirtualTreeColumns.Update(Item: TCollectionItem);
  9218. begin
  9219. // This is the only place which gets notified when a new column has been added or removed
  9220. // and we need this event to adjust the column position array.
  9221. InitializePositionArray;
  9222. if csLoading in Header.Treeview.ComponentState then
  9223. FNeedPositionsFix := True
  9224. else
  9225. UpdatePositions;
  9226. // The first column which is created is by definition also the main column.
  9227. if (Count > 0) and (Header.FMainColumn < 0) then
  9228. FHeader.FMainColumn := 0;
  9229. if not (csLoading in Header.Treeview.ComponentState) and not (hsLoading in FHeader.FStates) then
  9230. begin
  9231. with FHeader do
  9232. begin
  9233. if hoAutoResize in FOptions then
  9234. AdjustAutoSize(InvalidColumn);
  9235. if Assigned(Item) then
  9236. Invalidate(Item as TVirtualTreeColumn)
  9237. else
  9238. if Treeview.HandleAllocated then
  9239. begin
  9240. Treeview.UpdateHorizontalScrollBar(False);
  9241. Invalidate(nil);
  9242. Treeview.Invalidate;
  9243. end;
  9244. if not (tsUpdating in Treeview.FStates) then
  9245. // This is mainly to let the designer know when a change occurs at design time which
  9246. // doesn't involve the object inspector (like column resizing with the mouse).
  9247. // This does NOT include design time code as the communication is done via an interface.
  9248. Treeview.UpdateDesigner;
  9249. end;
  9250. end;
  9251. end;
  9252. //----------------------------------------------------------------------------------------------------------------------
  9253. procedure TVirtualTreeColumns.UpdatePositions(Force: Boolean = False);
  9254. // Recalculates the left border of every column and updates their position property according to the
  9255. // PostionToIndex array which primarily determines where each column is placed visually.
  9256. var
  9257. I, RunningPos: Integer;
  9258. begin
  9259. if not FNeedPositionsFix and (Force or (UpdateCount = 0)) then
  9260. begin
  9261. RunningPos := 0;
  9262. for I := 0 to High(FPositionToIndex) do
  9263. with Items[FPositionToIndex[I]] do
  9264. begin
  9265. FPosition := I;
  9266. FLeft := RunningPos;
  9267. if coVisible in FOptions then
  9268. Inc(RunningPos, FWidth);
  9269. end;
  9270. FHeader.Treeview.UpdateHorizontalScrollBar(False);
  9271. end;
  9272. end;
  9273. //----------------------------------------------------------------------------------------------------------------------
  9274. function TVirtualTreeColumns.Add: TVirtualTreeColumn;
  9275. begin
  9276. Result := TVirtualTreeColumn(inherited Add);
  9277. end;
  9278. //----------------------------------------------------------------------------------------------------------------------
  9279. procedure TVirtualTreeColumns.AnimatedResize(Column: TColumnIndex; NewWidth: Integer);
  9280. // Resizes the given column animated by scrolling the window DC.
  9281. var
  9282. OldWidth: Integer;
  9283. DC: HDC;
  9284. I,
  9285. Steps,
  9286. DX: Integer;
  9287. HeaderScrollRect,
  9288. ScrollRect,
  9289. R: TRect;
  9290. NewBrush,
  9291. LastBrush: HBRUSH;
  9292. begin
  9293. if not IsValidColumn(Column) then
  9294. Exit; // Just in case.
  9295. // Make sure the width constrains are considered.
  9296. if NewWidth < Items[Column].FMinWidth then
  9297. NewWidth := Items[Column].FMinWidth;
  9298. if NewWidth > Items[Column].FMaxWidth then
  9299. NewWidth := Items[Column].FMaxWidth;
  9300. OldWidth := Items[Column].Width;
  9301. // Nothing to do if the width is the same.
  9302. if OldWidth <> NewWidth then
  9303. begin
  9304. if not ( (hoDisableAnimatedResize in FHeader.Options) or
  9305. (coDisableAnimatedResize in Items[Column].Options) ) then
  9306. begin
  9307. DC := GetWindowDC(FHeader.Treeview.Handle);
  9308. with FHeader.Treeview do
  9309. try
  9310. Steps := 32;
  9311. DX := (NewWidth - OldWidth) div Steps;
  9312. // Determination of the scroll rectangle is a bit complicated since we neither want
  9313. // to scroll the scrollbars nor the border of the treeview window.
  9314. HeaderScrollRect := FHeaderRect;
  9315. ScrollRect := HeaderScrollRect;
  9316. // Exclude the header itself from scrolling.
  9317. ScrollRect.Top := ScrollRect.Bottom;
  9318. ScrollRect.Bottom := ScrollRect.Top + ClientHeight;
  9319. ScrollRect.Right := ScrollRect.Left + ClientWidth;
  9320. with Items[Column] do
  9321. Inc(ScrollRect.Left, FLeft + FWidth);
  9322. HeaderScrollRect.Left := ScrollRect.Left;
  9323. HeaderScrollRect.Right := ScrollRect.Right;
  9324. // When the new width is larger then avoid artefacts on the left hand side
  9325. // by deleting a small stripe
  9326. if NewWidth > OldWidth then
  9327. begin
  9328. R := ScrollRect;
  9329. NewBrush := CreateSolidBrush(ColorToRGB(Color));
  9330. LastBrush := SelectObject(DC, NewBrush);
  9331. R.Right := R.Left + DX;
  9332. FillRect(DC, R, NewBrush);
  9333. SelectObject(DC, LastBrush);
  9334. DeleteObject(NewBrush);
  9335. end
  9336. else
  9337. begin
  9338. Inc(HeaderScrollRect.Left, DX);
  9339. Inc(ScrollRect.Left, DX);
  9340. end;
  9341. for I := 0 to Steps - 1 do
  9342. begin
  9343. ScrollDC(DC, DX, 0, HeaderScrollRect, HeaderScrollRect, 0, nil);
  9344. Inc(HeaderScrollRect.Left, DX);
  9345. ScrollDC(DC, DX, 0, ScrollRect, ScrollRect, 0, nil);
  9346. Inc(ScrollRect.Left, DX);
  9347. Sleep(1);
  9348. end;
  9349. finally
  9350. ReleaseDC(Handle, DC);
  9351. end;
  9352. end;
  9353. Items[Column].Width := NewWidth;
  9354. end;
  9355. end;
  9356. //----------------------------------------------------------------------------------------------------------------------
  9357. procedure TVirtualTreeColumns.Assign(Source: TPersistent);
  9358. begin
  9359. // Let the collection class assign the items.
  9360. inherited;
  9361. if Source is TVirtualTreeColumns then
  9362. begin
  9363. // Copying the position array is the only needed task here.
  9364. FPositionToIndex := Copy(TVirtualTreeColumns(Source).FPositionToIndex, 0, MaxInt);
  9365. // Make sure the left edges are correct after assignment.
  9366. FNeedPositionsFix := False;
  9367. UpdatePositions(True);
  9368. end;
  9369. end;
  9370. //----------------------------------------------------------------------------------------------------------------------
  9371. procedure TVirtualTreeColumns.Clear;
  9372. begin
  9373. FClearing := True;
  9374. try
  9375. Header.Treeview.CancelEditNode;
  9376. // Since we're freeing all columns, the following have to be true when we're done.
  9377. FHoverIndex := NoColumn;
  9378. FDownIndex := NoColumn;
  9379. FTrackIndex := NoColumn;
  9380. FClickIndex := NoColumn;
  9381. FCheckBoxHit := False;
  9382. with Header do
  9383. if not (hsLoading in FStates) then
  9384. begin
  9385. FAutoSizeIndex := NoColumn;
  9386. FMainColumn := NoColumn;
  9387. FSortColumn := NoColumn;
  9388. end;
  9389. with Header.Treeview do
  9390. if not (csLoading in ComponentState) then
  9391. FFocusedColumn := NoColumn;
  9392. inherited Clear;
  9393. finally
  9394. FClearing := False;
  9395. end;
  9396. end;
  9397. //----------------------------------------------------------------------------------------------------------------------
  9398. function TVirtualTreeColumns.ColumnFromPosition(P: TPoint; Relative: Boolean = True): TColumnIndex;
  9399. // Determines the current column based on the position passed in P.
  9400. var
  9401. I, Sum: Integer;
  9402. begin
  9403. Result := InvalidColumn;
  9404. // The position must be within the header area, but we extend the vertical bounds to the entire treeview area.
  9405. if (P.X >= 0) and (P.Y >= 0) and (P.Y <= FHeader.TreeView.Height) then
  9406. with FHeader, Treeview do
  9407. begin
  9408. if Relative and (P.X >= GetVisibleFixedWidth) then
  9409. Sum := -FEffectiveOffsetX
  9410. else
  9411. Sum := 0;
  9412. if UseRightToLeftAlignment then
  9413. Inc(Sum, ComputeRTLOffset(True));
  9414. for I := 0 to Count - 1 do
  9415. if coVisible in Items[FPositionToIndex[I]].FOptions then
  9416. begin
  9417. Inc(Sum, Items[FPositionToIndex[I]].Width);
  9418. if P.X < Sum then
  9419. begin
  9420. Result := FPositionToIndex[I];
  9421. Break;
  9422. end;
  9423. end;
  9424. end;
  9425. end;
  9426. //----------------------------------------------------------------------------------------------------------------------
  9427. function TVirtualTreeColumns.ColumnFromPosition(PositionIndex: TColumnPosition): TColumnIndex;
  9428. // Returns the index of the column at the given position.
  9429. begin
  9430. if Integer(PositionIndex) < Length(FPositionToIndex) then
  9431. Result := FPositionToIndex[PositionIndex]
  9432. else
  9433. Result := NoColumn;
  9434. end;
  9435. //----------------------------------------------------------------------------------------------------------------------
  9436. function TVirtualTreeColumns.Equals(OtherColumnsObj: TObject): Boolean;
  9437. // Compares itself with the given set of columns and returns True if all published properties are the same
  9438. // (including column order), otherwise False is returned.
  9439. var
  9440. I: Integer;
  9441. OtherColumns : TVirtualTreeColumns;
  9442. begin
  9443. if not (OtherColumnsObj is TVirtualTreeColumns) then
  9444. begin
  9445. Result := False;
  9446. Exit;
  9447. end;
  9448. OtherColumns := TVirtualTreeColumns (OtherColumnsObj);
  9449. // Same number of columns?
  9450. Result := OtherColumns.Count = Count;
  9451. if Result then
  9452. begin
  9453. // Same order of columns?
  9454. Result := CompareMem(Pointer(FPositionToIndex), Pointer(OtherColumns.FPositionToIndex),
  9455. Length(FPositionToIndex) * SizeOf(TColumnIndex));
  9456. if Result then
  9457. begin
  9458. for I := 0 to Count - 1 do
  9459. if not Items[I].Equals(OtherColumns[I]) then
  9460. begin
  9461. Result := False;
  9462. Break;
  9463. end;
  9464. end;
  9465. end;
  9466. end;
  9467. //----------------------------------------------------------------------------------------------------------------------
  9468. procedure TVirtualTreeColumns.GetColumnBounds(Column: TColumnIndex; var Left, Right: Integer);
  9469. // Returns the left and right bound of the given column. If Column is NoColumn then the entire client width is returned.
  9470. begin
  9471. if Column <= NoColumn then
  9472. begin
  9473. Left := 0;
  9474. Right := FHeader.Treeview.ClientWidth;
  9475. end
  9476. else
  9477. begin
  9478. Left := Items[Column].Left;
  9479. Right := Left + Items[Column].Width;
  9480. if FHeader.Treeview.UseRightToLeftAlignment then
  9481. begin
  9482. Inc(Left, FHeader.Treeview.ComputeRTLOffset(True));
  9483. Inc(Right, FHeader.Treeview.ComputeRTLOffset(True));
  9484. end;
  9485. end;
  9486. end;
  9487. //----------------------------------------------------------------------------------------------------------------------
  9488. function TVirtualTreeColumns.GetScrollWidth: Integer;
  9489. // Returns the average width of all visible, non-fixed columns. If there is no such column the indent is returned.
  9490. var
  9491. I: Integer;
  9492. ScrollColumnCount: Integer;
  9493. begin
  9494. Result := 0;
  9495. ScrollColumnCount := 0;
  9496. for I := 0 to FHeader.Columns.Count - 1 do
  9497. begin
  9498. if ([coVisible, coFixed] * FHeader.Columns[I].Options = [coVisible]) then
  9499. begin
  9500. Inc(Result, FHeader.Columns[I].Width);
  9501. Inc(ScrollColumnCount);
  9502. end;
  9503. end;
  9504. if ScrollColumnCount > 0 then // use average width
  9505. Result := Round(Result / ScrollColumnCount)
  9506. else // use indent
  9507. Result := Integer(FHeader.Treeview.FIndent);
  9508. end;
  9509. //----------------------------------------------------------------------------------------------------------------------
  9510. function TVirtualTreeColumns.GetFirstVisibleColumn(ConsiderAllowFocus: Boolean = False): TColumnIndex;
  9511. // Returns the index of the first visible column or "InvalidColumn" if either no columns are defined or
  9512. // all columns are hidden.
  9513. // If ConsiderAllowFocus is True then the column has not only to be visible but also focus has to be allowed.
  9514. var
  9515. I: Integer;
  9516. begin
  9517. Result := InvalidColumn;
  9518. for I := 0 to Count - 1 do
  9519. if (coVisible in Items[FPositionToIndex[I]].FOptions) and
  9520. ( (not ConsiderAllowFocus) or
  9521. (coAllowFocus in Items[FPositionToIndex[I]].FOptions)
  9522. ) then
  9523. begin
  9524. Result := FPositionToIndex[I];
  9525. Break;
  9526. end;
  9527. end;
  9528. //----------------------------------------------------------------------------------------------------------------------
  9529. function TVirtualTreeColumns.GetLastVisibleColumn(ConsiderAllowFocus: Boolean = False): TColumnIndex;
  9530. // Returns the index of the last visible column or "InvalidColumn" if either no columns are defined or
  9531. // all columns are hidden.
  9532. // If ConsiderAllowFocus is True then the column has not only to be visible but also focus has to be allowed.
  9533. var
  9534. I: Integer;
  9535. begin
  9536. Result := InvalidColumn;
  9537. for I := Count - 1 downto 0 do
  9538. if (coVisible in Items[FPositionToIndex[I]].FOptions) and
  9539. ( (not ConsiderAllowFocus) or
  9540. (coAllowFocus in Items[FPositionToIndex[I]].FOptions)
  9541. ) then
  9542. begin
  9543. Result := FPositionToIndex[I];
  9544. Break;
  9545. end;
  9546. end;
  9547. //----------------------------------------------------------------------------------------------------------------------
  9548. function TVirtualTreeColumns.GetFirstColumn: TColumnIndex;
  9549. // Returns the first column in display order.
  9550. begin
  9551. if Count = 0 then
  9552. Result := InvalidColumn
  9553. else
  9554. Result := FPositionToIndex[0];
  9555. end;
  9556. //----------------------------------------------------------------------------------------------------------------------
  9557. function TVirtualTreeColumns.GetNextColumn(Column: TColumnIndex): TColumnIndex;
  9558. // Returns the next column in display order. Column is the index of an item in the collection (a column).
  9559. var
  9560. Position: Integer;
  9561. begin
  9562. if Column < 0 then
  9563. Result := InvalidColumn
  9564. else
  9565. begin
  9566. Position := Items[Column].Position;
  9567. if Position < Count - 1 then
  9568. Result := FPositionToIndex[Position + 1]
  9569. else
  9570. Result := InvalidColumn;
  9571. end;
  9572. end;
  9573. //----------------------------------------------------------------------------------------------------------------------
  9574. function TVirtualTreeColumns.GetNextVisibleColumn(Column: TColumnIndex; ConsiderAllowFocus: Boolean = False): TColumnIndex;
  9575. // Returns the next visible column in display order, Column is an index into the columns list.
  9576. // If ConsiderAllowFocus is True then the column has not only to be visible but also focus has to be allowed.
  9577. begin
  9578. Result := Column;
  9579. repeat
  9580. Result := GetNextColumn(Result);
  9581. until (Result = InvalidColumn) or
  9582. ( (coVisible in Items[Result].FOptions) and
  9583. ( (not ConsiderAllowFocus) or
  9584. (coAllowFocus in Items[Result].FOptions)
  9585. )
  9586. );
  9587. end;
  9588. //----------------------------------------------------------------------------------------------------------------------
  9589. function TVirtualTreeColumns.GetPreviousColumn(Column: TColumnIndex): TColumnIndex;
  9590. // Returns the previous column in display order, Column is an index into the columns list.
  9591. var
  9592. Position: Integer;
  9593. begin
  9594. if Column < 0 then
  9595. Result := InvalidColumn
  9596. else
  9597. begin
  9598. Position := Items[Column].Position;
  9599. if Position > 0 then
  9600. Result := FPositionToIndex[Position - 1]
  9601. else
  9602. Result := InvalidColumn;
  9603. end;
  9604. end;
  9605. //----------------------------------------------------------------------------------------------------------------------
  9606. function TVirtualTreeColumns.GetPreviousVisibleColumn(Column: TColumnIndex; ConsiderAllowFocus: Boolean = False): TColumnIndex;
  9607. // Returns the previous visible column in display order, Column is an index into the columns list.
  9608. // If ConsiderAllowFocus is True then the column has not only to be visible but also focus has to be allowed.
  9609. begin
  9610. Result := Column;
  9611. repeat
  9612. Result := GetPreviousColumn(Result);
  9613. until (Result = InvalidColumn) or
  9614. ( (coVisible in Items[Result].FOptions) and
  9615. ( (not ConsiderAllowFocus) or
  9616. (coAllowFocus in Items[Result].FOptions)
  9617. )
  9618. );
  9619. end;
  9620. //----------------------------------------------------------------------------------------------------------------------
  9621. function TVirtualTreeColumns.GetVisibleColumns: TColumnsArray;
  9622. // Returns a list of all currently visible columns in actual order.
  9623. var
  9624. I, Counter: Integer;
  9625. begin
  9626. SetLength(Result, Count);
  9627. Counter := 0;
  9628. for I := 0 to Count - 1 do
  9629. if coVisible in Items[FPositionToIndex[I]].FOptions then
  9630. begin
  9631. Result[Counter] := Items[FPositionToIndex[I]];
  9632. Inc(Counter);
  9633. end;
  9634. // Set result length to actual visible count.
  9635. SetLength(Result, Counter);
  9636. end;
  9637. //----------------------------------------------------------------------------------------------------------------------
  9638. function TVirtualTreeColumns.GetVisibleFixedWidth: Integer;
  9639. // Determines the horizontal space all visible and fixed columns occupy.
  9640. var
  9641. I: Integer;
  9642. begin
  9643. Result := 0;
  9644. for I := 0 to Count - 1 do
  9645. begin
  9646. if Items[I].Options * [coVisible, coFixed] = [coVisible, coFixed] then
  9647. Inc(Result, Items[I].Width);
  9648. end;
  9649. end;
  9650. //----------------------------------------------------------------------------------------------------------------------
  9651. function TVirtualTreeColumns.IsValidColumn(Column: TColumnIndex): Boolean;
  9652. // Determines whether the given column is valid or not, that is, whether it is one of the current columns.
  9653. begin
  9654. Result := (Column > NoColumn) and (Column < Count);
  9655. end;
  9656. //----------------------------------------------------------------------------------------------------------------------
  9657. procedure TVirtualTreeColumns.LoadFromStream(const Stream: TStream; Version: Integer);
  9658. var
  9659. I,
  9660. ItemCount: Integer;
  9661. begin
  9662. Clear;
  9663. Stream.ReadBuffer(ItemCount, SizeOf(ItemCount));
  9664. // number of columns
  9665. if ItemCount > 0 then
  9666. begin
  9667. BeginUpdate;
  9668. try
  9669. for I := 0 to ItemCount - 1 do
  9670. Add.LoadFromStream(Stream, Version);
  9671. SetLength(FPositionToIndex, ItemCount);
  9672. Stream.ReadBuffer(FPositionToIndex[0], ItemCount * SizeOf(TColumnIndex));
  9673. UpdatePositions(True);
  9674. finally
  9675. EndUpdate;
  9676. end;
  9677. end;
  9678. // Data introduced with header stream version 5
  9679. if Version > 4 then
  9680. Stream.ReadBuffer(FDefaultWidth, SizeOf(FDefaultWidth));
  9681. end;
  9682. //----------------------------------------------------------------------------------------------------------------------
  9683. procedure TVirtualTreeColumns.PaintHeader(DC: HDC; R: TRect; HOffset: Integer);
  9684. // Backward compatible header paint method. This method takes care of visually moving floating columns
  9685. var
  9686. VisibleFixedWidth: Integer;
  9687. RTLOffset: Integer;
  9688. procedure PaintFixedArea;
  9689. begin
  9690. if VisibleFixedWidth > 0 then
  9691. PaintHeader(FHeaderBitmap.Canvas,
  9692. Rect(0, 0, Min(R.Right, VisibleFixedWidth), R.Bottom - R.Top),
  9693. Point(R.Left, R.Top), RTLOffset);
  9694. end;
  9695. begin
  9696. // Adjust size of the header bitmap
  9697. with TWithSafeRect(FHeader.Treeview.FHeaderRect) do
  9698. begin
  9699. FHeaderBitmap.Width := Max(Right, R.Right - R.Left);
  9700. FHeaderBitmap.Height := Bottom;
  9701. end;
  9702. VisibleFixedWidth := GetVisibleFixedWidth;
  9703. // Consider right-to-left directionality.
  9704. if FHeader.TreeView.UseRightToLeftAlignment then
  9705. RTLOffset := FHeader.Treeview.ComputeRTLOffset
  9706. else
  9707. RTLOffset := 0;
  9708. if RTLOffset = 0 then
  9709. PaintFixedArea;
  9710. // Paint the floating part of the header.
  9711. PaintHeader(FHeaderBitmap.Canvas,
  9712. Rect(VisibleFixedWidth - HOffset, 0, R.Right + VisibleFixedWidth - HOffset, R.Bottom - R.Top),
  9713. Point(R.Left + VisibleFixedWidth, R.Top), RTLOffset);
  9714. // In case of right-to-left directionality we paint the fixed part last.
  9715. if RTLOffset <> 0 then
  9716. PaintFixedArea;
  9717. // Blit the result to target.
  9718. with TWithSafeRect(R) do
  9719. BitBlt(DC, Left, Top, Right - Left, Bottom - Top, FHeaderBitmap.Canvas.Handle, Left, Top, SRCCOPY);
  9720. end;
  9721. //----------------------------------------------------------------------------------------------------------------------
  9722. procedure TVirtualTreeColumns.PaintHeader(TargetCanvas: TCanvas; R: TRect; const Target: TPoint;
  9723. RTLOffset: Integer = 0);
  9724. // Main paint method to draw the header.
  9725. // This procedure will paint the a slice (given in R) out of HeaderRect into TargetCanvas starting at position Target.
  9726. // This function does not offer the option to visually move floating columns due to scrolling. To accomplish this you
  9727. // need to call this method twice.
  9728. const
  9729. SortGlyphs: array[TSortDirection, Boolean] of Integer = ( // ascending/descending, normal/XP style
  9730. (3, 5) {ascending}, (2, 4) {descending}
  9731. );
  9732. var
  9733. Run: TColumnIndex;
  9734. RightBorderFlag,
  9735. NormalButtonStyle,
  9736. NormalButtonFlags,
  9737. PressedButtonStyle,
  9738. PressedButtonFlags,
  9739. RaisedButtonStyle,
  9740. RaisedButtonFlags: Cardinal;
  9741. Images: TCustomImageList;
  9742. OwnerDraw,
  9743. AdvancedOwnerDraw: Boolean;
  9744. PaintInfo: THeaderPaintInfo;
  9745. RequestedElements,
  9746. ActualElements: THeaderPaintElements;
  9747. //--------------- local functions -------------------------------------------
  9748. procedure PrepareButtonStyles;
  9749. // Prepare the button styles and flags for later usage.
  9750. begin
  9751. RaisedButtonStyle := 0;
  9752. RaisedButtonFlags := 0;
  9753. case FHeader.Style of
  9754. hsThickButtons:
  9755. begin
  9756. NormalButtonStyle := BDR_RAISEDINNER or BDR_RAISEDOUTER;
  9757. NormalButtonFlags := BF_LEFT or BF_TOP or BF_BOTTOM or BF_MIDDLE or BF_SOFT or BF_ADJUST;
  9758. PressedButtonStyle := BDR_RAISEDINNER or BDR_RAISEDOUTER;
  9759. PressedButtonFlags := NormalButtonFlags or BF_RIGHT or BF_FLAT or BF_ADJUST;
  9760. end;
  9761. hsFlatButtons:
  9762. begin
  9763. NormalButtonStyle := BDR_RAISEDINNER;
  9764. NormalButtonFlags := BF_LEFT or BF_TOP or BF_BOTTOM or BF_MIDDLE or BF_ADJUST;
  9765. PressedButtonStyle := BDR_SUNKENOUTER;
  9766. PressedButtonFlags := BF_RECT or BF_MIDDLE or BF_ADJUST;
  9767. end;
  9768. else
  9769. // hsPlates or hsXPStyle, values are not used in the latter case
  9770. begin
  9771. NormalButtonStyle := BDR_RAISEDINNER;
  9772. NormalButtonFlags := BF_RECT or BF_MIDDLE or BF_SOFT or BF_ADJUST;
  9773. PressedButtonStyle := BDR_SUNKENOUTER;
  9774. PressedButtonFlags := BF_RECT or BF_MIDDLE or BF_ADJUST;
  9775. RaisedButtonStyle := BDR_RAISEDINNER;
  9776. RaisedButtonFlags := BF_LEFT or BF_TOP or BF_BOTTOM or BF_MIDDLE or BF_ADJUST;
  9777. end;
  9778. end;
  9779. end;
  9780. //---------------------------------------------------------------------------
  9781. procedure DrawBackground;
  9782. // Draw the header background.
  9783. var
  9784. BackgroundRect: TRect;
  9785. Details: TThemedElementDetails;
  9786. begin
  9787. BackgroundRect := Rect(Target.X, Target.Y, Target.X + R.Right - R.Left, Target.Y + FHeader.Height);
  9788. with TargetCanvas do
  9789. begin
  9790. if hpeBackground in RequestedElements then
  9791. begin
  9792. PaintInfo.PaintRectangle := BackgroundRect;
  9793. FHeader.Treeview.DoAdvancedHeaderDraw(PaintInfo, [hpeBackground]);
  9794. end
  9795. else
  9796. begin
  9797. if tsUseThemes in FHeader.Treeview.FStates then
  9798. begin
  9799. Details := StyleServices.GetElementDetails(thHeaderItemRightNormal);
  9800. StyleServices.DrawElement(Handle, Details, BackgroundRect, @BackgroundRect);
  9801. end
  9802. else
  9803. begin
  9804. Brush.Color := FHeader.FBackground;
  9805. FillRect(BackgroundRect);
  9806. end;
  9807. end;
  9808. end;
  9809. end;
  9810. //---------------------------------------------------------------------------
  9811. procedure PaintColumnHeader(AColumn: TColumnIndex; ATargetRect: TRect);
  9812. // Draw a single column to TargetRect. The clipping rect needs to be set before
  9813. // this procedure is called.
  9814. var
  9815. Y: Integer;
  9816. SavedDC: Integer;
  9817. ColCaptionText: UnicodeString;
  9818. ColImageInfo: TVTImageInfo;
  9819. SortIndex: Integer;
  9820. SortGlyphSize: TSize;
  9821. Glyph: TThemedHeader;
  9822. Details: TThemedElementDetails;
  9823. WrapCaption: Boolean;
  9824. DrawFormat: Cardinal;
  9825. Pos: TRect;
  9826. DrawHot: Boolean;
  9827. ImageWidth: Integer;
  9828. begin
  9829. ColImageInfo.Ghosted := False;
  9830. PaintInfo.Column := Items[AColumn];
  9831. with PaintInfo, Column do
  9832. begin
  9833. IsHoverIndex := (AColumn = FHoverIndex) and (hoHotTrack in FHeader.FOptions) and (coEnabled in FOptions);
  9834. IsDownIndex := (AColumn = FDownIndex) and not FCheckBoxHit;
  9835. if (coShowDropMark in FOptions) and (AColumn = FDropTarget) and (AColumn <> FDragIndex) then
  9836. begin
  9837. if FDropBefore then
  9838. DropMark := dmmLeft
  9839. else
  9840. DropMark := dmmRight;
  9841. end
  9842. else
  9843. DropMark := dmmNone;
  9844. IsEnabled := (coEnabled in FOptions) and (FHeader.Treeview.Enabled);
  9845. ShowHeaderGlyph := (hoShowImages in FHeader.FOptions) and ((Assigned(Images) and (FImageIndex > -1)) or FCheckBox);
  9846. ShowSortGlyph := (AColumn = FHeader.FSortColumn) and (hoShowSortGlyphs in FHeader.FOptions);
  9847. WrapCaption := coWrapCaption in FOptions;
  9848. PaintRectangle := ATargetRect;
  9849. // This path for text columns or advanced owner draw.
  9850. if (Style = vsText) or not OwnerDraw or AdvancedOwnerDraw then
  9851. begin
  9852. // See if the application wants to draw part of the header itself.
  9853. RequestedElements := [];
  9854. if AdvancedOwnerDraw then
  9855. begin
  9856. PaintInfo.Column := Items[AColumn];
  9857. FHeader.Treeview.DoHeaderDrawQueryElements(PaintInfo, RequestedElements);
  9858. end;
  9859. if ShowRightBorder or (AColumn < Count - 1) then
  9860. RightBorderFlag := BF_RIGHT
  9861. else
  9862. RightBorderFlag := 0;
  9863. if hpeBackground in RequestedElements then
  9864. FHeader.Treeview.DoAdvancedHeaderDraw(PaintInfo, [hpeBackground])
  9865. else
  9866. begin
  9867. if tsUseThemes in FHeader.Treeview.FStates then
  9868. begin
  9869. if IsDownIndex then
  9870. Details := StyleServices.GetElementDetails(thHeaderItemPressed)
  9871. else
  9872. if IsHoverIndex then
  9873. Details := StyleServices.GetElementDetails(thHeaderItemHot)
  9874. else
  9875. Details := StyleServices.GetElementDetails(thHeaderItemNormal);
  9876. StyleServices.DrawElement(TargetCanvas.Handle, Details, PaintRectangle, @PaintRectangle);
  9877. end
  9878. else
  9879. begin
  9880. if IsDownIndex then
  9881. DrawEdge(TargetCanvas.Handle, PaintRectangle, PressedButtonStyle, PressedButtonFlags)
  9882. else
  9883. // Plates have the special case of raising on mouse over.
  9884. if (FHeader.Style = hsPlates) and IsHoverIndex and
  9885. (coAllowClick in FOptions) and (coEnabled in FOptions) then
  9886. DrawEdge(TargetCanvas.Handle, PaintRectangle, RaisedButtonStyle,
  9887. RaisedButtonFlags or RightBorderFlag)
  9888. else
  9889. DrawEdge(TargetCanvas.Handle, PaintRectangle, NormalButtonStyle,
  9890. NormalButtonFlags or RightBorderFlag);
  9891. end;
  9892. end;
  9893. PaintRectangle := ATargetRect;
  9894. // calculate text and glyph position
  9895. InflateRect(PaintRectangle, -2, -2);
  9896. DrawFormat := DT_TOP or DT_NOPREFIX;
  9897. case CaptionAlignment of
  9898. taLeftJustify : DrawFormat := DrawFormat or DT_LEFT;
  9899. taRightJustify : DrawFormat := DrawFormat or DT_RIGHT;
  9900. taCenter : DrawFormat := DrawFormat or DT_CENTER;
  9901. end;
  9902. if UseRightToLeftReading then
  9903. DrawFormat := DrawFormat + DT_RTLREADING;
  9904. ComputeHeaderLayout(TargetCanvas.Handle, PaintRectangle, ShowHeaderGlyph, ShowSortGlyph, GlyphPos,
  9905. SortGlyphPos, SortGlyphSize, TextRectangle, DrawFormat);
  9906. // Move glyph and text one pixel to the right and down to simulate a pressed button.
  9907. if IsDownIndex then
  9908. begin
  9909. OffsetRect(TextRectangle, 1, 1);
  9910. Inc(GlyphPos.X);
  9911. Inc(GlyphPos.Y);
  9912. Inc(SortGlyphPos.X);
  9913. Inc(SortGlyphPos.Y);
  9914. end;
  9915. // Advanced owner draw allows to paint elements, which would normally not be painted (because of space
  9916. // limitations, empty captions etc.).
  9917. ActualElements := RequestedElements * [hpeHeaderGlyph, hpeSortGlyph, hpeDropMark, hpeText];
  9918. // main glyph
  9919. FHasImage := False;
  9920. if Assigned(Images) then
  9921. ImageWidth := Images.Width
  9922. else
  9923. ImageWidth := 0;
  9924. if not (hpeHeaderGlyph in ActualElements) and ShowHeaderGlyph and
  9925. (not ShowSortGlyph or (FBiDiMode <> bdLeftToRight) or (GlyphPos.X + ImageWidth <= SortGlyphPos.X) ) then
  9926. begin
  9927. if not FCheckBox then
  9928. begin
  9929. ColImageInfo.Images := Images;
  9930. Images.Draw(TargetCanvas, GlyphPos.X, GlyphPos.Y, FImageIndex, IsEnabled);
  9931. end
  9932. else
  9933. begin
  9934. with Header.Treeview do
  9935. begin
  9936. ColImageInfo.Images := GetCheckImageListFor(CheckImageKind);
  9937. ColImageInfo.Index := GetCheckImage(nil, FCheckType, FCheckState, IsEnabled);
  9938. ColImageInfo.XPos := GlyphPos.X;
  9939. ColImageInfo.YPos := GlyphPos.Y;
  9940. PaintCheckImage(TargetCanvas, ColImageInfo, False);
  9941. end;
  9942. end;
  9943. FHasImage := True;
  9944. with TWithSafeRect(FImageRect) do
  9945. begin
  9946. Left := GlyphPos.X;
  9947. Top := GlyphPos.Y;
  9948. Right := Left + ColImageInfo.Images.Width;
  9949. Bottom := Top + ColImageInfo.Images.Height;
  9950. end;
  9951. end;
  9952. // caption
  9953. if WrapCaption then
  9954. ColCaptionText := FCaptionText
  9955. else
  9956. ColCaptionText := Text;
  9957. if IsHoverIndex and FHeader.Treeview.VclStyleEnabled then
  9958. DrawHot := True
  9959. else
  9960. DrawHot := (IsHoverIndex and (hoHotTrack in FHeader.FOptions) and not(tsUseThemes in FHeader.Treeview.FStates));
  9961. if not(hpeText in ActualElements) and (Length(Text) > 0) then
  9962. DrawButtonText(TargetCanvas.Handle, ColCaptionText, TextRectangle, IsEnabled, DrawHot, DrawFormat, WrapCaption);
  9963. // sort glyph
  9964. if not (hpeSortGlyph in ActualElements) and ShowSortGlyph then
  9965. begin
  9966. if tsUseExplorerTheme in FHeader.Treeview.FStates then
  9967. begin
  9968. Pos.TopLeft := SortGlyphPos;
  9969. Pos.Right := Pos.Left + SortGlyphSize.cx;
  9970. Pos.Bottom := Pos.Top + SortGlyphSize.cy;
  9971. if FHeader.FSortDirection = sdAscending then
  9972. Glyph := thHeaderSortArrowSortedUp
  9973. else
  9974. Glyph := thHeaderSortArrowSortedDown;
  9975. Details := StyleServices.GetElementDetails(Glyph);
  9976. StyleServices.DrawElement(TargetCanvas.Handle, Details, Pos, @Pos);
  9977. end
  9978. else
  9979. begin
  9980. SortIndex := SortGlyphs[FHeader.FSortDirection, tsUseThemes in FHeader.Treeview.FStates];
  9981. UtilityImages.Draw(TargetCanvas, SortGlyphPos.X, SortGlyphPos.Y, SortIndex);
  9982. end;
  9983. end;
  9984. // Show an indication if this column is the current drop target in a header drag operation.
  9985. if not (hpeDropMark in ActualElements) and (DropMark <> dmmNone) then
  9986. begin
  9987. Y := (PaintRectangle.Top + PaintRectangle.Bottom - UtilityImages.Height) div 2;
  9988. if DropMark = dmmLeft then
  9989. UtilityImages.Draw(TargetCanvas, PaintRectangle.Left, Y, 0)
  9990. else
  9991. UtilityImages.Draw(TargetCanvas, PaintRectangle.Right - 16 , Y, 1);
  9992. end;
  9993. if ActualElements <> [] then
  9994. begin
  9995. SavedDC := SaveDC(TargetCanvas.Handle);
  9996. FHeader.Treeview.DoAdvancedHeaderDraw(PaintInfo, ActualElements);
  9997. RestoreDC(TargetCanvas.Handle, SavedDC);
  9998. end;
  9999. end
  10000. else // Let application draw the header.
  10001. FHeader.Treeview.DoHeaderDraw(TargetCanvas, Items[AColumn], PaintRectangle, IsHoverIndex, IsDownIndex,
  10002. DropMark);
  10003. end;
  10004. end;
  10005. //--------------- end local functions ---------------------------------------
  10006. var
  10007. TargetRect: TRect;
  10008. MaxX: Integer;
  10009. begin
  10010. if IsRectEmpty(R) then
  10011. Exit;
  10012. // If both draw posibillities are specified then prefer the advanced way.
  10013. AdvancedOwnerDraw := (hoOwnerDraw in FHeader.FOptions) and Assigned(FHeader.Treeview.FOnAdvancedHeaderDraw) and
  10014. Assigned(FHeader.Treeview.FOnHeaderDrawQueryElements) and not (csDesigning in FHeader.Treeview.ComponentState);
  10015. OwnerDraw := (hoOwnerDraw in FHeader.FOptions) and Assigned(FHeader.Treeview.FOnHeaderDraw) and
  10016. not (csDesigning in FHeader.Treeview.ComponentState) and not AdvancedOwnerDraw;
  10017. ZeroMemory(@PaintInfo, SizeOf(PaintInfo));
  10018. PaintInfo.TargetCanvas := TargetCanvas;
  10019. with PaintInfo, TargetCanvas do
  10020. begin
  10021. // Use shortcuts for the images and the font.
  10022. Images := FHeader.FImages;
  10023. Font := FHeader.FFont;
  10024. PrepareButtonStyles;
  10025. // At first, query the application which parts of the header it wants to draw on its own.
  10026. RequestedElements := [];
  10027. if AdvancedOwnerDraw then
  10028. begin
  10029. PaintRectangle := R;
  10030. Column := nil;
  10031. FHeader.Treeview.DoHeaderDrawQueryElements(PaintInfo, RequestedElements);
  10032. end;
  10033. // Draw the background.
  10034. DrawBackground;
  10035. // Now that we have drawn the background, we apply the header's dimensions to R.
  10036. R := Rect(Max(R.Left, 0), Max(R.Top, 0), Min(R.Right, TotalWidth), Min(R.Bottom, Header.Height));
  10037. // Determine where to stop.
  10038. MaxX := Target.X + R.Right - R.Left;
  10039. // Determine the start column.
  10040. Run := ColumnFromPosition(Point(R.Left + RTLOffset, 0), False);
  10041. if Run <= NoColumn then
  10042. Exit;
  10043. TargetRect.Top := Target.Y;
  10044. TargetRect.Bottom := Target.Y + R.Bottom - R.Top;
  10045. TargetRect.Left := Target.X - R.Left + Items[Run].FLeft + RTLOffset;
  10046. // TargetRect.Right will be set in the loop
  10047. ShowRightBorder := (FHeader.Style = hsThickButtons) or not (hoAutoResize in FHeader.FOptions) or
  10048. (FHeader.Treeview.BevelKind = bkNone);
  10049. // Now go for each button.
  10050. while (Run > NoColumn) and (TargetRect.Left < MaxX) do
  10051. begin
  10052. TargetRect.Right := TargetRect.Left + Items[Run].FWidth;
  10053. // create a clipping rect to limit painting to button area
  10054. ClipCanvas(TargetCanvas, Rect(Max(TargetRect.Left, Target.X), Target.Y + R.Top,
  10055. Min(TargetRect.Right, MaxX), TargetRect.Bottom));
  10056. PaintColumnHeader(Run, TargetRect);
  10057. SelectClipRgn(Handle, 0);
  10058. TargetRect.Left := TargetRect.Right;
  10059. Run := GetNextVisibleColumn(Run);
  10060. end;
  10061. end;
  10062. end;
  10063. //----------------------------------------------------------------------------------------------------------------------
  10064. procedure TVirtualTreeColumns.SaveToStream(const Stream: TStream);
  10065. var
  10066. I: Integer;
  10067. begin
  10068. I := Count;
  10069. Stream.WriteBuffer(I, SizeOf(I));
  10070. if I > 0 then
  10071. begin
  10072. for I := 0 to Count - 1 do
  10073. TVirtualTreeColumn(Items[I]).SaveToStream(Stream);
  10074. Stream.WriteBuffer(FPositionToIndex[0], Count * SizeOf(TColumnIndex));
  10075. end;
  10076. // Data introduced with header stream version 5.
  10077. Stream.WriteBuffer(DefaultWidth, SizeOf(DefaultWidth));
  10078. end;
  10079. //----------------------------------------------------------------------------------------------------------------------
  10080. function TVirtualTreeColumns.TotalWidth: Integer;
  10081. var
  10082. LastColumn: TColumnIndex;
  10083. begin
  10084. Result := 0;
  10085. if (Count > 0) and (Length(FPositionToIndex) > 0) then
  10086. begin
  10087. LastColumn := FPositionToIndex[Count - 1];
  10088. if not (coVisible in Items[LastColumn].FOptions) then
  10089. LastColumn := GetPreviousVisibleColumn(LastColumn);
  10090. if LastColumn > NoColumn then
  10091. with Items[LastColumn] do
  10092. Result := FLeft + FWidth;
  10093. end;
  10094. end;
  10095. //----------------- TVTFixedAreaConstraints ----------------------------------------------------------------------------
  10096. constructor TVTFixedAreaConstraints.Create(AOwner: TVTHeader);
  10097. begin
  10098. inherited Create;
  10099. FHeader := AOwner;
  10100. end;
  10101. //----------------------------------------------------------------------------------------------------------------------
  10102. procedure TVTFixedAreaConstraints.SetConstraints(Index: Integer; Value: TVTConstraintPercent);
  10103. begin
  10104. case Index of
  10105. 0:
  10106. if Value <> FMaxHeightPercent then
  10107. begin
  10108. FMaxHeightPercent := Value;
  10109. if (Value > 0) and (Value < FMinHeightPercent) then
  10110. FMinHeightPercent := Value;
  10111. Change;
  10112. end;
  10113. 1:
  10114. if Value <> FMaxWidthPercent then
  10115. begin
  10116. FMaxWidthPercent := Value;
  10117. if (Value > 0) and (Value < FMinWidthPercent) then
  10118. FMinWidthPercent := Value;
  10119. Change;
  10120. end;
  10121. 2:
  10122. if Value <> FMinHeightPercent then
  10123. begin
  10124. FMinHeightPercent := Value;
  10125. if (FMaxHeightPercent > 0) and (Value > FMaxHeightPercent) then
  10126. FMaxHeightPercent := Value;
  10127. Change;
  10128. end;
  10129. 3:
  10130. if Value <> FMinWidthPercent then
  10131. begin
  10132. FMinWidthPercent := Value;
  10133. if (FMaxWidthPercent > 0) and (Value > FMaxWidthPercent) then
  10134. FMaxWidthPercent := Value;
  10135. Change;
  10136. end;
  10137. end;
  10138. end;
  10139. //----------------------------------------------------------------------------------------------------------------------
  10140. procedure TVTFixedAreaConstraints.Change;
  10141. begin
  10142. if Assigned(FOnChange) then
  10143. FOnChange(Self);
  10144. end;
  10145. //----------------------------------------------------------------------------------------------------------------------
  10146. procedure TVTFixedAreaConstraints.Assign(Source: TPersistent);
  10147. begin
  10148. if Source is TVTFixedAreaConstraints then
  10149. begin
  10150. FMaxHeightPercent := TVTFixedAreaConstraints(Source).FMaxHeightPercent;
  10151. FMaxWidthPercent := TVTFixedAreaConstraints(Source).FMaxWidthPercent;
  10152. FMinHeightPercent := TVTFixedAreaConstraints(Source).FMinHeightPercent;
  10153. FMinWidthPercent := TVTFixedAreaConstraints(Source).FMinWidthPercent;
  10154. Change;
  10155. end
  10156. else
  10157. inherited;
  10158. end;
  10159. //----------------- TVTHeader -----------------------------------------------------------------------------------------
  10160. constructor TVTHeader.Create(AOwner: TBaseVirtualTree);
  10161. begin
  10162. inherited Create;
  10163. FOwner := AOwner;
  10164. FColumns := GetColumnsClass.Create(Self);
  10165. FHeight := 19;
  10166. FDefaultHeight := FHeight;
  10167. FMinHeight := 10;
  10168. FMaxHeight := 10000;
  10169. FFont := TFont.Create;
  10170. FFont.OnChange := FontChanged;
  10171. FParentFont := False;
  10172. FBackground := clBtnFace;
  10173. FOptions := [hoColumnResize, hoDrag, hoShowSortGlyphs];
  10174. FImageChangeLink := TChangeLink.Create;
  10175. FImageChangeLink.OnChange := ImageListChange;
  10176. FSortColumn := NoColumn;
  10177. FSortDirection := sdAscending;
  10178. FMainColumn := NoColumn;
  10179. FDragImage := TVTDragImage.Create(AOwner);
  10180. with FDragImage do
  10181. begin
  10182. Fade := False;
  10183. PostBlendBias := 0;
  10184. PreBlendBias := -50;
  10185. Transparency := 140;
  10186. end;
  10187. FFixedAreaConstraints := TVTFixedAreaConstraints.Create(Self);
  10188. FFixedAreaConstraints.OnChange := FixedAreaConstraintsChanged;
  10189. end;
  10190. //----------------------------------------------------------------------------------------------------------------------
  10191. destructor TVTHeader.Destroy;
  10192. begin
  10193. FDragImage.Free;
  10194. FFixedAreaConstraints.Free;
  10195. FImageChangeLink.Free;
  10196. FFont.Free;
  10197. FColumns.Clear; // TCollection's Clear method is not virtual, so we have to call our own Clear method manually.
  10198. FColumns.Free;
  10199. inherited;
  10200. end;
  10201. //----------------------------------------------------------------------------------------------------------------------
  10202. procedure TVTHeader.FontChanged(Sender: TObject);
  10203. var
  10204. I: Integer;
  10205. lMaxHeight: Integer;
  10206. begin
  10207. if toAutoChangeScale in Treeview.TreeOptions.AutoOptions then
  10208. begin
  10209. // Find the largest Columns[].Spacing
  10210. lMaxHeight := 0;
  10211. for I := 0 to Self.Columns.Count - 1 do
  10212. lMaxHeight := Max(lMaxHeight, Columns[I].Spacing);
  10213. // Calculate the required size based on the font, this is important as the use migth just vave increased the size of the icon font
  10214. with TBitmap.Create do
  10215. try
  10216. Canvas.Font.Assign(FFont);
  10217. lMaxHeight := lMaxHeight {top spacing} + (lMaxHeight div 2) {minimum bottom spacing} + Canvas.TextHeight('Q');
  10218. finally
  10219. Free;
  10220. end;
  10221. // Get the maximum of the scaled original value an
  10222. lMaxHeight := Max(lMaxHeight, FHeight);
  10223. // Set the calculated size
  10224. Self.SetHeight(lMaxHeight);
  10225. end;
  10226. Invalidate(nil);
  10227. end;
  10228. //----------------------------------------------------------------------------------------------------------------------
  10229. function TVTHeader.GetMainColumn: TColumnIndex;
  10230. begin
  10231. if FColumns.Count > 0 then
  10232. Result := FMainColumn
  10233. else
  10234. Result := NoColumn;
  10235. end;
  10236. //----------------------------------------------------------------------------------------------------------------------
  10237. function TVTHeader.GetUseColumns: Boolean;
  10238. begin
  10239. Result := FColumns.Count > 0;
  10240. end;
  10241. //----------------------------------------------------------------------------------------------------------------------
  10242. function TVTHeader.IsFontStored: Boolean;
  10243. begin
  10244. Result := not ParentFont;
  10245. end;
  10246. //----------------------------------------------------------------------------------------------------------------------
  10247. procedure TVTHeader.SetAutoSizeIndex(Value: TColumnIndex);
  10248. begin
  10249. if FAutoSizeIndex <> Value then
  10250. begin
  10251. FAutoSizeIndex := Value;
  10252. if hoAutoResize in FOptions then
  10253. Columns.AdjustAutoSize(InvalidColumn);
  10254. end;
  10255. end;
  10256. //----------------------------------------------------------------------------------------------------------------------
  10257. procedure TVTHeader.SetBackground(Value: TColor);
  10258. begin
  10259. if FBackground <> Value then
  10260. begin
  10261. FBackground := Value;
  10262. Invalidate(nil);
  10263. end;
  10264. end;
  10265. //----------------------------------------------------------------------------------------------------------------------
  10266. procedure TVTHeader.SetColumns(Value: TVirtualTreeColumns);
  10267. begin
  10268. FColumns.Assign(Value);
  10269. end;
  10270. //----------------------------------------------------------------------------------------------------------------------
  10271. procedure TVTHeader.SetDefaultHeight(Value: Integer);
  10272. begin
  10273. if Value < FMinHeight then
  10274. Value := FMinHeight;
  10275. if Value > FMaxHeight then
  10276. Value := FMaxHeight;
  10277. if FHeight = FDefaultHeight then
  10278. SetHeight(Value);
  10279. FDefaultHeight := Value;
  10280. end;
  10281. //----------------------------------------------------------------------------------------------------------------------
  10282. procedure TVTHeader.SetFont(const Value: TFont);
  10283. begin
  10284. FFont.Assign(Value);
  10285. FParentFont := False;
  10286. end;
  10287. //----------------------------------------------------------------------------------------------------------------------
  10288. procedure TVTHeader.SetHeight(Value: Integer);
  10289. var
  10290. RelativeMaxHeight,
  10291. RelativeMinHeight,
  10292. EffectiveMaxHeight,
  10293. EffectiveMinHeight: Integer;
  10294. begin
  10295. if not TreeView.HandleAllocated then
  10296. begin
  10297. FHeight := Value;
  10298. Include(FStates, hsNeedScaling);
  10299. end
  10300. else
  10301. begin
  10302. with FFixedAreaConstraints do
  10303. begin
  10304. RelativeMaxHeight := ((Treeview.ClientHeight + FHeight) * FMaxHeightPercent) div 100;
  10305. RelativeMinHeight := ((Treeview.ClientHeight + FHeight) * FMinHeightPercent) div 100;
  10306. EffectiveMinHeight := IfThen(FMaxHeightPercent > 0, Min(RelativeMaxHeight, FMinHeight), FMinHeight);
  10307. EffectiveMaxHeight := IfThen(FMinHeightPercent > 0, Max(RelativeMinHeight, FMaxHeight), FMaxHeight);
  10308. Value := Min(Max(Value, EffectiveMinHeight), EffectiveMaxHeight);
  10309. if FMinHeightPercent > 0 then
  10310. Value := Max(RelativeMinHeight, Value);
  10311. if FMaxHeightPercent > 0 then
  10312. Value := Min(RelativeMaxHeight, Value);
  10313. end;
  10314. if FHeight <> Value then
  10315. begin
  10316. FHeight := Value;
  10317. if not (csLoading in Treeview.ComponentState) and not (hsScaling in FStates) then
  10318. RecalculateHeader;
  10319. Treeview.Invalidate;
  10320. UpdateWindow(Treeview.Handle);
  10321. end;
  10322. end;
  10323. end;
  10324. //----------------------------------------------------------------------------------------------------------------------
  10325. procedure TVTHeader.SetImages(const Value: TCustomImageList);
  10326. begin
  10327. if FImages <> Value then
  10328. begin
  10329. if Assigned(FImages) then
  10330. begin
  10331. FImages.UnRegisterChanges(FImageChangeLink);
  10332. FImages.RemoveFreeNotification(FOwner);
  10333. end;
  10334. FImages := Value;
  10335. if Assigned(FImages) then
  10336. begin
  10337. FImages.RegisterChanges(FImageChangeLink);
  10338. FImages.FreeNotification(FOwner);
  10339. end;
  10340. if not (csLoading in Treeview.ComponentState) then
  10341. Invalidate(nil);
  10342. end;
  10343. end;
  10344. //----------------------------------------------------------------------------------------------------------------------
  10345. procedure TVTHeader.SetMainColumn(Value: TColumnIndex);
  10346. begin
  10347. if csLoading in Treeview.ComponentState then
  10348. FMainColumn := Value
  10349. else
  10350. begin
  10351. if Value < 0 then
  10352. Value := 0;
  10353. if Value > FColumns.Count - 1 then
  10354. Value := FColumns.Count - 1;
  10355. if Value <> FMainColumn then
  10356. begin
  10357. FMainColumn := Value;
  10358. if not (csLoading in Treeview.ComponentState) then
  10359. begin
  10360. Treeview.MainColumnChanged;
  10361. if not (toExtendedFocus in Treeview.FOptions.FSelectionOptions) then
  10362. Treeview.FocusedColumn := FMainColumn;
  10363. Treeview.Invalidate;
  10364. end;
  10365. end;
  10366. end;
  10367. end;
  10368. //----------------------------------------------------------------------------------------------------------------------
  10369. procedure TVTHeader.SetMaxHeight(Value: Integer);
  10370. begin
  10371. if Value < FMinHeight then
  10372. Value := FMinHeight;
  10373. FMaxHeight := Value;
  10374. SetHeight(FHeight);
  10375. end;
  10376. //----------------------------------------------------------------------------------------------------------------------
  10377. procedure TVTHeader.SetMinHeight(Value: Integer);
  10378. begin
  10379. if Value < 0 then
  10380. Value := 0;
  10381. if Value > FMaxHeight then
  10382. Value := FMaxHeight;
  10383. FMinHeight := Value;
  10384. SetHeight(FHeight);
  10385. end;
  10386. //----------------------------------------------------------------------------------------------------------------------
  10387. procedure TVTHeader.SetOptions(Value: TVTHeaderOptions);
  10388. var
  10389. ToBeSet,
  10390. ToBeCleared: TVTHeaderOptions;
  10391. begin
  10392. ToBeSet := Value - FOptions;
  10393. ToBeCleared := FOptions - Value;
  10394. FOptions := Value;
  10395. if (hoAutoResize in (ToBeSet + ToBeCleared)) and (FColumns.Count > 0) then
  10396. begin
  10397. FColumns.AdjustAutoSize(InvalidColumn);
  10398. if Treeview.HandleAllocated then
  10399. begin
  10400. Treeview.UpdateHorizontalScrollBar(False);
  10401. if hoAutoResize in ToBeSet then
  10402. Treeview.Invalidate;
  10403. end;
  10404. end;
  10405. if not (csLoading in Treeview.ComponentState) and Treeview.HandleAllocated then
  10406. begin
  10407. if hoVisible in (ToBeSet + ToBeCleared) then
  10408. RecalculateHeader;
  10409. Invalidate(nil);
  10410. Treeview.Invalidate;
  10411. end;
  10412. end;
  10413. //----------------------------------------------------------------------------------------------------------------------
  10414. procedure TVTHeader.SetParentFont(Value: Boolean);
  10415. begin
  10416. if FParentFont <> Value then
  10417. begin
  10418. FParentFont := Value;
  10419. if FParentFont then
  10420. FFont.Assign(FOwner.Font);
  10421. end;
  10422. end;
  10423. //----------------------------------------------------------------------------------------------------------------------
  10424. procedure TVTHeader.SetSortColumn(Value: TColumnIndex);
  10425. begin
  10426. if csLoading in Treeview.ComponentState then
  10427. FSortColumn := Value
  10428. else
  10429. DoSetSortColumn(Value);
  10430. end;
  10431. //----------------------------------------------------------------------------------------------------------------------
  10432. procedure TVTHeader.SetSortDirection(const Value: TSortDirection);
  10433. begin
  10434. if Value <> FSortDirection then
  10435. begin
  10436. FSortDirection := Value;
  10437. Invalidate(nil);
  10438. if ((toAutoSort in Treeview.FOptions.FAutoOptions) or (hoHeaderClickAutoSort in Options)) and (Treeview.FUpdateCount = 0) then
  10439. Treeview.SortTree(FSortColumn, FSortDirection, True);
  10440. end;
  10441. end;
  10442. //----------------------------------------------------------------------------------------------------------------------
  10443. function TVTHeader.CanSplitterResize(P: TPoint): Boolean;
  10444. begin
  10445. Result := hoHeightResize in FOptions;
  10446. DoCanSplitterResize(P, Result);
  10447. end;
  10448. //----------------------------------------------------------------------------------------------------------------------
  10449. procedure TVTHeader.SetStyle(Value: TVTHeaderStyle);
  10450. begin
  10451. if FStyle <> Value then
  10452. begin
  10453. FStyle := Value;
  10454. if not (csLoading in Treeview.ComponentState) then
  10455. Invalidate(nil);
  10456. end;
  10457. end;
  10458. //----------------------------------------------------------------------------------------------------------------------
  10459. function TVTHeader.CanWriteColumns: Boolean;
  10460. // descendants may override this to optionally prevent column writing (e.g. if they are build dynamically).
  10461. begin
  10462. Result := True;
  10463. end;
  10464. //----------------------------------------------------------------------------------------------------------------------
  10465. procedure TVTHeader.ChangeScale(M, D: Integer);
  10466. var
  10467. I: Integer;
  10468. begin
  10469. // This method is only executed if toAutoChangeScale is set
  10470. if not ParentFont then
  10471. FFont.Size := MulDiv(FFont.Size, M, D);
  10472. Self.Height := MulDiv(FHeight, M, D);
  10473. // Scale the columns widths too
  10474. for I := 0 to FColumns.Count - 1 do
  10475. begin
  10476. Self.FColumns[I].Width := MulDiv(Self.FColumns[I].Width, M, D);
  10477. end;//for I
  10478. end;
  10479. //----------------------------------------------------------------------------------------------------------------------
  10480. function TVTHeader.DetermineSplitterIndex(P: TPoint): Boolean;
  10481. // Tries to find the index of that column whose right border corresponds to P.
  10482. // Result is True if column border was hit (with -3..+5 pixels tolerance).
  10483. // For continuous resizing the current track index and the column's left/right border are set.
  10484. // Note: The hit test is checking from right to left (or left to right in RTL mode) to make enlarging of zero-sized
  10485. // columns possible.
  10486. var
  10487. I,
  10488. VisibleFixedWidth: Integer;
  10489. SplitPoint: Integer;
  10490. //--------------- local function --------------------------------------------
  10491. function IsNearBy(IsFixedCol: Boolean; LeftTolerance, RightTolerance: Integer): Boolean;
  10492. begin
  10493. if IsFixedCol then
  10494. Result := (P.X < SplitPoint + Treeview.FEffectiveOffsetX + RightTolerance) and (P.X > SplitPoint + Treeview.FEffectiveOffsetX - LeftTolerance)
  10495. else
  10496. Result := (P.X > VisibleFixedWidth) and (P.X < SplitPoint + RightTolerance) and (P.X > SplitPoint - LeftTolerance);
  10497. end;
  10498. //--------------- end local function ----------------------------------------
  10499. begin
  10500. Result := False;
  10501. FColumns.FTrackIndex := NoColumn;
  10502. VisibleFixedWidth := FColumns.GetVisibleFixedWidth;
  10503. if FColumns.Count > 0 then
  10504. begin
  10505. if Treeview.UseRightToLeftAlignment then
  10506. begin
  10507. SplitPoint := -Treeview.FEffectiveOffsetX;
  10508. if Integer(Treeview.FRangeX) < Treeview.ClientWidth then
  10509. Inc(SplitPoint, Treeview.ClientWidth - Integer(Treeview.FRangeX));
  10510. for I := 0 to FColumns.Count - 1 do
  10511. with FColumns, Items[FPositionToIndex[I]] do
  10512. if coVisible in FOptions then
  10513. begin
  10514. if IsNearBy(coFixed in FOptions, 5, 3) then
  10515. begin
  10516. if CanSplitterResize(P, FPositionToIndex[I]) then
  10517. begin
  10518. Result := True;
  10519. FTrackIndex := FPositionToIndex[I];
  10520. // Keep the right border of this column. This and the current mouse position
  10521. // directly determine the current column width.
  10522. FTrackPoint.X := SplitPoint + IfThen(coFixed in FOptions, Treeview.FEffectiveOffsetX) + FWidth;
  10523. FTrackPoint.Y := P.Y;
  10524. Break;
  10525. end;
  10526. end;
  10527. Inc(SplitPoint, FWidth);
  10528. end;
  10529. end
  10530. else
  10531. begin
  10532. SplitPoint := -Treeview.FEffectiveOffsetX + Integer(Treeview.FRangeX);
  10533. for I := FColumns.Count - 1 downto 0 do
  10534. with FColumns, Items[FPositionToIndex[I]] do
  10535. if coVisible in FOptions then
  10536. begin
  10537. if IsNearBy(coFixed in FOptions, 3, 5) then
  10538. begin
  10539. if CanSplitterResize(P, FPositionToIndex[I]) then
  10540. begin
  10541. Result := True;
  10542. FTrackIndex := FPositionToIndex[I];
  10543. // Keep the left border of this column. This and the current mouse position
  10544. // directly determine the current column width.
  10545. FTrackPoint.X := SplitPoint + IfThen(coFixed in FOptions, Treeview.FEffectiveOffsetX) - FWidth;
  10546. FTrackPoint.Y := P.Y;
  10547. Break;
  10548. end;
  10549. end;
  10550. Dec(SplitPoint, FWidth);
  10551. end;
  10552. end;
  10553. end;
  10554. end;
  10555. //----------------------------------------------------------------------------------------------------------------------
  10556. procedure TVTHeader.DoAfterAutoFitColumn(Column: TColumnIndex);
  10557. begin
  10558. if Assigned(TreeView.FOnAfterAutoFitColumn) then
  10559. TreeView.FOnAfterAutoFitColumn(Self, Column);
  10560. end;
  10561. //----------------------------------------------------------------------------------------------------------------------
  10562. procedure TVTHeader.DoAfterColumnWidthTracking(Column: TColumnIndex);
  10563. // Tell the application that a column width tracking operation has been finished.
  10564. begin
  10565. if Assigned(TreeView.FOnAfterColumnWidthTracking) then
  10566. TreeView.FOnAfterColumnWidthTracking(Self, Column);
  10567. end;
  10568. //----------------------------------------------------------------------------------------------------------------------
  10569. procedure TVTHeader.DoAfterHeightTracking;
  10570. // Tell the application that a height tracking operation has been finished.
  10571. begin
  10572. if Assigned(TreeView.FOnAfterHeaderHeightTracking) then
  10573. TreeView.FOnAfterHeaderHeightTracking(Self);
  10574. end;
  10575. //----------------------------------------------------------------------------------------------------------------------
  10576. function TVTHeader.DoBeforeAutoFitColumn(Column: TColumnIndex; SmartAutoFitType: TSmartAutoFitType): Boolean;
  10577. // Query the application if we may autofit a column.
  10578. begin
  10579. Result := True;
  10580. if Assigned(TreeView.FOnBeforeAutoFitColumn) then
  10581. TreeView.FOnBeforeAutoFitColumn(Self, Column, SmartAutoFitType, Result);
  10582. end;
  10583. //----------------------------------------------------------------------------------------------------------------------
  10584. procedure TVTHeader.DoBeforeColumnWidthTracking(Column: TColumnIndex; Shift: TShiftState);
  10585. // Tell the a application that a column width tracking operation may begin.
  10586. begin
  10587. if Assigned(TreeView.FOnBeforeColumnWidthTracking) then
  10588. TreeView.FOnBeforeColumnWidthTracking(Self, Column, Shift);
  10589. end;
  10590. //----------------------------------------------------------------------------------------------------------------------
  10591. procedure TVTHeader.DoBeforeHeightTracking(Shift: TShiftState);
  10592. // Tell the application that a height tracking operation may begin.
  10593. begin
  10594. if Assigned(TreeView.FOnBeforeHeaderHeightTracking) then
  10595. TreeView.FOnBeforeHeaderHeightTracking(Self, Shift);
  10596. end;
  10597. //----------------------------------------------------------------------------------------------------------------------
  10598. procedure TVTHeader.DoCanSplitterResize(P: TPoint; var Allowed: Boolean);
  10599. begin
  10600. if Assigned(TreeView.FOnCanSplitterResizeHeader) then
  10601. TreeView.FOnCanSplitterResizeHeader(Self, P, Allowed);
  10602. end;
  10603. //----------------------------------------------------------------------------------------------------------------------
  10604. function TVTHeader.DoColumnWidthDblClickResize(Column: TColumnIndex; P: TPoint; Shift: TShiftState): Boolean;
  10605. // Queries the application whether a double click on the column splitter should resize the column.
  10606. begin
  10607. Result := True;
  10608. if Assigned(TreeView.FOnColumnWidthDblClickResize) then
  10609. TreeView.FOnColumnWidthDblClickResize(Self, Column, Shift, P, Result);
  10610. end;
  10611. //----------------------------------------------------------------------------------------------------------------------
  10612. function TVTHeader.DoColumnWidthTracking(Column: TColumnIndex; Shift: TShiftState; var TrackPoint: TPoint; P: TPoint): Boolean;
  10613. begin
  10614. Result := True;
  10615. if Assigned(TreeView.FOnColumnWidthTracking) then
  10616. TreeView.FOnColumnWidthTracking(Self, Column, Shift, TrackPoint, P, Result);
  10617. end;
  10618. //----------------------------------------------------------------------------------------------------------------------
  10619. function TVTHeader.DoGetPopupMenu(Column: TColumnIndex; Position: TPoint): TPopupMenu;
  10620. // Queries the application whether there is a column specific header popup menu.
  10621. var
  10622. AskParent: Boolean;
  10623. begin
  10624. Result := nil;
  10625. if Assigned(TreeView.FOnGetPopupMenu) then
  10626. TreeView.FOnGetPopupMenu(TreeView, nil, Column, Position, AskParent, Result);
  10627. end;
  10628. //----------------------------------------------------------------------------------------------------------------------
  10629. function TVTHeader.DoHeightTracking(var P: TPoint; Shift: TShiftState): Boolean;
  10630. begin
  10631. Result := True;
  10632. if Assigned(TreeView.FOnHeaderHeightTracking) then
  10633. TreeView.FOnHeaderHeightTracking(Self, P, Shift, Result);
  10634. end;
  10635. //----------------------------------------------------------------------------------------------------------------------
  10636. function TVTHeader.DoHeightDblClickResize(var P: TPoint; Shift: TShiftState): Boolean;
  10637. begin
  10638. Result := True;
  10639. if Assigned(TreeView.FOnHeaderHeightDblClickResize) then
  10640. TreeView.FOnHeaderHeightDblClickResize(Self, P, Shift, Result);
  10641. end;
  10642. //----------------------------------------------------------------------------------------------------------------------
  10643. procedure TVTHeader.DoSetSortColumn(Value: TColumnIndex);
  10644. begin
  10645. if Value < NoColumn then
  10646. Value := NoColumn;
  10647. if Value > Columns.Count - 1 then
  10648. Value := Columns.Count - 1;
  10649. if FSortColumn <> Value then
  10650. begin
  10651. if FSortColumn > NoColumn then
  10652. Invalidate(Columns[FSortColumn]);
  10653. FSortColumn := Value;
  10654. if FSortColumn > NoColumn then
  10655. Invalidate(Columns[FSortColumn]);
  10656. if ((toAutoSort in Treeview.FOptions.FAutoOptions) or (hoHeaderClickAutoSort in Options)) and (Treeview.FUpdateCount = 0) then
  10657. Treeview.SortTree(FSortColumn, FSortDirection, True);
  10658. end;
  10659. end;
  10660. //----------------------------------------------------------------------------------------------------------------------
  10661. procedure TVTHeader.DragTo(P: TPoint);
  10662. // Moves the drag image to a new position, which is determined from the passed point P and the previous
  10663. // mouse position.
  10664. var
  10665. I,
  10666. NewTarget: Integer;
  10667. // optimized drag image move support
  10668. ClientP: TPoint;
  10669. Left,
  10670. Right: Integer;
  10671. NeedRepaint: Boolean; // True if the screen needs an update (changed drop target or drop side)
  10672. begin
  10673. // Determine new drop target and which side of it is prefered.
  10674. ClientP := Treeview.ScreenToClient(P);
  10675. // Make coordinates relative to (0, 0) of the non-client area.
  10676. Inc(ClientP.Y, FHeight);
  10677. NewTarget := FColumns.ColumnFromPosition(ClientP);
  10678. NeedRepaint := (NewTarget <> InvalidColumn) and (NewTarget <> FColumns.FDropTarget);
  10679. if NewTarget >= 0 then
  10680. begin
  10681. FColumns.GetColumnBounds(NewTarget, Left, Right);
  10682. if (ClientP.X < ((Left + Right) div 2)) <> FColumns.FDropBefore then
  10683. begin
  10684. NeedRepaint := True;
  10685. FColumns.FDropBefore := not FColumns.FDropBefore;
  10686. end;
  10687. end;
  10688. if NeedRepaint then
  10689. begin
  10690. // Invalidate columns which need a repaint.
  10691. if FColumns.FDropTarget > NoColumn then
  10692. begin
  10693. I := FColumns.FDropTarget;
  10694. FColumns.FDropTarget := NoColumn;
  10695. Invalidate(FColumns.Items[I]);
  10696. end;
  10697. if (NewTarget > NoColumn) and (NewTarget <> FColumns.FDropTarget) then
  10698. begin
  10699. Invalidate(FColumns.Items[NewTarget]);
  10700. FColumns.FDropTarget := NewTarget;
  10701. end;
  10702. end;
  10703. FDragImage.DragTo(P, NeedRepaint);
  10704. end;
  10705. //----------------------------------------------------------------------------------------------------------------------
  10706. procedure TVTHeader.FixedAreaConstraintsChanged(Sender: TObject);
  10707. // This method gets called when FFixedAreaConstraints is changed.
  10708. begin
  10709. if Treeview.HandleAllocated then
  10710. RescaleHeader
  10711. else
  10712. Include(FStates, hsNeedScaling);
  10713. end;
  10714. //----------------------------------------------------------------------------------------------------------------------
  10715. function TVTHeader.GetColumnsClass: TVirtualTreeColumnsClass;
  10716. // Returns the class to be used for the actual column implementation. descendants may optionally override this and
  10717. // return their own class.
  10718. begin
  10719. Result := TVirtualTreeColumns;
  10720. end;
  10721. //----------------------------------------------------------------------------------------------------------------------
  10722. function TVTHeader.GetOwner: TPersistent;
  10723. begin
  10724. Result := FOwner;
  10725. end;
  10726. //----------------------------------------------------------------------------------------------------------------------
  10727. function TVTHeader.GetShiftState: TShiftState;
  10728. begin
  10729. Result := [];
  10730. if GetKeyState(VK_SHIFT) < 0 then
  10731. Include(Result, ssShift);
  10732. if GetKeyState(VK_CONTROL) < 0 then
  10733. Include(Result, ssCtrl);
  10734. if GetKeyState(VK_MENU) < 0 then
  10735. Include(Result, ssAlt);
  10736. end;
  10737. //----------------------------------------------------------------------------------------------------------------------
  10738. function TVTHeader.HandleHeaderMouseMove(var Message: TWMMouseMove): Boolean;
  10739. var
  10740. P: TPoint;
  10741. NextColumn,
  10742. I: TColumnIndex;
  10743. NewWidth: Integer;
  10744. begin
  10745. Result := False;
  10746. with Message do
  10747. begin
  10748. P := Point(XPos, YPos);
  10749. if hsColumnWidthTrackPending in FStates then
  10750. begin
  10751. Treeview.StopTimer(HeaderTimer);
  10752. FStates := FStates - [hsColumnWidthTrackPending] + [hsColumnWidthTracking];
  10753. HandleHeaderMouseMove := True;
  10754. Result := 0;
  10755. end
  10756. else
  10757. if hsHeightTrackPending in FStates then
  10758. begin
  10759. Treeview.StopTimer(HeaderTimer);
  10760. FStates := FStates - [hsHeightTrackPending] + [hsHeightTracking];
  10761. HandleHeaderMouseMove := True;
  10762. Result := 0;
  10763. end
  10764. else
  10765. if hsColumnWidthTracking in FStates then
  10766. begin
  10767. if DoColumnWidthTracking(FColumns.FTrackIndex, GetShiftState, FTrackPoint, P) then
  10768. begin
  10769. if Treeview.UseRightToLeftAlignment then
  10770. begin
  10771. NewWidth := FTrackPoint.X - XPos;
  10772. NextColumn := FColumns.GetPreviousVisibleColumn(FColumns.FTrackIndex);
  10773. end
  10774. else
  10775. begin
  10776. NewWidth := XPos - FTrackPoint.X;
  10777. NextColumn := FColumns.GetNextVisibleColumn(FColumns.FTrackIndex);
  10778. end;
  10779. // The autosized column cannot be resized using the mouse normally. Instead we resize the next
  10780. // visible column, so it look as we directly resize the autosized column.
  10781. if (hoAutoResize in FOptions) and (FColumns.FTrackIndex = FAutoSizeIndex) and
  10782. (NextColumn > NoColumn) and (coResizable in FColumns[NextColumn].FOptions) and
  10783. (FColumns[FColumns.FTrackIndex].FMinWidth < NewWidth) and
  10784. (FColumns[FColumns.FTrackIndex].FMaxWidth > NewWidth) then
  10785. FColumns[NextColumn].Width := FColumns[NextColumn].Width - NewWidth
  10786. + FColumns[FColumns.FTrackIndex].Width
  10787. else
  10788. FColumns[FColumns.FTrackIndex].Width := NewWidth; // 1 EListError seen here (List index out of bounds (-1)) since 10/2013
  10789. end;
  10790. HandleHeaderMouseMove := True;
  10791. Result := 0;
  10792. end
  10793. else
  10794. if hsHeightTracking in FStates then
  10795. begin
  10796. if DoHeightTracking(P, GetShiftState) then
  10797. SetHeight(Integer(FHeight) + P.Y);
  10798. HandleHeaderMouseMove := True;
  10799. Result := 0;
  10800. end
  10801. else
  10802. begin
  10803. if hsDragPending in FStates then
  10804. begin
  10805. P := Treeview.ClientToScreen(P);
  10806. // start actual dragging if allowed
  10807. if (hoDrag in FOptions) and Treeview.DoHeaderDragging(FColumns.FDownIndex) then
  10808. begin
  10809. if ((Abs(FDragStart.X - P.X) > Mouse.DragThreshold) or
  10810. (Abs(FDragStart.Y - P.Y) > Mouse.DragThreshold)) then
  10811. begin
  10812. Treeview.StopTimer(HeaderTimer);
  10813. I := FColumns.FDownIndex;
  10814. FColumns.FDownIndex := NoColumn;
  10815. FColumns.FHoverIndex := NoColumn;
  10816. if I > NoColumn then
  10817. Invalidate(FColumns[I]);
  10818. PrepareDrag(P, FDragStart);
  10819. FStates := FStates - [hsDragPending] + [hsDragging];
  10820. HandleHeaderMouseMove := True;
  10821. Result := 0;
  10822. end;
  10823. end;
  10824. end
  10825. else
  10826. if hsDragging in FStates then
  10827. begin
  10828. DragTo(Treeview.ClientToScreen(Point(XPos, YPos)));
  10829. HandleHeaderMouseMove := True;
  10830. Result := 0;
  10831. end;
  10832. end;
  10833. end;
  10834. end;
  10835. //----------------------------------------------------------------------------------------------------------------------
  10836. function TVTHeader.HandleMessage(var Message: TMessage): Boolean;
  10837. // The header gets here the opportunity to handle certain messages before they reach the tree. This is important
  10838. // because the tree needs to handle various non-client area messages for the header as well as some dragging/tracking
  10839. // events.
  10840. // By returning True the message will not be handled further, otherwise the message is then dispatched
  10841. // to the proper message handlers.
  10842. var
  10843. P: TPoint;
  10844. R: TRect;
  10845. I: TColumnIndex;
  10846. OldPosition: Integer;
  10847. HitIndex: TColumnIndex;
  10848. NewCursor: HCURSOR;
  10849. Button: TMouseButton;
  10850. Menu: TPopupMenu;
  10851. IsInHeader,
  10852. IsHSplitterHit,
  10853. IsVSplitterHit: Boolean;
  10854. //--------------- local function --------------------------------------------
  10855. function HSplitterHit: Boolean;
  10856. var
  10857. NextCol: TColumnIndex;
  10858. begin
  10859. Result := (hoColumnResize in FOptions) and DetermineSplitterIndex(P);
  10860. if Result and not InHeader(P) then
  10861. begin
  10862. NextCol := FColumns.GetNextVisibleColumn(FColumns.FTrackIndex);
  10863. if not (coFixed in FColumns[FColumns.FTrackIndex].Options) or (NextCol <= NoColumn) or
  10864. (coFixed in FColumns[NextCol].Options) or (P.Y > Integer(Treeview.FRangeY)) then
  10865. Result := False;
  10866. end;
  10867. end;
  10868. //--------------- end local function ----------------------------------------
  10869. begin
  10870. Result := False;
  10871. case Message.Msg of
  10872. WM_SIZE:
  10873. begin
  10874. if not (tsWindowCreating in FOwner.FStates) then
  10875. if (hoAutoResize in FOptions) and not (hsAutoSizing in FStates) then
  10876. begin
  10877. FColumns.AdjustAutoSize(InvalidColumn);
  10878. Invalidate(nil);
  10879. end
  10880. else
  10881. if not (hsScaling in FStates) then
  10882. begin
  10883. RescaleHeader;
  10884. Invalidate(nil);
  10885. end;
  10886. end;
  10887. CM_PARENTFONTCHANGED:
  10888. if FParentFont then
  10889. FFont.Assign(FOwner.Font);
  10890. CM_BIDIMODECHANGED:
  10891. for I := 0 to FColumns.Count - 1 do
  10892. if coParentBiDiMode in FColumns[I].FOptions then
  10893. FColumns[I].ParentBiDiModeChanged;
  10894. WM_NCMBUTTONDOWN:
  10895. begin
  10896. with TWMNCMButtonDown(Message) do
  10897. P := Treeview.ScreenToClient(Point(XCursor, YCursor));
  10898. if InHeader(P) then
  10899. FOwner.DoHeaderMouseDown(mbMiddle, GetShiftState, P.X, P.Y + Integer(FHeight));
  10900. end;
  10901. WM_NCMBUTTONUP:
  10902. begin
  10903. with TWMNCMButtonUp(Message) do
  10904. P := FOwner.ScreenToClient(Point(XCursor, YCursor));
  10905. if InHeader(P) then
  10906. begin
  10907. FColumns.HandleClick(P, mbMiddle, True, False);
  10908. FOwner.DoHeaderMouseUp(mbMiddle, GetShiftState, P.X, P.Y + Integer(FHeight));
  10909. FColumns.FDownIndex := NoColumn;
  10910. FColumns.FCheckBoxHit := False;
  10911. end;
  10912. end;
  10913. WM_LBUTTONDBLCLK,
  10914. WM_NCLBUTTONDBLCLK,
  10915. WM_NCMBUTTONDBLCLK,
  10916. WM_NCRBUTTONDBLCLK:
  10917. begin
  10918. if Message.Msg <> WM_LBUTTONDBLCLK then
  10919. with TWMNCLButtonDblClk(Message) do
  10920. P := FOwner.ScreenToClient(Point(XCursor, YCursor))
  10921. else
  10922. with TWMLButtonDblClk(Message) do
  10923. P := Point(XPos, YPos);
  10924. if (hoHeightDblClickResize in FOptions) and InHeaderSplitterArea(P) and (FDefaultHeight > 0) then
  10925. begin
  10926. if DoHeightDblClickResize(P, GetShiftState) and (FDefaultHeight > 0) then
  10927. SetHeight(FMinHeight);
  10928. Result := True;
  10929. end
  10930. else
  10931. if HSplitterHit and ((Message.Msg = WM_NCLBUTTONDBLCLK) or (Message.Msg = WM_LBUTTONDBLCLK)) and
  10932. (hoDblClickResize in FOptions) and (FColumns.FTrackIndex > NoColumn) then
  10933. begin
  10934. // If the click was on a splitter then resize column to smallest width.
  10935. if DoColumnWidthDblClickResize(FColumns.FTrackIndex, P, GetShiftState) then
  10936. AutoFitColumns(True, smaUseColumnOption, FColumns[FColumns.FTrackIndex].FPosition,
  10937. FColumns[FColumns.FTrackIndex].FPosition);
  10938. Message.Result := 0;
  10939. Result := True;
  10940. end
  10941. else
  10942. if InHeader(P) and (Message.Msg <> WM_LBUTTONDBLCLK) then
  10943. begin
  10944. case Message.Msg of
  10945. WM_NCMBUTTONDBLCLK:
  10946. Button := mbMiddle;
  10947. WM_NCRBUTTONDBLCLK:
  10948. Button := mbRight;
  10949. else
  10950. // WM_NCLBUTTONDBLCLK
  10951. Button := mbLeft;
  10952. end;
  10953. if Button = mbLeft then
  10954. Columns.AdjustDownColumn(P);
  10955. FColumns.HandleClick(P, Button, True, True);
  10956. end;
  10957. end;
  10958. // The "hot" area of the headers horizontal splitter is partly within the client area of the the tree, so we need
  10959. // to handle WM_LBUTTONDOWN here, too.
  10960. WM_LBUTTONDOWN,
  10961. WM_NCLBUTTONDOWN:
  10962. begin
  10963. Application.CancelHint;
  10964. if not (csDesigning in Treeview.ComponentState) then
  10965. begin
  10966. // make sure no auto scrolling is active...
  10967. Treeview.StopTimer(ScrollTimer);
  10968. Treeview.DoStateChange([], [tsScrollPending, tsScrolling]);
  10969. // ... pending editing is cancelled (actual editing remains active)
  10970. Treeview.StopTimer(EditTimer);
  10971. Treeview.DoStateChange([], [tsEditPending]);
  10972. end;
  10973. if Message.Msg = WM_LBUTTONDOWN then
  10974. // Coordinates are already client area based.
  10975. with TWMLButtonDown(Message) do
  10976. P := Point(XPos, YPos)
  10977. else
  10978. with TWMNCLButtonDown(Message) do
  10979. begin
  10980. // want the drag start point in screen coordinates
  10981. FDragStart := Point(XCursor, YCursor);
  10982. P := Treeview.ScreenToClient(FDragStart);
  10983. end;
  10984. IsInHeader := InHeader(P);
  10985. // in design-time header columns are always resizable
  10986. if (csDesigning in Treeview.ComponentState) then
  10987. IsVSplitterHit := InHeaderSplitterArea(P)
  10988. else
  10989. IsVSplitterHit := InHeaderSplitterArea(P) and CanSplitterResize(P);
  10990. IsHSplitterHit := HSplitterHit;
  10991. if IsVSplitterHit or IsHSplitterHit then
  10992. begin
  10993. FTrackStart := P;
  10994. FColumns.FHoverIndex := NoColumn;
  10995. if IsVSplitterHit then
  10996. begin
  10997. if not (csDesigning in Treeview.ComponentState) then
  10998. DoBeforeHeightTracking(GetShiftState);
  10999. Include(FStates, hsHeightTrackPending);
  11000. end
  11001. else
  11002. begin
  11003. if not (csDesigning in Treeview.ComponentState) then
  11004. DoBeforeColumnWidthTracking(FColumns.FTrackIndex, GetShiftState);
  11005. Include(FStates, hsColumnWidthTrackPending);
  11006. end;
  11007. SetCapture(Treeview.Handle);
  11008. Result := True;
  11009. Message.Result := 0;
  11010. end
  11011. else
  11012. if IsInHeader then
  11013. begin
  11014. HitIndex := Columns.AdjustDownColumn(P);
  11015. // in design-time header columns are always draggable
  11016. if ((csDesigning in Treeview.ComponentState) and (HitIndex > NoColumn)) or
  11017. ((hoDrag in FOptions) and (HitIndex > NoColumn) and (coDraggable in FColumns[HitIndex].FOptions)) then
  11018. begin
  11019. // Show potential drag operation.
  11020. // Disabled columns do not start a drag operation because they can't be clicked.
  11021. Include(FStates, hsDragPending);
  11022. SetCapture(Treeview.Handle);
  11023. Result := True;
  11024. Message.Result := 0;
  11025. end;
  11026. end;
  11027. // This is a good opportunity to notify the application.
  11028. if not (csDesigning in Treeview.ComponentState) and IsInHeader then
  11029. FOwner.DoHeaderMouseDown(mbLeft, GetShiftState, P.X, P.Y + Integer(FHeight));
  11030. end;
  11031. WM_NCRBUTTONDOWN:
  11032. begin
  11033. with TWMNCRButtonDown(Message) do
  11034. P := FOwner.ScreenToClient(Point(XCursor, YCursor));
  11035. if InHeader(P) then
  11036. FOwner.DoHeaderMouseDown(mbRight, GetShiftState, P.X, P.Y + Integer(FHeight));
  11037. end;
  11038. WM_NCRBUTTONUP:
  11039. if not (csDesigning in FOwner.ComponentState) then
  11040. with TWMNCRButtonUp(Message) do
  11041. begin
  11042. Application.CancelHint;
  11043. P := FOwner.ScreenToClient(Point(XCursor, YCursor));
  11044. if InHeader(P) then
  11045. begin
  11046. FColumns.HandleClick(P, mbRight, True, False);
  11047. FOwner.DoHeaderMouseUp(mbRight, GetShiftState, P.X, P.Y + Integer(FHeight));
  11048. FColumns.FDownIndex := NoColumn;
  11049. FColumns.FTrackIndex := NoColumn;
  11050. FColumns.FCheckBoxHit := False;
  11051. Menu := FPopupMenu;
  11052. if not Assigned(Menu) then
  11053. Menu := DoGetPopupMenu(FColumns.ColumnFromPosition(Point(P.X, P.Y + Integer(FHeight))), P);
  11054. // Trigger header popup if there's one.
  11055. if Assigned(Menu) then
  11056. begin
  11057. Treeview.StopTimer(ScrollTimer);
  11058. Treeview.StopTimer(HeaderTimer);
  11059. FColumns.FHoverIndex := NoColumn;
  11060. Treeview.DoStateChange([], [tsScrollPending, tsScrolling]);
  11061. Menu.PopupComponent := Treeview;
  11062. Menu.Popup(XCursor, YCursor);
  11063. HandleMessage := True;
  11064. end;
  11065. end;
  11066. end;
  11067. // When the tree window has an active mouse capture then we only get "client-area" messages.
  11068. WM_LBUTTONUP,
  11069. WM_NCLBUTTONUP:
  11070. begin
  11071. Application.CancelHint;
  11072. if FStates <> [] then
  11073. begin
  11074. ReleaseCapture;
  11075. if hsDragging in FStates then
  11076. begin
  11077. // successfull dragging moves columns
  11078. with TWMLButtonUp(Message) do
  11079. P := Treeview.ClientToScreen(Point(XPos, YPos));
  11080. GetWindowRect(Treeview.Handle, R);
  11081. with FColumns do
  11082. begin
  11083. FDragImage.EndDrag;
  11084. if (FDropTarget > -1) and (FDropTarget <> FDragIndex) and PtInRect(R, P) then
  11085. begin
  11086. OldPosition := FColumns[FDragIndex].Position;
  11087. if FColumns.FDropBefore then
  11088. begin
  11089. if FColumns[FDragIndex].Position < FColumns[FDropTarget].Position then
  11090. FColumns[FDragIndex].Position := Max(0, FColumns[FDropTarget].Position - 1)
  11091. else
  11092. FColumns[FDragIndex].Position := FColumns[FDropTarget].Position;
  11093. end
  11094. else
  11095. begin
  11096. if FColumns[FDragIndex].Position < FColumns[FDropTarget].Position then
  11097. FColumns[FDragIndex].Position := FColumns[FDropTarget].Position
  11098. else
  11099. FColumns[FDragIndex].Position := FColumns[FDropTarget].Position + 1;
  11100. end;
  11101. Treeview.DoHeaderDragged(FDragIndex, OldPosition);
  11102. end
  11103. else
  11104. Treeview.DoHeaderDraggedOut(FDragIndex, P);
  11105. FDropTarget := NoColumn;
  11106. end;
  11107. Invalidate(nil);
  11108. end;
  11109. Result := True;
  11110. Message.Result := 0;
  11111. end;
  11112. case Message.Msg of
  11113. WM_LBUTTONUP:
  11114. with TWMLButtonUp(Message) do
  11115. begin
  11116. if FColumns.FDownIndex > NoColumn then
  11117. FColumns.HandleClick(Point(XPos, YPos), mbLeft, False, False);
  11118. if FStates <> [] then
  11119. FOwner.DoHeaderMouseUp(mbLeft, KeysToShiftState(Keys), XPos, YPos);
  11120. end;
  11121. WM_NCLBUTTONUP:
  11122. with TWMNCLButtonUp(Message) do
  11123. begin
  11124. P := FOwner.ScreenToClient(Point(XCursor, YCursor));
  11125. FColumns.HandleClick(P, mbLeft, False, False);
  11126. FOwner.DoHeaderMouseUp(mbLeft, GetShiftState, P.X, P.Y + Integer(FHeight));
  11127. end;
  11128. end;
  11129. if FColumns.FTrackIndex > NoColumn then
  11130. begin
  11131. if hsColumnWidthTracking in FStates then
  11132. DoAfterColumnWidthTracking(FColumns.FTrackIndex);
  11133. Invalidate(Columns[FColumns.FTrackIndex]);
  11134. FColumns.FTrackIndex := NoColumn;
  11135. end;
  11136. if FColumns.FDownIndex > NoColumn then
  11137. begin
  11138. Invalidate(Columns[FColumns.FDownIndex]);
  11139. FColumns.FDownIndex := NoColumn;
  11140. end;
  11141. if hsHeightTracking in FStates then
  11142. DoAfterHeightTracking;
  11143. FStates := FStates - [hsDragging, hsDragPending,
  11144. hsColumnWidthTracking, hsColumnWidthTrackPending,
  11145. hsHeightTracking, hsHeightTrackPending];
  11146. end;
  11147. // hovering, mouse leave detection
  11148. WM_NCMOUSEMOVE:
  11149. with TWMNCMouseMove(Message), FColumns do
  11150. begin
  11151. P := Treeview.ScreenToClient(Point(XCursor, YCursor));
  11152. Treeview.DoHeaderMouseMove(GetShiftState, P.X, P.Y + Integer(FHeight));
  11153. if InHeader(P) and ((AdjustHoverColumn(P)) or ((FDownIndex >= 0) and (FHoverIndex <> FDownIndex))) then
  11154. begin
  11155. // We need a mouse leave detection from here for the non client area.
  11156. // TODO: The best solution available would be the TrackMouseEvent API.
  11157. // With the drop of the support of Win95 totally and WinNT4 we should replace the timer.
  11158. Treeview.StopTimer(HeaderTimer);
  11159. SetTimer(Treeview.Handle, HeaderTimer, 50, nil);
  11160. // use Delphi's internal hint handling for header hints too
  11161. if hoShowHint in FOptions then
  11162. begin
  11163. // client coordinates!
  11164. XCursor := P.X;
  11165. YCursor := P.Y + Integer(FHeight);
  11166. Application.HintMouseMessage(Treeview, Message);
  11167. end;
  11168. end;
  11169. end;
  11170. WM_TIMER:
  11171. if TWMTimer(Message).TimerID = HeaderTimer then
  11172. begin
  11173. // determine current mouse position to check if it left the window
  11174. GetCursorPos(P);
  11175. P := Treeview.ScreenToClient(P);
  11176. with FColumns do
  11177. begin
  11178. if not InHeader(P) or ((FDownIndex > NoColumn) and (FHoverIndex <> FDownIndex)) then
  11179. begin
  11180. Treeview.StopTimer(HeaderTimer);
  11181. FHoverIndex := NoColumn;
  11182. FClickIndex := NoColumn;
  11183. FDownIndex := NoColumn;
  11184. FCheckBoxHit := False;
  11185. Result := True;
  11186. Message.Result := 0;
  11187. Invalidate(nil);
  11188. end;
  11189. end;
  11190. end;
  11191. WM_MOUSEMOVE: // mouse capture and general message redirection
  11192. Result := HandleHeaderMouseMove(TWMMouseMove(Message));
  11193. WM_SETCURSOR:
  11194. // Feature: design-time header
  11195. if (FStates = []) then
  11196. begin
  11197. // Retrieve last cursor position (GetMessagePos does not work here, I don't know why).
  11198. GetCursorPos(P);
  11199. // Is the mouse in the header rectangle and near the splitters?
  11200. P := Treeview.ScreenToClient(P);
  11201. IsHSplitterHit := HSplitterHit;
  11202. // in design-time header columns are always resizable
  11203. if (csDesigning in Treeview.ComponentState) then
  11204. IsVSplitterHit := InHeaderSplitterArea(P)
  11205. else
  11206. IsVSplitterHit := InHeaderSplitterArea(P) and CanSplitterResize(P);
  11207. if IsVSplitterHit or IsHSplitterHit then
  11208. begin
  11209. NewCursor := Screen.Cursors[Treeview.Cursor];
  11210. if IsVSplitterHit and ((hoHeightResize in FOptions) or (csDesigning in Treeview.ComponentState)) then
  11211. NewCursor := Screen.Cursors[crVertSplit]
  11212. else
  11213. if IsHSplitterHit then
  11214. NewCursor := Screen.Cursors[crHeaderSplit];
  11215. if not (csDesigning in Treeview.ComponentState) then
  11216. Treeview.DoGetHeaderCursor(NewCursor);
  11217. Result := NewCursor <> Screen.Cursors[crDefault];
  11218. if Result then
  11219. begin
  11220. Windows.SetCursor(NewCursor);
  11221. Message.Result := 1;
  11222. end;
  11223. end;
  11224. end
  11225. else
  11226. begin
  11227. Message.Result := 1;
  11228. Result := True;
  11229. end;
  11230. WM_KEYDOWN,
  11231. WM_KILLFOCUS:
  11232. if (Message.Msg = WM_KILLFOCUS) or
  11233. (TWMKeyDown(Message).CharCode = VK_ESCAPE) then
  11234. begin
  11235. if hsDragging in FStates then
  11236. begin
  11237. ReleaseCapture;
  11238. FDragImage.EndDrag;
  11239. Exclude(FStates, hsDragging);
  11240. FColumns.FDropTarget := NoColumn;
  11241. Invalidate(nil);
  11242. Result := True;
  11243. Message.Result := 0;
  11244. end
  11245. else
  11246. begin
  11247. if [hsColumnWidthTracking, hsHeightTracking] * FStates <> [] then
  11248. begin
  11249. ReleaseCapture;
  11250. if hsColumnWidthTracking in FStates then
  11251. DoAfterColumnWidthTracking(FColumns.FTrackIndex);
  11252. if hsHeightTracking in FStates then
  11253. DoAfterHeightTracking;
  11254. Result := True;
  11255. Message.Result := 0;
  11256. end;
  11257. FStates := FStates - [hsColumnWidthTracking, hsColumnWidthTrackPending,
  11258. hsHeightTracking, hsHeightTrackPending];
  11259. end;
  11260. end;
  11261. end;
  11262. end;
  11263. //----------------------------------------------------------------------------------------------------------------------
  11264. procedure TVTHeader.ImageListChange(Sender: TObject);
  11265. begin
  11266. if not (csDestroying in Treeview.ComponentState) then
  11267. Invalidate(nil);
  11268. end;
  11269. //----------------------------------------------------------------------------------------------------------------------
  11270. procedure TVTHeader.PrepareDrag(P, Start: TPoint);
  11271. // Initializes dragging of the header, P is the current mouse postion and Start the initial mouse position.
  11272. var
  11273. Image: TBitmap;
  11274. ImagePos: TPoint;
  11275. DragColumn: TVirtualTreeColumn;
  11276. RTLOffset: Integer;
  11277. begin
  11278. // Determine initial position of drag image (screen coordinates).
  11279. FColumns.FDropTarget := NoColumn;
  11280. Start := Treeview.ScreenToClient(Start);
  11281. Inc(Start.Y, FHeight);
  11282. FColumns.FDragIndex := FColumns.ColumnFromPosition(Start);
  11283. DragColumn := FColumns[FColumns.FDragIndex];
  11284. Image := TBitmap.Create;
  11285. with Image do
  11286. try
  11287. PixelFormat := pf32Bit;
  11288. Width := DragColumn.Width;
  11289. Height := FHeight;
  11290. // Erase the entire image with the color key value, for the case not everything
  11291. // in the image is covered by the header image.
  11292. Canvas.Brush.Color := clBtnFace;
  11293. Canvas.FillRect(Rect(0, 0, Width, Height));
  11294. if TreeView.UseRightToLeftAlignment then
  11295. RTLOffset := Treeview.ComputeRTLOffset
  11296. else
  11297. RTLOffset := 0;
  11298. with DragColumn do
  11299. FColumns.PaintHeader(Canvas, Rect(FLeft, 0, FLeft + Width, Height), Point(-RTLOffset, 0), RTLOffset);
  11300. if Treeview.UseRightToLeftAlignment then
  11301. ImagePos := Treeview.ClientToScreen(Point(DragColumn.Left + Treeview.ComputeRTLOffset(True), 0))
  11302. else
  11303. ImagePos := Treeview.ClientToScreen(Point(DragColumn.Left, 0));
  11304. // Column rectangles are given in local window coordinates not client coordinates.
  11305. Dec(ImagePos.Y, FHeight);
  11306. if hoRestrictDrag in FOptions then
  11307. FDragImage.MoveRestriction := dmrHorizontalOnly
  11308. else
  11309. FDragImage.MoveRestriction := dmrNone;
  11310. FDragImage.PrepareDrag(Image, ImagePos, P, nil);
  11311. FDragImage.ShowDragImage;
  11312. finally
  11313. Image.Free;
  11314. end;
  11315. end;
  11316. //----------------------------------------------------------------------------------------------------------------------
  11317. procedure TVTHeader.ReadColumns(Reader: TReader);
  11318. begin
  11319. Include(FStates, hsLoading);
  11320. Columns.Clear;
  11321. Reader.ReadValue;
  11322. Reader.ReadCollection(Columns);
  11323. Exclude(FStates, hsLoading);
  11324. end;
  11325. //----------------------------------------------------------------------------------------------------------------------
  11326. procedure TVTHeader.RecalculateHeader;
  11327. // Initiate a recalculation of the non-client area of the owner tree.
  11328. begin
  11329. if Treeview.HandleAllocated then
  11330. begin
  11331. Treeview.UpdateHeaderRect;
  11332. SetWindowPos(Treeview.Handle, 0, 0, 0, 0, 0, SWP_FRAMECHANGED or SWP_NOMOVE or SWP_NOACTIVATE or SWP_NOOWNERZORDER or
  11333. SWP_NOSENDCHANGING or SWP_NOSIZE or SWP_NOZORDER);
  11334. end;
  11335. end;
  11336. //----------------------------------------------------------------------------------------------------------------------
  11337. procedure TVTHeader.RescaleHeader;
  11338. // Rescale the fixed elements (fixed columns, header itself) to FixedAreaConstraints.
  11339. var
  11340. FixedWidth,
  11341. MaxFixedWidth,
  11342. MinFixedWidth: Integer;
  11343. //--------------- local function --------------------------------------------
  11344. procedure ComputeConstraints;
  11345. var
  11346. I: TColumnIndex;
  11347. begin
  11348. with FColumns do
  11349. begin
  11350. I := GetFirstVisibleColumn;
  11351. while I > NoColumn do
  11352. begin
  11353. if (coFixed in FColumns[I].Options) and (FColumns[I].Width < FColumns[I].MinWidth) then
  11354. FColumns[I].FWidth := FColumns[I].FMinWidth;
  11355. I := GetNextVisibleColumn(I);
  11356. end;
  11357. FixedWidth := GetVisibleFixedWidth;
  11358. end;
  11359. with FFixedAreaConstraints do
  11360. begin
  11361. MinFixedWidth := (TreeView.ClientWidth * FMinWidthPercent) div 100;
  11362. MaxFixedWidth := (TreeView.ClientWidth * FMaxWidthPercent) div 100;
  11363. end;
  11364. end;
  11365. //----------- end local function --------------------------------------------
  11366. begin
  11367. if ([csLoading, csReading, csWriting, csDestroying] * Treeview.ComponentState = []) and not
  11368. (hsLoading in FStates) and Treeview.HandleAllocated then
  11369. begin
  11370. Include(FStates, hsScaling);
  11371. SetHeight(FHeight);
  11372. RecalculateHeader;
  11373. with FFixedAreaConstraints do
  11374. if (FMinHeightPercent > 0) or (FMaxHeightPercent > 0) then
  11375. begin
  11376. ComputeConstraints;
  11377. with FColumns do
  11378. if (FMaxWidthPercent > 0) and (FixedWidth > MaxFixedWidth) then
  11379. ResizeColumns(MaxFixedWidth - FixedWidth, 0, Count - 1, [coVisible, coFixed])
  11380. else
  11381. if (FMinWidthPercent > 0) and (FixedWidth < MinFixedWidth) then
  11382. ResizeColumns(MinFixedWidth - FixedWidth, 0, Count - 1, [coVisible, coFixed]);
  11383. FColumns.UpdatePositions;
  11384. end;
  11385. Exclude(FStates, hsScaling);
  11386. Exclude(FStates, hsNeedScaling);
  11387. end;
  11388. end;
  11389. //----------------------------------------------------------------------------------------------------------------------
  11390. procedure TVTHeader.UpdateMainColumn;
  11391. // Called once the load process of the owner tree is done.
  11392. begin
  11393. if FMainColumn < 0 then
  11394. FMainColumn := 0;
  11395. if FMainColumn > FColumns.Count - 1 then
  11396. FMainColumn := FColumns.Count - 1;
  11397. end;
  11398. //----------------------------------------------------------------------------------------------------------------------
  11399. procedure TVTHeader.UpdateSpringColumns;
  11400. var
  11401. I: TColumnIndex;
  11402. SpringCount: Integer;
  11403. Sign: Integer;
  11404. ChangeBy: Single;
  11405. Difference: Single;
  11406. NewAccumulator: Single;
  11407. begin
  11408. with TreeView do
  11409. ChangeBy := FHeaderRect.Right - FHeaderRect.Left - FLastWidth;
  11410. if (hoAutoSpring in FOptions) and (FLastWidth <> 0) and (ChangeBy <> 0) then
  11411. begin
  11412. // Stay positive if downsizing the control.
  11413. if ChangeBy < 0 then
  11414. Sign := -1
  11415. else
  11416. Sign := 1;
  11417. ChangeBy := Abs(ChangeBy);
  11418. // Count how many columns have spring enabled.
  11419. SpringCount := 0;
  11420. for I := 0 to FColumns.Count-1 do
  11421. if [coVisible, coAutoSpring] * FColumns[I].FOptions = [coVisible, coAutoSpring] then
  11422. Inc(SpringCount);
  11423. if SpringCount > 0 then
  11424. begin
  11425. // Calculate the size to add/sub to each columns.
  11426. Difference := ChangeBy / SpringCount;
  11427. // Adjust the column's size accumulators and resize if the result is >= 1.
  11428. for I := 0 to FColumns.Count - 1 do
  11429. if [coVisible, coAutoSpring] * FColumns[I].FOptions = [coVisible, coAutoSpring] then
  11430. begin
  11431. // Sum up rest changes from previous runs and the amount from this one and store it in the
  11432. // column. If there is at least one pixel difference then do a resize and reset the accumulator.
  11433. NewAccumulator := FColumns[I].FSpringRest + Difference;
  11434. // Set new width if at least one pixel size difference is reached.
  11435. if NewAccumulator >= 1 then
  11436. FColumns[I].SetWidth(FColumns[I].FWidth + (Trunc(NewAccumulator) * Sign));
  11437. FColumns[I].FSpringRest := Frac(NewAccumulator);
  11438. // Keep track of the size count.
  11439. ChangeBy := ChangeBy - Difference;
  11440. // Exit loop if resize count drops below freezing point.
  11441. if ChangeBy < 0 then
  11442. Break;
  11443. end;
  11444. end;
  11445. end;
  11446. with TreeView do
  11447. FLastWidth := FHeaderRect.Right - FHeaderRect.Left;
  11448. end;
  11449. //----------------------------------------------------------------------------------------------------------------------
  11450. type
  11451. // --- HACK WARNING!
  11452. // This type cast is a partial rewrite of the private section of TWriter. The purpose is to have access to
  11453. // the FPropPath member, which is otherwise not accessible. The reason why this access is needed is that
  11454. // with nested components this member contains unneeded property path information. These information prevent
  11455. // successful load of the stored properties later.
  11456. // In Classes.pas you can see that FPropPath is reset several times to '' to prevent this case for certain properies.
  11457. // Unfortunately, there is no clean way for us here to do the same.
  11458. {$hints off}
  11459. TWriterHack = class(TFiler)
  11460. private
  11461. FRootAncestor: TComponent;
  11462. FPropPath: string;
  11463. end;
  11464. {$hints on}
  11465. procedure TVTHeader.WriteColumns(Writer: TWriter);
  11466. // Write out the columns but take care for the case VT is a nested component.
  11467. var
  11468. LastPropPath: string;
  11469. begin
  11470. // Save last property path for restoration.
  11471. LastPropPath := TWriterHack(Writer).FPropPath;
  11472. try
  11473. // If VT is a nested component then this path contains the name of the parent component at this time
  11474. // (otherwise it is already empty). This path is then combined with the property name under which the tree
  11475. // is defined in the parent component. Unfortunately, the load code in Classes.pas does not consider this case
  11476. // is then unable to load this property.
  11477. TWriterHack(Writer).FPropPath := '';
  11478. Writer.WriteCollection(Columns);
  11479. finally
  11480. TWriterHack(Writer).FPropPath := LastPropPath;
  11481. end;
  11482. end;
  11483. //----------------------------------------------------------------------------------------------------------------------
  11484. function TVTHeader.AllowFocus(ColumnIndex: TColumnIndex): Boolean;
  11485. begin
  11486. Result := False;
  11487. if not FColumns.IsValidColumn(ColumnIndex) then
  11488. Exit; // Just in case.
  11489. Result := (coAllowFocus in FColumns[ColumnIndex].Options);
  11490. end;
  11491. //----------------------------------------------------------------------------------------------------------------------
  11492. procedure TVTHeader.Assign(Source: TPersistent);
  11493. begin
  11494. if Source is TVTHeader then
  11495. begin
  11496. AutoSizeIndex := TVTHeader(Source).AutoSizeIndex;
  11497. Background := TVTHeader(Source).Background;
  11498. Columns := TVTHeader(Source).Columns;
  11499. Font := TVTHeader(Source).Font;
  11500. FixedAreaConstraints.Assign(TVTHeader(Source).FixedAreaConstraints);
  11501. Height := TVTHeader(Source).Height;
  11502. Images := TVTHeader(Source).Images;
  11503. MainColumn := TVTHeader(Source).MainColumn;
  11504. Options := TVTHeader(Source).Options;
  11505. ParentFont := TVTHeader(Source).ParentFont;
  11506. PopupMenu := TVTHeader(Source).PopupMenu;
  11507. SortColumn := TVTHeader(Source).SortColumn;
  11508. SortDirection := TVTHeader(Source).SortDirection;
  11509. Style := TVTHeader(Source).Style;
  11510. RescaleHeader;
  11511. end
  11512. else
  11513. inherited;
  11514. end;
  11515. //----------------------------------------------------------------------------------------------------------------------
  11516. procedure TVTHeader.AutoFitColumns(Animated: Boolean = True; SmartAutoFitType: TSmartAutoFitType = smaUseColumnOption;
  11517. RangeStartCol: Integer = NoColumn; RangeEndCol: Integer = NoColumn);
  11518. //--------------- local functions -------------------------------------------
  11519. function GetUseSmartColumnWidth(ColumnIndex: TColumnIndex): Boolean;
  11520. begin
  11521. Result := False;
  11522. case SmartAutoFitType of
  11523. smaAllColumns:
  11524. Result := True;
  11525. smaNoColumn:
  11526. Result := False;
  11527. smaUseColumnOption:
  11528. Result := coSmartResize in FColumns.Items[ColumnIndex].FOptions;
  11529. end;
  11530. end;
  11531. //----------------------------------------------------------------------------
  11532. procedure DoAutoFitColumn(Column: TColumnIndex);
  11533. begin
  11534. with FColumns do
  11535. if ([coResizable, coVisible] * Items[FPositionToIndex[Column]].FOptions = [coResizable, coVisible]) and
  11536. DoBeforeAutoFitColumn(FPositionToIndex[Column], SmartAutoFitType) and not TreeView.OperationCanceled then
  11537. begin
  11538. if Animated then
  11539. AnimatedResize(FPositionToIndex[Column], Treeview.GetMaxColumnWidth(FPositionToIndex[Column],
  11540. GetUseSmartColumnWidth(FPositionToIndex[Column])))
  11541. else
  11542. FColumns[FPositionToIndex[Column]].Width := Treeview.GetMaxColumnWidth(FPositionToIndex[Column],
  11543. GetUseSmartColumnWidth(FPositionToIndex[Column]));
  11544. DoAfterAutoFitColumn(FPositionToIndex[Column]);
  11545. end;
  11546. end;
  11547. //--------------- end local functions ----------------------------------------
  11548. var
  11549. I: Integer;
  11550. StartCol,
  11551. EndCol: Integer;
  11552. begin
  11553. StartCol := Max(NoColumn + 1, RangeStartCol);
  11554. if RangeEndCol <= NoColumn then
  11555. EndCol := FColumns.Count - 1
  11556. else
  11557. EndCol := Min(RangeEndCol, FColumns.Count - 1);
  11558. if StartCol > EndCol then
  11559. Exit; // nothing to do
  11560. TreeView.StartOperation(okAutoFitColumns);
  11561. try
  11562. if Assigned(TreeView.FOnBeforeAutoFitColumns) then
  11563. TreeView.FOnBeforeAutoFitColumns(Self, SmartAutoFitType);
  11564. for I := StartCol to EndCol do
  11565. DoAutoFitColumn(I);
  11566. if Assigned(TreeView.FOnAfterAutoFitColumns) then
  11567. TreeView.FOnAfterAutoFitColumns(Self);
  11568. finally
  11569. Treeview.EndOperation(okAutoFitColumns);
  11570. end;
  11571. end;
  11572. //----------------------------------------------------------------------------------------------------------------------
  11573. function TVTHeader.InHeader(P: TPoint): Boolean;
  11574. // Determines whether the given point (client coordinates!) is within the header rectangle (non-client coordinates).
  11575. var
  11576. R, RW: TRect;
  11577. begin
  11578. R := Treeview.FHeaderRect;
  11579. // Current position of the owner in screen coordinates.
  11580. GetWindowRect(Treeview.Handle, RW);
  11581. // Convert to client coordinates.
  11582. MapWindowPoints(0, Treeview.Handle, RW, 2);
  11583. // Consider the header within this rectangle.
  11584. OffsetRect(R, RW.Left, RW.Top);
  11585. Result := PtInRect(R, P);
  11586. end;
  11587. //----------------------------------------------------------------------------------------------------------------------
  11588. function TVTHeader.InHeaderSplitterArea(P: TPoint): Boolean;
  11589. // Determines whether the given point (client coordinates!) hits the horizontal splitter area of the header.
  11590. var
  11591. R, RW: TRect;
  11592. begin
  11593. if (P.Y > 2) or (P.Y < -2) or not (hoVisible in FOptions) then
  11594. Result := False
  11595. else
  11596. begin
  11597. R := Treeview.FHeaderRect;
  11598. Inc(R.Bottom, 2);
  11599. // Current position of the owner in screen coordinates.
  11600. GetWindowRect(Treeview.Handle, RW);
  11601. // Convert to client coordinates.
  11602. MapWindowPoints(0, Treeview.Handle, RW, 2);
  11603. // Consider the header within this rectangle.
  11604. OffsetRect(R, RW.Left, RW.Top);
  11605. Result := PtInRect(R, P);
  11606. end;
  11607. end;
  11608. //----------------------------------------------------------------------------------------------------------------------
  11609. procedure TVTHeader.Invalidate(Column: TVirtualTreeColumn; ExpandToBorder: Boolean = False);
  11610. // Because the header is in the non-client area of the tree it needs some special handling in order to initiate its
  11611. // repainting.
  11612. // If ExpandToBorder is True then not only the given column but everything or (depending on hoFullRepaintOnResize) just
  11613. // everything to its right (or left, in RTL mode) will be invalidated (useful for resizing). This makes only sense when
  11614. // a column is given.
  11615. var
  11616. R, RW: TRect;
  11617. begin
  11618. if (hoVisible in FOptions) and Treeview.HandleAllocated then
  11619. with Treeview do
  11620. begin
  11621. if Column = nil then
  11622. R := FHeaderRect
  11623. else
  11624. begin
  11625. R := Column.GetRect;
  11626. if not (coFixed in Column.Options) then
  11627. OffsetRect(R, -FEffectiveOffsetX, 0);
  11628. if UseRightToLeftAlignment then
  11629. OffsetRect(R, ComputeRTLOffset, 0);
  11630. if ExpandToBorder then
  11631. begin
  11632. if (hoFullRepaintOnResize in FHeader.FOptions) then
  11633. begin
  11634. R.Left := FHeaderRect.Left;
  11635. R.Right := FHeaderRect.Right;
  11636. end
  11637. else
  11638. begin
  11639. if UseRightToLeftAlignment then
  11640. R.Left := FHeaderRect.Left
  11641. else
  11642. R.Right := FHeaderRect.Right;
  11643. end;
  11644. end;
  11645. end;
  11646. // Current position of the owner in screen coordinates.
  11647. GetWindowRect(Handle, RW);
  11648. // Consider the header within this rectangle.
  11649. OffsetRect(R, RW.Left, RW.Top);
  11650. // Expressed in client coordinates (because RedrawWindow wants them so, they will actually become negative).
  11651. MapWindowPoints(0, Handle, R, 2);
  11652. RedrawWindow(Handle, @R, 0, RDW_FRAME or RDW_INVALIDATE or RDW_VALIDATE or RDW_NOINTERNALPAINT or
  11653. RDW_NOERASE or RDW_NOCHILDREN);
  11654. end;
  11655. end;
  11656. //----------------------------------------------------------------------------------------------------------------------
  11657. procedure TVTHeader.LoadFromStream(const Stream: TStream);
  11658. // restore the state of the header from the given stream
  11659. var
  11660. Dummy,
  11661. Version: Integer;
  11662. S: AnsiString;
  11663. OldOptions: TVTHeaderOptions;
  11664. begin
  11665. Include(FStates, hsLoading);
  11666. with Stream do
  11667. try
  11668. // Switch off all options which could influence loading the columns (they will be later set again).
  11669. OldOptions := FOptions;
  11670. FOptions := [];
  11671. // Determine whether the stream contains data without a version number.
  11672. ReadBuffer(Dummy, SizeOf(Dummy));
  11673. if Dummy > -1 then
  11674. begin
  11675. // Seek back to undo the read operation if this is an old stream format.
  11676. Seek(-SizeOf(Dummy), soFromCurrent);
  11677. Version := -1;
  11678. end
  11679. else // Read version number if this is a "versionized" format.
  11680. ReadBuffer(Version, SizeOf(Version));
  11681. Columns.LoadFromStream(Stream, Version);
  11682. ReadBuffer(Dummy, SizeOf(Dummy));
  11683. AutoSizeIndex := Dummy;
  11684. ReadBuffer(Dummy, SizeOf(Dummy));
  11685. Background := Dummy;
  11686. ReadBuffer(Dummy, SizeOf(Dummy));
  11687. Height := Dummy;
  11688. ReadBuffer(Dummy, SizeOf(Dummy));
  11689. FOptions := OldOptions;
  11690. Options := TVTHeaderOptions(Dummy);
  11691. // PopupMenu is neither saved nor restored
  11692. ReadBuffer(Dummy, SizeOf(Dummy));
  11693. Style := TVTHeaderStyle(Dummy);
  11694. // TFont has no own save routine so we do it manually
  11695. with Font do
  11696. begin
  11697. ReadBuffer(Dummy, SizeOf(Dummy));
  11698. Color := Dummy;
  11699. ReadBuffer(Dummy, SizeOf(Dummy));
  11700. Height := Dummy;
  11701. ReadBuffer(Dummy, SizeOf(Dummy));
  11702. SetLength(S, Dummy);
  11703. ReadBuffer(PAnsiChar(S)^, Dummy);
  11704. if VTHeaderStreamVersion >= 4 then
  11705. {$if CompilerVersion >= 20}
  11706. Name := UTF8ToString(S)
  11707. {$else}
  11708. Name := UTF8Decode(S)
  11709. {$ifend}
  11710. else
  11711. Name := S;
  11712. ReadBuffer(Dummy, SizeOf(Dummy));
  11713. Pitch := TFontPitch(Dummy);
  11714. ReadBuffer(Dummy, SizeOf(Dummy));
  11715. Style := TFontStyles(Byte(Dummy));
  11716. end;
  11717. // Read data introduced by stream version 1+.
  11718. if Version > 0 then
  11719. begin
  11720. ReadBuffer(Dummy, SizeOf(Dummy));
  11721. MainColumn := Dummy;
  11722. ReadBuffer(Dummy, SizeOf(Dummy));
  11723. SortColumn := Dummy;
  11724. ReadBuffer(Dummy, SizeOf(Dummy));
  11725. SortDirection := TSortDirection(Byte(Dummy));
  11726. end;
  11727. // Read data introduced by stream version 5+.
  11728. if Version > 4 then
  11729. begin
  11730. ReadBuffer(Dummy, SizeOf(Dummy));
  11731. ParentFont := Boolean(Dummy);
  11732. ReadBuffer(Dummy, SizeOf(Dummy));
  11733. FMaxHeight := Integer(Dummy);
  11734. ReadBuffer(Dummy, SizeOf(Dummy));
  11735. FMinHeight := Integer(Dummy);
  11736. ReadBuffer(Dummy, SizeOf(Dummy));
  11737. FDefaultHeight := Integer(Dummy);
  11738. with FFixedAreaConstraints do
  11739. begin
  11740. ReadBuffer(Dummy, SizeOf(Dummy));
  11741. FMaxHeightPercent := TVTConstraintPercent(Dummy);
  11742. ReadBuffer(Dummy, SizeOf(Dummy));
  11743. FMaxWidthPercent := TVTConstraintPercent(Dummy);
  11744. ReadBuffer(Dummy, SizeOf(Dummy));
  11745. FMinHeightPercent := TVTConstraintPercent(Dummy);
  11746. ReadBuffer(Dummy, SizeOf(Dummy));
  11747. FMinWidthPercent := TVTConstraintPercent(Dummy);
  11748. end;
  11749. end;
  11750. finally
  11751. Exclude(FStates, hsLoading);
  11752. Treeview.DoColumnResize(NoColumn);
  11753. end;
  11754. end;
  11755. //----------------------------------------------------------------------------------------------------------------------
  11756. function TVTHeader.ResizeColumns(ChangeBy: Integer; RangeStartCol: TColumnIndex; RangeEndCol: TColumnIndex;
  11757. Options: TVTColumnOptions = [coVisible]): Integer;
  11758. // Distribute the given width change to a range of columns. A 'fair' way is used to distribute ChangeBy to the columns,
  11759. // while ensuring that everything that can be distributed will be distributed.
  11760. var
  11761. Start,
  11762. I: TColumnIndex;
  11763. ColCount,
  11764. ToGo,
  11765. Sign,
  11766. Rest,
  11767. MaxDelta,
  11768. Difference: Integer;
  11769. Constraints,
  11770. Widths: array of Integer;
  11771. BonusPixel: Boolean;
  11772. //--------------- local functions -------------------------------------------
  11773. function IsResizable (Column: TColumnIndex): Boolean;
  11774. begin
  11775. if BonusPixel then
  11776. Result := Widths[Column - RangeStartCol] < Constraints[Column - RangeStartCol]
  11777. else
  11778. Result := Widths[Column - RangeStartCol] > Constraints[Column - RangeStartCol];
  11779. end;
  11780. //---------------------------------------------------------------------------
  11781. procedure IncDelta(Column: TColumnIndex);
  11782. begin
  11783. if BonusPixel then
  11784. Inc(MaxDelta, FColumns[Column].MaxWidth - Widths[Column - RangeStartCol])
  11785. else
  11786. Inc(MaxDelta, Widths[Column - RangeStartCol] - Constraints[Column - RangeStartCol]);
  11787. end;
  11788. //---------------------------------------------------------------------------
  11789. function ChangeWidth(Column: TColumnIndex; Delta: Integer): Integer;
  11790. begin
  11791. if Delta > 0 then
  11792. Delta := Min(Delta, Constraints[Column - RangeStartCol] - Widths[Column - RangeStartCol])
  11793. else
  11794. Delta := Max(Delta, Constraints[Column - RangeStartCol] - Widths[Column - RangeStartCol]);
  11795. Inc(Widths[Column - RangeStartCol], Delta);
  11796. Dec(ToGo, Abs(Delta));
  11797. Result := Abs(Delta);
  11798. end;
  11799. //---------------------------------------------------------------------------
  11800. function ReduceConstraints: Boolean;
  11801. var
  11802. MaxWidth,
  11803. MaxReserveCol,
  11804. Column: TColumnIndex;
  11805. begin
  11806. Result := True;
  11807. if not (hsScaling in FStates) or BonusPixel then
  11808. Exit;
  11809. MaxWidth := 0;
  11810. MaxReserveCol := NoColumn;
  11811. for Column := RangeStartCol to RangeEndCol do
  11812. if (Options * FColumns[Column].FOptions = Options) and
  11813. (FColumns[Column].FWidth > MaxWidth) then
  11814. begin
  11815. MaxWidth := Widths[Column - RangeStartCol];
  11816. MaxReserveCol := Column;
  11817. end;
  11818. if (MaxReserveCol <= NoColumn) or (Constraints[MaxReserveCol - RangeStartCol] <= 10) then
  11819. Result := False
  11820. else
  11821. Dec(Constraints[MaxReserveCol - RangeStartCol],
  11822. Constraints[MaxReserveCol - RangeStartCol] div 10);
  11823. end;
  11824. //----------- end local functions -------------------------------------------
  11825. begin
  11826. Result := 0;
  11827. if ChangeBy <> 0 then
  11828. begin
  11829. // Do some initialization here
  11830. BonusPixel := ChangeBy > 0;
  11831. Sign := IfThen(BonusPixel, 1, -1);
  11832. Start := IfThen(BonusPixel, RangeStartCol, RangeEndCol);
  11833. ToGo := Abs(ChangeBy);
  11834. SetLength(Widths, RangeEndCol - RangeStartCol + 1);
  11835. SetLength(Constraints, RangeEndCol - RangeStartCol + 1);
  11836. for I := RangeStartCol to RangeEndCol do
  11837. begin
  11838. Widths[I - RangeStartCol] := FColumns[I].FWidth;
  11839. Constraints[I - RangeStartCol] := IfThen(BonusPixel, FColumns[I].MaxWidth, FColumns[I].MinWidth);
  11840. end;
  11841. repeat
  11842. repeat
  11843. MaxDelta := 0;
  11844. ColCount := 0;
  11845. for I := RangeStartCol to RangeEndCol do
  11846. if (Options * FColumns[I].FOptions = Options) and IsResizable(I) then
  11847. begin
  11848. Inc(ColCount);
  11849. IncDelta(I);
  11850. end;
  11851. if MaxDelta < Abs(ChangeBy) then
  11852. if not ReduceConstraints then
  11853. Break;
  11854. until (MaxDelta >= Abs(ChangeBy)) or not (hsScaling in FStates);
  11855. if ColCount = 0 then
  11856. Break;
  11857. ToGo := Min(ToGo, MaxDelta);
  11858. Difference := ToGo div ColCount;
  11859. Rest := ToGo mod ColCount;
  11860. if Difference > 0 then
  11861. for I := RangeStartCol to RangeEndCol do
  11862. if (Options * FColumns[I].FOptions = Options) and IsResizable(I) then
  11863. ChangeWidth(I, Difference * Sign);
  11864. // Now distribute Rest.
  11865. I := Start;
  11866. while Rest > 0 do
  11867. begin
  11868. if (Options * FColumns[I].FOptions = Options) and IsResizable(I) then
  11869. if FColumns[I].FBonusPixel <> BonusPixel then
  11870. begin
  11871. Dec(Rest, ChangeWidth(I, Sign));
  11872. FColumns[I].FBonusPixel := BonusPixel;
  11873. end;
  11874. Inc(I, Sign);
  11875. if (BonusPixel and (I > RangeEndCol)) or (not BonusPixel and (I < RangeStartCol)) then
  11876. begin
  11877. for I := RangeStartCol to RangeEndCol do
  11878. if Options * FColumns[I].FOptions = Options then
  11879. FColumns[I].FBonusPixel := not FColumns[I].FBonusPixel;
  11880. I := Start;
  11881. end;
  11882. end;
  11883. until ToGo <= 0;
  11884. // Now set the computed widths. We also compute the result here.
  11885. Include(FStates, hsResizing);
  11886. for I := RangeStartCol to RangeEndCol do
  11887. if (Options * FColumns[I].FOptions = Options) then
  11888. begin
  11889. Inc(Result, Widths[I - RangeStartCol] - FColumns[I].FWidth);
  11890. FColumns[I].SetWidth(Widths[I - RangeStartCol]);
  11891. end;
  11892. Exclude(FStates, hsResizing);
  11893. end;
  11894. end;
  11895. //----------------------------------------------------------------------------------------------------------------------
  11896. procedure TVTHeader.RestoreColumns;
  11897. // Restores all columns to their width which they had before they have been auto fitted.
  11898. var
  11899. I: TColumnIndex;
  11900. begin
  11901. with FColumns do
  11902. for I := Count - 1 downto 0 do
  11903. if [coResizable, coVisible] * Items[FPositionToIndex[I]].FOptions = [coResizable, coVisible] then
  11904. Items[I].RestoreLastWidth;
  11905. end;
  11906. //----------------------------------------------------------------------------------------------------------------------
  11907. procedure TVTHeader.SaveToStream(const Stream: TStream);
  11908. // Saves the complete state of the header into the provided stream.
  11909. var
  11910. Dummy: Integer;
  11911. Tmp: AnsiString;
  11912. begin
  11913. with Stream do
  11914. begin
  11915. // In previous version of VT was no header stream version defined.
  11916. // For feature enhancements it is necessary, however, to know which stream
  11917. // format we are trying to load.
  11918. // In order to distict from non-version streams an indicator is inserted.
  11919. Dummy := -1;
  11920. WriteBuffer(Dummy, SizeOf(Dummy));
  11921. // Write current stream version number, nothing more is required at the time being.
  11922. Dummy := VTHeaderStreamVersion;
  11923. WriteBuffer(Dummy, SizeOf(Dummy));
  11924. // Save columns in case they depend on certain options (like auto size).
  11925. Columns.SaveToStream(Stream);
  11926. Dummy := FAutoSizeIndex;
  11927. WriteBuffer(Dummy, SizeOf(Dummy));
  11928. Dummy := FBackground;
  11929. WriteBuffer(Dummy, SizeOf(Dummy));
  11930. Dummy := FHeight;
  11931. WriteBuffer(Dummy, SizeOf(Dummy));
  11932. Dummy := Integer(FOptions);
  11933. WriteBuffer(Dummy, SizeOf(Dummy));
  11934. // PopupMenu is neither saved nor restored
  11935. Dummy := Ord(FStyle);
  11936. WriteBuffer(Dummy, SizeOf(Dummy));
  11937. // TFont has no own save routine so we do it manually
  11938. with Font do
  11939. begin
  11940. Dummy := Color;
  11941. WriteBuffer(Dummy, SizeOf(Dummy));
  11942. // Need only to write one: size or height, I decided to write height.
  11943. Dummy := Height;
  11944. WriteBuffer(Dummy, SizeOf(Dummy));
  11945. Tmp := UTF8Encode(Name);
  11946. Dummy := Length(Tmp);
  11947. WriteBuffer(Dummy, SizeOf(Dummy));
  11948. WriteBuffer(PAnsiChar(Tmp)^, Dummy);
  11949. Dummy := Ord(Pitch);
  11950. WriteBuffer(Dummy, SizeOf(Dummy));
  11951. Dummy := Byte(Style);
  11952. WriteBuffer(Dummy, SizeOf(Dummy));
  11953. end;
  11954. // Data introduced by stream version 1.
  11955. Dummy := FMainColumn;
  11956. WriteBuffer(Dummy, SizeOf(Dummy));
  11957. Dummy := FSortColumn;
  11958. WriteBuffer(Dummy, SizeOf(Dummy));
  11959. Dummy := Byte(FSortDirection);
  11960. WriteBuffer(Dummy, SizeOf(Dummy));
  11961. // Data introduced by stream version 5.
  11962. Dummy := Integer(ParentFont);
  11963. WriteBuffer(Dummy, SizeOf(Dummy));
  11964. Dummy := Integer(FMaxHeight);
  11965. WriteBuffer(Dummy, SizeOf(Dummy));
  11966. Dummy := Integer(FMinHeight);
  11967. WriteBuffer(Dummy, SizeOf(Dummy));
  11968. Dummy := Integer(FDefaultHeight);
  11969. WriteBuffer(Dummy, SizeOf(Dummy));
  11970. with FFixedAreaConstraints do
  11971. begin
  11972. Dummy := Integer(FMaxHeightPercent);
  11973. WriteBuffer(Dummy, SizeOf(Dummy));
  11974. Dummy := Integer(FMaxWidthPercent);
  11975. WriteBuffer(Dummy, SizeOf(Dummy));
  11976. Dummy := Integer(FMinHeightPercent);
  11977. WriteBuffer(Dummy, SizeOf(Dummy));
  11978. Dummy := Integer(FMinWidthPercent);
  11979. WriteBuffer(Dummy, SizeOf(Dummy));
  11980. end;
  11981. end;
  11982. end;
  11983. //----------------- TScrollBarOptions ----------------------------------------------------------------------------------
  11984. constructor TScrollBarOptions.Create(AOwner: TBaseVirtualTree);
  11985. begin
  11986. inherited Create;
  11987. FOwner := AOwner;
  11988. FAlwaysVisible := False;
  11989. FScrollBarStyle := sbmRegular;
  11990. FScrollBars := ssBoth;
  11991. FIncrementX := 20;
  11992. FIncrementY := 20;
  11993. end;
  11994. //----------------------------------------------------------------------------------------------------------------------
  11995. procedure TScrollBarOptions.SetAlwaysVisible(Value: Boolean);
  11996. begin
  11997. if FAlwaysVisible <> Value then
  11998. begin
  11999. FAlwaysVisible := Value;
  12000. if not (csLoading in FOwner.ComponentState) and FOwner.HandleAllocated then
  12001. FOwner.RecreateWnd;
  12002. end;
  12003. end;
  12004. //----------------------------------------------------------------------------------------------------------------------
  12005. procedure TScrollBarOptions.SetScrollBars(Value: TScrollStyle);
  12006. begin
  12007. if FScrollBars <> Value then
  12008. begin
  12009. FScrollBars := Value;
  12010. if not (csLoading in FOwner.ComponentState) and FOwner.HandleAllocated then
  12011. FOwner.RecreateWnd;
  12012. end;
  12013. end;
  12014. //----------------------------------------------------------------------------------------------------------------------
  12015. procedure TScrollBarOptions.SetScrollBarStyle(Value: TScrollBarStyle);
  12016. begin
  12017. if FScrollBarStyle <> Value then
  12018. begin
  12019. FScrollBarStyle := Value;
  12020. end;
  12021. end;
  12022. //----------------------------------------------------------------------------------------------------------------------
  12023. function TScrollBarOptions.GetOwner: TPersistent;
  12024. begin
  12025. Result := FOwner;
  12026. end;
  12027. //----------------------------------------------------------------------------------------------------------------------
  12028. procedure TScrollBarOptions.Assign(Source: TPersistent);
  12029. begin
  12030. if Source is TScrollBarOptions then
  12031. begin
  12032. AlwaysVisible := TScrollBarOptions(Source).AlwaysVisible;
  12033. HorizontalIncrement := TScrollBarOptions(Source).HorizontalIncrement;
  12034. ScrollBars := TScrollBarOptions(Source).ScrollBars;
  12035. ScrollBarStyle := TScrollBarOptions(Source).ScrollBarStyle;
  12036. VerticalIncrement := TScrollBarOptions(Source).VerticalIncrement;
  12037. end
  12038. else
  12039. inherited;
  12040. end;
  12041. //----------------- TVTColors ------------------------------------------------------------------------------------------
  12042. constructor TVTColors.Create(AOwner: TBaseVirtualTree);
  12043. begin
  12044. FOwner := AOwner;
  12045. FColors[0] := clBtnShadow; // DisabledColor
  12046. FColors[1] := clHighlight; // DropMarkColor
  12047. FColors[2] := clHighLight; // DropTargetColor
  12048. FColors[3] := clHighLight; // FocusedSelectionColor
  12049. FColors[4] := clBtnFace; // GridLineColor
  12050. FColors[5] := clBtnShadow; // TreeLineColor
  12051. FColors[6] := clBtnFace; // UnfocusedSelectionColor
  12052. FColors[7] := clBtnFace; // BorderColor
  12053. FColors[8] := clWindowText; // HotColor
  12054. FColors[9] := clHighLight; // FocusedSelectionBorderColor
  12055. FColors[10] := clBtnFace; // UnfocusedSelectionBorderColor
  12056. FColors[11] := clHighlight; // DropTargetBorderColor
  12057. FColors[12] := clHighlight; // SelectionRectangleBlendColor
  12058. FColors[13] := clHighlight; // SelectionRectangleBorderColor
  12059. FColors[14] := clBtnShadow; // HeaderHotColor
  12060. FColors[15] := clHighlightText; // SelectionTextColor
  12061. FColors[16] := clBtnFace; // UnfocusedColor [IPK]
  12062. end;
  12063. //----------------------------------------------------------------------------------------------------------------------
  12064. function TVTColors.GetBackgroundColor: TColor;
  12065. begin
  12066. // XE2 VCL Style
  12067. {$IF CompilerVersion >= 23}
  12068. if FOwner.VclStyleEnabled {$IF CompilerVersion >= 24}and (seClient in FOwner.StyleElements){$IFEND} then
  12069. Result := StyleServices.GetStyleColor(scTreeView)
  12070. else
  12071. {$IFEND}
  12072. Result := FOwner.Color;
  12073. end;
  12074. //----------------------------------------------------------------------------------------------------------------------
  12075. function TVTColors.GetColor(const Index: Integer): TColor;
  12076. begin
  12077. {$IF CompilerVersion >= 23 }
  12078. if FOwner.VclStyleEnabled then
  12079. begin
  12080. case Index of
  12081. 0:
  12082. StyleServices.GetElementColor(StyleServices.GetElementDetails(ttItemDisabled), ecTextColor, Result); // DisabledColor
  12083. 1:
  12084. Result := StyleServices.GetSystemColor(clHighlight); // DropMarkColor
  12085. 2:
  12086. Result := StyleServices.GetSystemColor(clHighlight); // DropTargetColor
  12087. 3:
  12088. Result := StyleServices.GetSystemColor(clHighlight); // FocusedSelectionColor
  12089. 4:
  12090. Result := StyleServices.GetSystemColor(clBtnFace); // GridLineColor
  12091. 5:
  12092. StyleServices.GetElementColor(StyleServices.GetElementDetails(ttBranch), ecBorderColor, Result); // TreeLineColor
  12093. 6:
  12094. Result := StyleServices.GetSystemColor(clHighlight); // UnfocusedSelectionColor
  12095. 7:
  12096. Result := StyleServices.GetSystemColor(clBtnFace); // BorderColor
  12097. 8:
  12098. if not StyleServices.GetElementColor(StyleServices.GetElementDetails(ttItemHot), ecTextColor, Result) or
  12099. (Result <> clWindowText) then
  12100. Result := NodeFontColor; // HotColor
  12101. 9:
  12102. StyleServices.GetElementColor(StyleServices.GetElementDetails(ttItemSelected), ecFillColor, Result);
  12103. // FocusedSelectionBorderColor
  12104. 10:
  12105. Result := StyleServices.GetSystemColor(clHighlight); // UnfocusedSelectionBorderColor
  12106. 11:
  12107. Result := StyleServices.GetSystemColor(clBtnFace); // DropTargetBorderColor
  12108. 12:
  12109. Result := StyleServices.GetSystemColor(clHighlight); // SelectionRectangleBlendColor
  12110. 13:
  12111. Result := StyleServices.GetSystemColor(clHighlight); // SelectionRectangleBorderColor
  12112. 14:
  12113. StyleServices.GetElementColor(StyleServices.GetElementDetails(thHeaderItemNormal), ecTextColor, Result); // HeaderHotColor
  12114. 15:
  12115. if not StyleServices.GetElementColor(StyleServices.GetElementDetails(ttItemSelected), ecTextColor, Result) or
  12116. (Result <> clWindowText) then
  12117. Result := NodeFontColor; // SelectionTextColor
  12118. end;
  12119. end
  12120. else
  12121. {$IFEND}
  12122. Result := FColors[Index];
  12123. end;
  12124. //----------------------------------------------------------------------------------------------------------------------
  12125. function TVTColors.GetHeaderFontColor: TColor;
  12126. begin
  12127. // XE2+ VCL Style
  12128. {$IF CompilerVersion >= 23}
  12129. if FOwner.VclStyleEnabled {$IF CompilerVersion >= 24}and (seFont in FOwner.StyleElements){$IFEND} then
  12130. StyleServices.GetElementColor(StyleServices.GetElementDetails(thHeaderItemNormal), ecTextColor, Result)
  12131. else
  12132. {$IFEND}
  12133. Result := FOwner.FHeader.Font.Color;
  12134. end;
  12135. //----------------------------------------------------------------------------------------------------------------------
  12136. function TVTColors.GetNodeFontColor: TColor;
  12137. begin
  12138. {$IF CompilerVersion >= 23}
  12139. if FOwner.VclStyleEnabled {$IF CompilerVersion >= 24}and (seFont in FOwner.StyleElements){$IFEND} then
  12140. StyleServices.GetElementColor(StyleServices.GetElementDetails(ttItemNormal), ecTextColor, Result)
  12141. else
  12142. {$IFEND}
  12143. Result := FOwner.Font.Color;
  12144. end;
  12145. //----------------------------------------------------------------------------------------------------------------------
  12146. procedure TVTColors.SetColor(const Index: Integer; const Value: TColor);
  12147. begin
  12148. if FColors[Index] <> Value then
  12149. begin
  12150. FColors[Index] := Value;
  12151. if not (csLoading in FOwner.ComponentState) and FOwner.HandleAllocated then
  12152. begin
  12153. // Cause helper bitmap rebuild if the button color changed.
  12154. case Index of
  12155. 5:
  12156. begin
  12157. FOwner.PrepareBitmaps(True, False);
  12158. FOwner.Invalidate;
  12159. end;
  12160. 7:
  12161. RedrawWindow(FOwner.Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE or RDW_NOERASE or RDW_NOCHILDREN)
  12162. else
  12163. FOwner.Invalidate;
  12164. end;
  12165. end;
  12166. end;
  12167. end;
  12168. //----------------------------------------------------------------------------------------------------------------------
  12169. procedure TVTColors.Assign(Source: TPersistent);
  12170. begin
  12171. if Source is TVTColors then
  12172. begin
  12173. FColors := TVTColors(Source).FColors;
  12174. if FOwner.FUpdateCount = 0 then
  12175. FOwner.Invalidate;
  12176. end
  12177. else
  12178. inherited;
  12179. end;
  12180. //----------------- TClipboardFormats ----------------------------------------------------------------------------------
  12181. constructor TClipboardFormats.Create(AOwner: TBaseVirtualTree);
  12182. begin
  12183. FOwner := AOwner;
  12184. Sorted := True;
  12185. Duplicates := dupIgnore;
  12186. end;
  12187. //----------------------------------------------------------------------------------------------------------------------
  12188. function TClipboardFormats.Add(const S: string): Integer;
  12189. // Restrict additions to the clipbard formats to only those which are registered with the owner tree or one of its
  12190. // ancestors.
  12191. var
  12192. Format: Word;
  12193. RegisteredClass: TVirtualTreeClass;
  12194. begin
  12195. RegisteredClass := InternalClipboardFormats.FindFormat(S, Format);
  12196. if Assigned(RegisteredClass) and FOwner.ClassType.InheritsFrom(RegisteredClass) then
  12197. Result := inherited Add(S)
  12198. else
  12199. Result := -1;
  12200. end;
  12201. //----------------------------------------------------------------------------------------------------------------------
  12202. procedure TClipboardFormats.Insert(Index: Integer; const S: string);
  12203. // Restrict additions to the clipbard formats to only those which are registered with the owner tree or one of its
  12204. // ancestors.
  12205. var
  12206. Format: Word;
  12207. RegisteredClass: TVirtualTreeClass;
  12208. begin
  12209. RegisteredClass := InternalClipboardFormats.FindFormat(S, Format);
  12210. if Assigned(RegisteredClass) and FOwner.ClassType.InheritsFrom(RegisteredClass) then
  12211. inherited Insert(Index, S);
  12212. end;
  12213. //----------------- TBaseVirtualTree -----------------------------------------------------------------------------------
  12214. constructor TBaseVirtualTree.Create(AOwner: TComponent);
  12215. begin
  12216. if not Initialized then
  12217. InitializeGlobalStructures;
  12218. inherited;
  12219. ControlStyle := ControlStyle - [csSetCaption] + [csCaptureMouse, csOpaque, csReplicatable, csDisplayDragImage,
  12220. csReflector];
  12221. FTotalInternalDataSize := 0;
  12222. FNodeDataSize := -1;
  12223. Width := 200;
  12224. Height := 100;
  12225. TabStop := True;
  12226. ParentColor := False;
  12227. FDefaultNodeHeight := 18;
  12228. FDragOperations := [doCopy, doMove];
  12229. FHotCursor := crDefault;
  12230. FScrollBarOptions := TScrollBarOptions.Create(Self);
  12231. FFocusedColumn := NoColumn;
  12232. FDragImageKind := diComplete;
  12233. FLastSelectionLevel := -1;
  12234. FAnimationType := hatSystemDefault;
  12235. FSelectionBlendFactor := 128;
  12236. FIndent := 18;
  12237. FPlusBM := TBitmap.Create;
  12238. FHotPlusBM := TBitmap.Create;
  12239. FMinusBM := TBitmap.Create;
  12240. FHotMinusBM := TBitmap.Create;
  12241. FBorderStyle := bsSingle;
  12242. FButtonStyle := bsRectangle;
  12243. FButtonFillMode := fmTreeColor;
  12244. FHeader := GetHeaderClass.Create(Self);
  12245. // we have an own double buffer handling
  12246. inherited DoubleBuffered := False;
  12247. FCheckImageKind := ckSystemDefault;
  12248. FCheckImages := SystemCheckImages;
  12249. FImageChangeLink := TChangeLink.Create;
  12250. FImageChangeLink.OnChange := ImageListChange;
  12251. FStateChangeLink := TChangeLink.Create;
  12252. FStateChangeLink.OnChange := ImageListChange;
  12253. FCustomCheckChangeLink := TChangeLink.Create;
  12254. FCustomCheckChangeLink.OnChange := ImageListChange;
  12255. FAutoExpandDelay := 1000;
  12256. FAutoScrollDelay := 1000;
  12257. FAutoScrollInterval := 1;
  12258. FBackground := TPicture.Create;
  12259. FDefaultPasteMode := amAddChildLast;
  12260. FMargin := 4;
  12261. FTextMargin := 4;
  12262. FLastDragEffect := DROPEFFECT_NONE;
  12263. FDragType := dtOLE;
  12264. FDragHeight := 350;
  12265. FDragWidth := 200;
  12266. FColors := TVTColors.Create(Self);
  12267. FEditDelay := 1000;
  12268. FDragImage := TVTDragImage.Create(Self);
  12269. with FDragImage do
  12270. begin
  12271. Fade := True;
  12272. PostBlendBias := 0;
  12273. PreBlendBias := 0;
  12274. Transparency := 200;
  12275. end;
  12276. SetLength(FSingletonNodeArray, 1);
  12277. FAnimationDuration := 200;
  12278. FSearchTimeout := 1000;
  12279. FSearchStart := ssFocusedNode;
  12280. FNodeAlignment := naProportional;
  12281. FLineStyle := lsDotted;
  12282. FIncrementalSearch := isNone;
  12283. FClipboardFormats := TClipboardFormats.Create(Self);
  12284. FOptions := GetOptionsClass.Create(Self);
  12285. AddThreadReference;
  12286. FVclStyleEnabled := False;
  12287. // XE2+ VCL Style
  12288. {$if CompilerVersion >= 23 }
  12289. FSetOrRestoreBevelKindAndBevelWidth := False;
  12290. FSavedBevelKind := bkNone;
  12291. FSavedBorderWidth := 0;
  12292. {$ifend}
  12293. end;
  12294. //----------------------------------------------------------------------------------------------------------------------
  12295. destructor TBaseVirtualTree.Destroy;
  12296. begin
  12297. InterruptValidation();
  12298. Exclude(FOptions.FMiscOptions, toReadOnly);
  12299. ReleaseThreadReference(Self);
  12300. StopWheelPanning;
  12301. CancelEditNode;
  12302. // Just in case it didn't happen already release the edit link.
  12303. FEditLink := nil;
  12304. FClipboardFormats.Free;
  12305. // Clear will also free the drag manager if it is still alive.
  12306. Clear;
  12307. FDragImage.Free;
  12308. FColors.Free;
  12309. FBackground.Free;
  12310. FImageChangeLink.Free;
  12311. FStateChangeLink.Free;
  12312. FCustomCheckChangeLink.Free;
  12313. FScrollBarOptions.Free;
  12314. // The window handle must be destroyed before the header is freed because it is needed in WM_NCDESTROY.
  12315. if HandleAllocated then
  12316. DestroyWindowHandle;
  12317. // Release FDottedBrush in case WM_NCDESTROY hasn't been triggered.
  12318. if FDottedBrush <> 0 then
  12319. DeleteObject(FDottedBrush);
  12320. FDottedBrush := 0;
  12321. FHeader.Free;
  12322. FHeader := nil; // Do not use FreeAndNil() before checking issue #497
  12323. FreeAndNil(FOptions); // WM_NCDESTROY accesses FOptions
  12324. FreeMem(FRoot);
  12325. FPlusBM.Free;
  12326. FHotPlusBM.Free;
  12327. FMinusBM.Free;
  12328. FHotMinusBM.Free;
  12329. inherited;
  12330. end;
  12331. //----------------------------------------------------------------------------------------------------------------------
  12332. procedure TBaseVirtualTree.AdjustCoordinatesByIndent(var PaintInfo: TVTPaintInfo; Indent: Integer);
  12333. // During painting of the main column some coordinates must be adjusted due to the tree lines.
  12334. // The offset resulting from the tree lines and indentation level is given in Indent.
  12335. var
  12336. Offset: Integer;
  12337. begin
  12338. with PaintInfo do
  12339. begin
  12340. Offset := Indent * Integer(FIndent);
  12341. if BidiMode = bdLeftToRight then
  12342. begin
  12343. Inc(ContentRect.Left, Offset);
  12344. Inc(ImageInfo[iiNormal].XPos, Offset);
  12345. Inc(ImageInfo[iiState].XPos, Offset);
  12346. Inc(ImageInfo[iiCheck].XPos, Offset);
  12347. end
  12348. else
  12349. begin
  12350. Dec(ContentRect.Right, Offset);
  12351. Dec(ImageInfo[iiNormal].XPos, Offset);
  12352. Dec(ImageInfo[iiState].XPos, Offset);
  12353. Dec(ImageInfo[iiCheck].XPos, Offset);
  12354. end;
  12355. end;
  12356. end;
  12357. //----------------------------------------------------------------------------------------------------------------------
  12358. procedure TBaseVirtualTree.AdjustTotalCount(Node: PVirtualNode; Value: Integer; Relative: Boolean = False);
  12359. // Sets a node's total count to the given value and recursively adjusts the parent's total count
  12360. // (actually, the adjustment is done iteratively to avoid function call overheads).
  12361. var
  12362. Difference: Integer;
  12363. Run: PVirtualNode;
  12364. begin
  12365. if Relative then
  12366. Difference := Value
  12367. else
  12368. Difference := Value - Integer(Node.TotalCount);
  12369. if Difference <> 0 then
  12370. begin
  12371. Run := Node;
  12372. // Root node has as parent the tree view.
  12373. while Assigned(Run) and (Run <> Pointer(Self)) do
  12374. begin
  12375. Inc(Integer(Run.TotalCount), Difference);
  12376. Run := Run.Parent;
  12377. end;
  12378. end;
  12379. end;
  12380. //----------------------------------------------------------------------------------------------------------------------
  12381. procedure TBaseVirtualTree.AdjustTotalHeight(Node: PVirtualNode; Value: Integer; Relative: Boolean = False);
  12382. // Sets a node's total height to the given value and recursively adjusts the parent's total height.
  12383. var
  12384. Difference: Integer;
  12385. Run: PVirtualNode;
  12386. begin
  12387. if Relative then
  12388. Difference := Value
  12389. else
  12390. Difference := Value - Integer(Node.TotalHeight);
  12391. if Difference <> 0 then
  12392. begin
  12393. Run := Node;
  12394. repeat
  12395. Inc(Integer(Run.TotalHeight), Difference);
  12396. // If the node is not visible or the parent node is not expanded or we are already at the top
  12397. // then nothing more remains to do.
  12398. if not (vsVisible in Run.States) or (Run = FRoot) or
  12399. (Run.Parent = nil) or not (vsExpanded in Run.Parent.States) then
  12400. Break;
  12401. Run := Run.Parent;
  12402. until False;
  12403. end;
  12404. UpdateVerticalRange;
  12405. end;
  12406. //----------------------------------------------------------------------------------------------------------------------
  12407. function TBaseVirtualTree.CalculateCacheEntryCount: Integer;
  12408. // Calculates the size of the position cache.
  12409. begin
  12410. if FVisibleCount > 1 then
  12411. Result := Ceil(FVisibleCount / CacheThreshold)
  12412. else
  12413. Result := 0;
  12414. end;
  12415. //----------------------------------------------------------------------------------------------------------------------
  12416. procedure TBaseVirtualTree.CalculateVerticalAlignments(ShowImages, ShowStateImages: Boolean; Node: PVirtualNode;
  12417. var VAlign, VButtonAlign: Integer);
  12418. // Calculates the vertical alignment of the given node and its associated expand/collapse button during
  12419. // a node paint cycle depending on the required node alignment style.
  12420. begin
  12421. // For absolute alignment the calculation is trivial.
  12422. case FNodeAlignment of
  12423. naFromTop:
  12424. VAlign := Node.Align;
  12425. naFromBottom:
  12426. VAlign := Integer(NodeHeight[Node]) - Node.Align;
  12427. else // naProportional
  12428. // Consider button and line alignment, but make sure neither the image nor the button (whichever is taller)
  12429. // go out of the entire node height (100% means bottom alignment to the node's bounds).
  12430. if ShowImages or ShowStateImages then
  12431. begin
  12432. if ShowImages then
  12433. VAlign := GetNodeImageSize(Node).cy
  12434. else
  12435. VAlign := FStateImages.Height;
  12436. VAlign := MulDiv((Integer(NodeHeight[Node]) - VAlign), Node.Align, 100) + VAlign div 2;
  12437. end
  12438. else
  12439. if toShowButtons in FOptions.FPaintOptions then
  12440. VAlign := MulDiv((Integer(NodeHeight[Node]) - FPlusBM.Height), Node.Align, 100) + FPlusBM.Height div 2
  12441. else
  12442. VAlign := MulDiv(Integer(Node.NodeHeight), Node.Align, 100);
  12443. end;
  12444. VButtonAlign := VAlign - FPlusBM.Height div 2 - (FPlusBM.Height and 1);
  12445. end;
  12446. //----------------------------------------------------------------------------------------------------------------------
  12447. function TBaseVirtualTree.ChangeCheckState(Node: PVirtualNode; Value: TCheckState): Boolean;
  12448. // Sets the check state of the node according to the given value and the node's check type.
  12449. // If the check state must be propagated to the parent nodes and one of them refuses to change then
  12450. // nothing happens and False is returned, otherwise True.
  12451. var
  12452. Run: PVirtualNode;
  12453. UncheckedCount,
  12454. MixedCheckCount,
  12455. CheckedCount: Cardinal;
  12456. begin
  12457. Result := not (vsChecking in Node.States);
  12458. with Node^ do
  12459. if Result then
  12460. begin
  12461. Include(States, vsChecking);
  12462. try
  12463. if not (vsInitialized in States) then
  12464. InitNode(Node)
  12465. else if CheckState = Value then
  12466. begin
  12467. // Value didn't change and node was initialized, so nothing to do
  12468. Result := False;
  12469. Exit;
  12470. end;//if
  12471. // Indicate that we are going to propagate check states up and down the hierarchy.
  12472. if FCheckPropagationCount = 0 then // WL, 05.02.2004: Do not enter tsCheckPropagation more than once
  12473. DoStateChange([tsCheckPropagation]);
  12474. Inc(FCheckPropagationCount); // WL, 05.02.2004
  12475. // Do actions which are associated with the given check state.
  12476. case CheckType of
  12477. // Check state change with additional consequences for check states of the children.
  12478. ctTriStateCheckBox:
  12479. begin
  12480. // Propagate state down to the children.
  12481. if toAutoTristateTracking in FOptions.FAutoOptions then
  12482. case Value of
  12483. csUncheckedNormal:
  12484. if Node.ChildCount > 0 then
  12485. begin
  12486. Run := FirstChild;
  12487. CheckedCount := 0;
  12488. MixedCheckCount := 0;
  12489. UncheckedCount := 0;
  12490. while Assigned(Run) do
  12491. begin
  12492. if Run.CheckType in [ctCheckBox, ctTriStateCheckBox] then
  12493. begin
  12494. SetCheckState(Run, csUncheckedNormal);
  12495. // Check if the new child state was set successfully, otherwise we have to adjust the
  12496. // node's new check state accordingly.
  12497. case Run.CheckState of
  12498. csCheckedNormal:
  12499. Inc(CheckedCount);
  12500. csMixedNormal:
  12501. Inc(MixedCheckCount);
  12502. csUncheckedNormal:
  12503. Inc(UncheckedCount);
  12504. end;
  12505. end;
  12506. Run := Run.NextSibling;
  12507. end;
  12508. // If there is still a mixed state child node checkbox then this node must be mixed checked too.
  12509. if MixedCheckCount > 0 then
  12510. Value := csMixedNormal
  12511. else
  12512. // If nodes are normally checked child nodes then the unchecked count determines what
  12513. // to set for the node itself.
  12514. if CheckedCount > 0 then
  12515. if UncheckedCount > 0 then
  12516. Value := csMixedNormal
  12517. else
  12518. Value := csCheckedNormal;
  12519. end;
  12520. csCheckedNormal:
  12521. if Node.ChildCount > 0 then
  12522. begin
  12523. Run := FirstChild;
  12524. CheckedCount := 0;
  12525. MixedCheckCount := 0;
  12526. UncheckedCount := 0;
  12527. while Assigned(Run) do
  12528. begin
  12529. if Run.CheckType in [ctCheckBox, ctTriStateCheckBox] then
  12530. begin
  12531. SetCheckState(Run, csCheckedNormal);
  12532. // Check if the new child state was set successfully, otherwise we have to adjust the
  12533. // node's new check state accordingly.
  12534. case Run.CheckState of
  12535. csCheckedNormal:
  12536. Inc(CheckedCount);
  12537. csMixedNormal:
  12538. Inc(MixedCheckCount);
  12539. csUncheckedNormal:
  12540. Inc(UncheckedCount);
  12541. end;
  12542. end;
  12543. Run := Run.NextSibling;
  12544. end;
  12545. // If there is still a mixed state child node checkbox then this node must be mixed checked too.
  12546. if MixedCheckCount > 0 then
  12547. Value := csMixedNormal
  12548. else
  12549. // If nodes are normally checked child nodes then the unchecked count determines what
  12550. // to set for the node itself.
  12551. if CheckedCount > 0 then
  12552. if UncheckedCount > 0 then
  12553. Value := csMixedNormal
  12554. else
  12555. Value := csCheckedNormal;
  12556. end;
  12557. end;
  12558. end;
  12559. // radio button check state change
  12560. ctRadioButton:
  12561. if Value = csCheckedNormal then
  12562. begin
  12563. Value := csCheckedNormal;
  12564. // Make sure only this node is checked.
  12565. Run := Parent.FirstChild;
  12566. while Assigned(Run) do
  12567. begin
  12568. if Run.CheckType = ctRadioButton then
  12569. Run.CheckState := csUncheckedNormal;
  12570. Run := Run.NextSibling;
  12571. end;
  12572. Invalidate;
  12573. end;
  12574. end;
  12575. if Result then
  12576. CheckState := Value // Set new check state
  12577. else
  12578. CheckState := UnpressedState[CheckState]; // Reset dynamic check state.
  12579. // Propagate state up to the parent.
  12580. if not (vsInitialized in Parent.States) then
  12581. InitNode(Parent);
  12582. if (toAutoTristateTracking in FOptions.FAutoOptions) and ([vsChecking, vsDisabled] * Parent.States = []) and
  12583. (CheckType in [ctCheckBox, ctTriStateCheckBox]) and (Parent <> FRoot) and
  12584. (Parent.CheckType = ctTriStateCheckBox) then
  12585. Result := CheckParentCheckState(Node, Value)
  12586. else
  12587. Result := True;
  12588. InvalidateNode(Node);
  12589. Dec(FCheckPropagationCount); // WL, 05.02.2004
  12590. if FCheckPropagationCount = 0 then // WL, 05.02.2004: Allow state change event after all check operations finished
  12591. DoStateChange([], [tsCheckPropagation]);
  12592. finally
  12593. Exclude(States, vsChecking);
  12594. end;
  12595. end;
  12596. end;
  12597. //----------------------------------------------------------------------------------------------------------------------
  12598. function TBaseVirtualTree.CollectSelectedNodesLTR(MainColumn, NodeLeft, NodeRight: Integer; Alignment: TAlignment;
  12599. OldRect, NewRect: TRect): Boolean;
  12600. // Helper routine used when a draw selection takes place. This version handles left-to-right directionality.
  12601. // In the process of adding or removing nodes the current selection is modified which requires to pack it after
  12602. // the function returns. Another side effect of this method is that a temporary list of nodes will be created
  12603. // (see also InternalCacheNode) which must be inserted into the current selection by the caller.
  12604. var
  12605. Run,
  12606. NextNode: PVirtualNode;
  12607. TextRight,
  12608. TextLeft,
  12609. CheckOffset,
  12610. CurrentTop,
  12611. CurrentRight,
  12612. NextTop,
  12613. NextColumn,
  12614. NodeWidth,
  12615. Dummy: Integer;
  12616. MinY, MaxY: Integer;
  12617. StateImageOffset: Integer;
  12618. IsInOldRect,
  12619. IsInNewRect: Boolean;
  12620. // quick check variables for various parameters
  12621. WithCheck,
  12622. WithImages,
  12623. WithStateImages,
  12624. DoSwitch,
  12625. AutoSpan: Boolean;
  12626. SimpleSelection: Boolean;
  12627. begin
  12628. // A priori nothing changes.
  12629. Result := False;
  12630. // Determine minimum and maximum vertical coordinates to limit iteration to.
  12631. MinY := Min(OldRect.Top, NewRect.Top);
  12632. MaxY := Max(OldRect.Bottom, NewRect.Bottom);
  12633. // Initialize short hand variables to speed up tests below.
  12634. DoSwitch := ssCtrl in FDrawSelShiftState;
  12635. WithCheck := (toCheckSupport in FOptions.FMiscOptions) and Assigned(FCheckImages);
  12636. // Don't check the events here as descendant trees might have overriden the DoGetImageIndex method.
  12637. WithImages := Assigned(FImages);
  12638. WithStateImages := Assigned(FStateImages);
  12639. if WithStateImages then
  12640. StateImageOffset := FStateImages.Width + 2
  12641. else
  12642. StateImageOffset := 0;
  12643. if WithCheck then
  12644. CheckOffset := FCheckImages.Width + 2
  12645. else
  12646. CheckOffset := 0;
  12647. AutoSpan := FHeader.UseColumns and (toAutoSpanColumns in FOptions.FAutoOptions);
  12648. SimpleSelection := toSimpleDrawSelection in FOptions.FSelectionOptions;
  12649. // This is the node to start with.
  12650. Run := GetNodeAt(0, MinY, False, CurrentTop);
  12651. if Assigned(Run) then
  12652. begin
  12653. // The initial minimal left border is determined by the identation level of the node and is dynamically adjusted.
  12654. if toShowRoot in FOptions.FPaintOptions then
  12655. Inc(NodeLeft, Integer((GetNodeLevel(Run) + 1) * FIndent) + FMargin)
  12656. else
  12657. Inc(NodeLeft, Integer(GetNodeLevel(Run) * FIndent) + FMargin);
  12658. // ----- main loop
  12659. // Change selection depending on the node's rectangle being in the selection rectangle or not, but
  12660. // touch only those nodes which overlap either the old selection rectangle or the new one but not both.
  12661. repeat
  12662. // Collect offsets for check, normal and state images.
  12663. TextLeft := NodeLeft;
  12664. if WithCheck and (Run.CheckType <> ctNone) then
  12665. Inc(TextLeft, CheckOffset);
  12666. if WithImages and HasImage(Run, ikNormal, MainColumn) then
  12667. Inc(TextLeft, GetNodeImageSize(Run).cx + 2);
  12668. if WithStateImages and HasImage(Run, ikState, MainColumn) then
  12669. Inc(TextLeft, StateImageOffset);
  12670. NextTop := CurrentTop + Integer(NodeHeight[Run]);
  12671. // Simple selection allows to draw the selection rectangle anywhere. No intersection with node captions is
  12672. // required. Only top and bottom bounds of the rectangle matter.
  12673. if SimpleSelection or (toFullRowSelect in FOptions.FSelectionOptions) then
  12674. begin
  12675. IsInOldRect := (NextTop > OldRect.Top) and (CurrentTop < OldRect.Bottom) and
  12676. ((FHeader.Columns.Count = 0) or (FHeader.Columns.TotalWidth > OldRect.Left)) and (NodeLeft < OldRect.Right);
  12677. IsInNewRect := (NextTop > NewRect.Top) and (CurrentTop < NewRect.Bottom) and
  12678. ((FHeader.Columns.Count = 0) or (FHeader.Columns.TotalWidth > NewRect.Left)) and (NodeLeft < NewRect.Right);
  12679. end
  12680. else
  12681. begin
  12682. // The right column border might be extended if column spanning is enabled.
  12683. if AutoSpan then
  12684. begin
  12685. with FHeader.FColumns do
  12686. begin
  12687. NextColumn := MainColumn;
  12688. repeat
  12689. Dummy := GetNextVisibleColumn(NextColumn);
  12690. if (Dummy = InvalidColumn) or not ColumnIsEmpty(Run, Dummy) or
  12691. (Items[Dummy].BidiMode <> bdLeftToRight) then
  12692. Break;
  12693. NextColumn := Dummy;
  12694. until False;
  12695. if NextColumn = MainColumn then
  12696. CurrentRight := NodeRight
  12697. else
  12698. GetColumnBounds(NextColumn, Dummy, CurrentRight);
  12699. end;
  12700. end
  12701. else
  12702. CurrentRight := NodeRight;
  12703. // Check if we need the node's width. This is the case when the node is not left aligned or the
  12704. // left border of the selection rectangle is to the right of the left node border.
  12705. if (TextLeft < OldRect.Left) or (TextLeft < NewRect.Left) or (Alignment <> taLeftJustify) then
  12706. begin
  12707. NodeWidth := DoGetNodeWidth(Run, MainColumn);
  12708. if NodeWidth >= (CurrentRight - TextLeft) then
  12709. TextRight := CurrentRight
  12710. else
  12711. case Alignment of
  12712. taLeftJustify:
  12713. TextRight := TextLeft + NodeWidth;
  12714. taCenter:
  12715. begin
  12716. TextLeft := (TextLeft + CurrentRight - NodeWidth) div 2;
  12717. TextRight := TextLeft + NodeWidth;
  12718. end;
  12719. else
  12720. // taRightJustify
  12721. TextRight := CurrentRight;
  12722. TextLeft := TextRight - NodeWidth;
  12723. end;
  12724. end
  12725. else
  12726. TextRight := CurrentRight;
  12727. // Now determine whether we need to change the state.
  12728. IsInOldRect := (OldRect.Left <= TextRight) and (OldRect.Right >= TextLeft) and
  12729. (NextTop > OldRect.Top) and (CurrentTop < OldRect.Bottom);
  12730. IsInNewRect := (NewRect.Left <= TextRight) and (NewRect.Right >= TextLeft) and
  12731. (NextTop > NewRect.Top) and (CurrentTop < NewRect.Bottom);
  12732. end;
  12733. if IsInOldRect xor IsInNewRect then
  12734. begin
  12735. Result := True;
  12736. if DoSwitch then
  12737. begin
  12738. if vsSelected in Run.States then
  12739. InternalRemoveFromSelection(Run)
  12740. else
  12741. InternalCacheNode(Run);
  12742. end
  12743. else
  12744. begin
  12745. if IsInNewRect then
  12746. InternalCacheNode(Run)
  12747. else
  12748. InternalRemoveFromSelection(Run);
  12749. end;
  12750. end;
  12751. CurrentTop := NextTop;
  12752. // Get next visible node and update left node position.
  12753. NextNode := GetNextVisibleNoInit(Run, True);
  12754. if NextNode = nil then
  12755. Break;
  12756. Inc(NodeLeft, CountLevelDifference(Run, NextNode) * Integer(FIndent));
  12757. Run := NextNode;
  12758. until CurrentTop > MaxY;
  12759. end;
  12760. end;
  12761. //----------------------------------------------------------------------------------------------------------------------
  12762. function TBaseVirtualTree.CollectSelectedNodesRTL(MainColumn, NodeLeft, NodeRight: Integer; Alignment: TAlignment;
  12763. OldRect, NewRect: TRect): Boolean;
  12764. // Helper routine used when a draw selection takes place. This version handles right-to-left directionality.
  12765. // See also comments in CollectSelectedNodesLTR.
  12766. var
  12767. Run,
  12768. NextNode: PVirtualNode;
  12769. TextRight,
  12770. TextLeft,
  12771. CheckOffset,
  12772. CurrentTop,
  12773. CurrentLeft,
  12774. NextTop,
  12775. NextColumn,
  12776. NodeWidth,
  12777. Dummy: Integer;
  12778. MinY, MaxY: Integer;
  12779. StateImageOffset: Integer;
  12780. IsInOldRect,
  12781. IsInNewRect: Boolean;
  12782. // quick check variables for various parameters
  12783. WithCheck,
  12784. WithImages,
  12785. WithStateImages,
  12786. DoSwitch,
  12787. AutoSpan: Boolean;
  12788. SimpleSelection: Boolean;
  12789. begin
  12790. // A priori nothing changes.
  12791. Result := False;
  12792. // Switch the alignment to the opposite value in RTL context.
  12793. ChangeBiDiModeAlignment(Alignment);
  12794. // Determine minimum and maximum vertical coordinates to limit iteration to.
  12795. MinY := Min(OldRect.Top, NewRect.Top);
  12796. MaxY := Max(OldRect.Bottom, NewRect.Bottom);
  12797. // Initialize short hand variables to speed up tests below.
  12798. DoSwitch := ssCtrl in FDrawSelShiftState;
  12799. WithCheck := (toCheckSupport in FOptions.FMiscOptions) and Assigned(FCheckImages);
  12800. // Don't check the events here as descendant trees might have overriden the DoGetImageIndex method.
  12801. WithImages := Assigned(FImages);
  12802. WithStateImages := Assigned(FStateImages);
  12803. if WithStateImages then
  12804. StateImageOffset := FStateImages.Width + 2
  12805. else
  12806. StateImageOffset := 0;
  12807. if WithCheck then
  12808. CheckOffset := FCheckImages.Width + 2
  12809. else
  12810. CheckOffset := 0;
  12811. AutoSpan := FHeader.UseColumns and (toAutoSpanColumns in FOptions.FAutoOptions);
  12812. SimpleSelection := toSimpleDrawSelection in FOptions.FSelectionOptions;
  12813. // This is the node to start with.
  12814. Run := GetNodeAt(0, MinY, False, CurrentTop);
  12815. if Assigned(Run) then
  12816. begin
  12817. // The initial minimal left border is determined by the identation level of the node and is dynamically adjusted.
  12818. if toShowRoot in FOptions.FPaintOptions then
  12819. Dec(NodeRight, Integer((GetNodeLevel(Run) + 1) * FIndent) + FMargin)
  12820. else
  12821. Dec(NodeRight, Integer(GetNodeLevel(Run) * FIndent) + FMargin);
  12822. // ----- main loop
  12823. // Change selection depending on the node's rectangle being in the selection rectangle or not, but
  12824. // touch only those nodes which overlap either the old selection rectangle or the new one but not both.
  12825. repeat
  12826. // Collect offsets for check, normal and state images.
  12827. TextRight := NodeRight;
  12828. if WithCheck and (Run.CheckType <> ctNone) then
  12829. Dec(TextRight, CheckOffset);
  12830. if WithImages and HasImage(Run, ikNormal, MainColumn) then
  12831. Dec(TextRight, GetNodeImageSize(Run).cx + 2);
  12832. if WithStateImages and HasImage(Run, ikState, MainColumn) then
  12833. Dec(TextRight, StateImageOffset);
  12834. NextTop := CurrentTop + Integer(NodeHeight[Run]);
  12835. // Simple selection allows to draw the selection rectangle anywhere. No intersection with node captions is
  12836. // required. Only top and bottom bounds of the rectangle matter.
  12837. if SimpleSelection then
  12838. begin
  12839. IsInOldRect := (NextTop > OldRect.Top) and (CurrentTop < OldRect.Bottom);
  12840. IsInNewRect := (NextTop > NewRect.Top) and (CurrentTop < NewRect.Bottom);
  12841. end
  12842. else
  12843. begin // The left column border might be extended if column spanning is enabled.
  12844. if AutoSpan then
  12845. begin
  12846. NextColumn := MainColumn;
  12847. repeat
  12848. Dummy := FHeader.FColumns.GetPreviousVisibleColumn(NextColumn);
  12849. if (Dummy = InvalidColumn) or not ColumnIsEmpty(Run, Dummy) or
  12850. (FHeader.FColumns[Dummy].BiDiMode = bdLeftToRight) then
  12851. Break;
  12852. NextColumn := Dummy;
  12853. until False;
  12854. if NextColumn = MainColumn then
  12855. CurrentLeft := NodeLeft
  12856. else
  12857. FHeader.FColumns.GetColumnBounds(NextColumn, CurrentLeft, Dummy);
  12858. end
  12859. else
  12860. CurrentLeft := NodeLeft;
  12861. // Check if we need the node's width. This is the case when the node is not left aligned (in RTL context this // means actually right aligned) or the right border of the selection rectangle is to the left
  12862. // of the right node border.
  12863. if (TextRight > OldRect.Right) or (TextRight > NewRect.Right) or (Alignment <> taRightJustify) then
  12864. begin
  12865. NodeWidth := DoGetNodeWidth(Run, MainColumn);
  12866. if NodeWidth >= (TextRight - CurrentLeft) then
  12867. TextLeft := CurrentLeft
  12868. else
  12869. case Alignment of
  12870. taLeftJustify:
  12871. begin
  12872. TextLeft := CurrentLeft;
  12873. TextRight := TextLeft + NodeWidth;
  12874. end;
  12875. taCenter:
  12876. begin
  12877. TextLeft := (TextRight + CurrentLeft - NodeWidth) div 2;
  12878. TextRight := TextLeft + NodeWidth;
  12879. end;
  12880. else
  12881. // taRightJustify
  12882. TextLeft := TextRight - NodeWidth;
  12883. end;
  12884. end
  12885. else
  12886. TextLeft := CurrentLeft;
  12887. // Now determine whether we need to change the state.
  12888. IsInOldRect := (OldRect.Right >= TextLeft) and (OldRect.Left <= TextRight) and
  12889. (NextTop > OldRect.Top) and (CurrentTop < OldRect.Bottom);
  12890. IsInNewRect := (NewRect.Right >= TextLeft) and (NewRect.Left <= TextRight) and
  12891. (NextTop > NewRect.Top) and (CurrentTop < NewRect.Bottom);
  12892. end;
  12893. if IsInOldRect xor IsInNewRect then
  12894. begin
  12895. Result := True;
  12896. if DoSwitch then
  12897. begin
  12898. if vsSelected in Run.States then
  12899. InternalRemoveFromSelection(Run)
  12900. else
  12901. InternalCacheNode(Run);
  12902. end
  12903. else
  12904. begin
  12905. if IsInNewRect then
  12906. InternalCacheNode(Run)
  12907. else
  12908. InternalRemoveFromSelection(Run);
  12909. end;
  12910. end;
  12911. CurrentTop := NextTop;
  12912. // Get next visible node and update left node position.
  12913. NextNode := GetNextVisibleNoInit(Run, True);
  12914. if NextNode = nil then
  12915. Break;
  12916. Dec(NodeRight, CountLevelDifference(Run, NextNode) * Integer(FIndent));
  12917. Run := NextNode;
  12918. until CurrentTop > MaxY;
  12919. end;
  12920. end;
  12921. //----------------------------------------------------------------------------------------------------------------------
  12922. procedure TBaseVirtualTree.ClearNodeBackground(const PaintInfo: TVTPaintInfo; UseBackground, Floating: Boolean;
  12923. R: TRect);
  12924. // Erases a node's background depending on what the application decides to do.
  12925. // UseBackground determines whether or not to use the background picture, while Floating indicates
  12926. // that R is given in coordinates of the small node bitmap or the superordinated target bitmap used in PaintTree.
  12927. var
  12928. BackColor: TColor;
  12929. EraseAction: TItemEraseAction;
  12930. Offset: TPoint;
  12931. begin
  12932. BackColor := FColors.BackGroundColor;
  12933. with PaintInfo do
  12934. begin
  12935. EraseAction := eaDefault;
  12936. if Floating then
  12937. begin
  12938. Offset := Point(-FEffectiveOffsetX, R.Top);
  12939. OffsetRect(R, 0, -Offset.Y);
  12940. end
  12941. else
  12942. Offset := Point(0, 0);
  12943. DoBeforeItemErase(Canvas, Node, R, BackColor, EraseAction);
  12944. with Canvas do
  12945. begin
  12946. case EraseAction of
  12947. eaNone:
  12948. ;
  12949. eaColor:
  12950. begin
  12951. // User has given a new background color.
  12952. Brush.Color := BackColor;
  12953. FillRect(R);
  12954. end;
  12955. else // eaDefault
  12956. if UseBackground then
  12957. begin
  12958. if toStaticBackground in TreeOptions.PaintOptions then
  12959. StaticBackground(FBackground.Bitmap, Canvas, Offset, R)
  12960. else
  12961. TileBackground(FBackground.Bitmap, Canvas, Offset, R);
  12962. end
  12963. else
  12964. begin
  12965. if (poDrawSelection in PaintOptions) and (toFullRowSelect in FOptions.FSelectionOptions) and
  12966. (vsSelected in Node.States) and not (toUseBlendedSelection in FOptions.PaintOptions) and not
  12967. (tsUseExplorerTheme in FStates) then
  12968. begin
  12969. if toShowHorzGridLines in FOptions.PaintOptions then
  12970. begin
  12971. Brush.Color := BackColor;
  12972. FillRect(Rect(R.Left, R.Bottom - 1, R.Right, R.Bottom));
  12973. Dec(R.Bottom);
  12974. end;
  12975. if Focused or (toPopupMode in FOptions.FPaintOptions) then
  12976. begin
  12977. Brush.Color := FColors.FocusedSelectionColor;
  12978. Pen.Color := FColors.FocusedSelectionBorderColor;
  12979. end
  12980. else
  12981. begin
  12982. Brush.Color := FColors.UnfocusedSelectionColor;
  12983. Pen.Color := FColors.UnfocusedSelectionBorderColor;
  12984. end;
  12985. with TWithSafeRect(R) do
  12986. RoundRect(Left, Top, Right, Bottom, FSelectionCurveRadius, FSelectionCurveRadius);
  12987. end
  12988. else
  12989. begin
  12990. Brush.Color := BackColor;
  12991. FillRect(R);
  12992. end;
  12993. end;
  12994. end;
  12995. DoAfterItemErase(Canvas, Node, R);
  12996. end;
  12997. end;
  12998. end;
  12999. //----------------------------------------------------------------------------------------------------------------------
  13000. function TBaseVirtualTree.CompareNodePositions(Node1, Node2: PVirtualNode; ConsiderChildrenAbove: Boolean = False): Integer;
  13001. // Tries hard and smart to quickly determine whether Node1's structural position is before Node2's position.
  13002. // If ConsiderChildrenAbove is True, the nodes will be compared with their visual order in mind.
  13003. // Returns 0 if Node1 = Node2, < 0 if Node1 is located before Node2 else > 0.
  13004. var
  13005. Run1,
  13006. Run2: PVirtualNode;
  13007. Level1,
  13008. Level2: Cardinal;
  13009. begin
  13010. Assert(Assigned(Node1) and Assigned(Node2), 'Nodes must never be nil.');
  13011. if Node1 = Node2 then
  13012. Result := 0
  13013. else
  13014. begin
  13015. if HasAsParent(Node1, Node2) then
  13016. Result := IfThen(ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions), -1, 1)
  13017. else
  13018. if HasAsParent(Node2, Node1) then
  13019. Result := IfThen(ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions), 1, -1)
  13020. else
  13021. begin
  13022. // the given nodes are neither equal nor are they parents of each other, so go up to FRoot
  13023. // for each node and compare the child indices of the top level parents
  13024. // Note: neither Node1 nor Node2 can be FRoot at this point as this (a bit strange) circumstance would
  13025. // be caught by the previous code.
  13026. // start lookup at the same level
  13027. Level1 := GetNodeLevel(Node1);
  13028. Level2 := GetNodeLevel(Node2);
  13029. Run1 := Node1;
  13030. while Level1 > Level2 do
  13031. begin
  13032. Run1 := Run1.Parent;
  13033. Dec(Level1);
  13034. end;
  13035. Run2 := Node2;
  13036. while Level2 > Level1 do
  13037. begin
  13038. Run2 := Run2.Parent;
  13039. Dec(Level2);
  13040. end;
  13041. // now go up until we find a common parent node (loop will safely stop at FRoot if the nodes
  13042. // don't share a common parent)
  13043. while Run1.Parent <> Run2.Parent do
  13044. begin
  13045. Run1 := Run1.Parent;
  13046. Run2 := Run2.Parent;
  13047. end;
  13048. Result := Integer(Run1.Index) - Integer(Run2.Index);
  13049. end;
  13050. end;
  13051. end;
  13052. //----------------------------------------------------------------------------------------------------------------------
  13053. procedure TBaseVirtualTree.DrawLineImage(const PaintInfo: TVTPaintInfo; X, Y, H, VAlign: Integer; Style: TVTLineType;
  13054. Reverse: Boolean);
  13055. // Draws (depending on Style) one of the 5 line types of the tree.
  13056. // If Reverse is True then a right-to-left column is being drawn, hence horizontal lines must be mirrored.
  13057. // X and Y describe the left upper corner of the line image rectangle, while H denotes its height (and width).
  13058. var
  13059. HalfWidth,
  13060. TargetX: Integer;
  13061. begin
  13062. HalfWidth := Round(FIndent / 2);
  13063. if Reverse then
  13064. TargetX := 0
  13065. else
  13066. TargetX := FIndent;
  13067. with PaintInfo.Canvas do
  13068. begin
  13069. case Style of
  13070. ltBottomRight:
  13071. begin
  13072. DrawDottedVLine(PaintInfo, Y + VAlign, Y + H, X + HalfWidth);
  13073. DrawDottedHLine(PaintInfo, X + HalfWidth, X + TargetX, Y + VAlign);
  13074. end;
  13075. ltTopDown:
  13076. DrawDottedVLine(PaintInfo, Y, Y + H, X + HalfWidth);
  13077. ltTopDownRight:
  13078. begin
  13079. DrawDottedVLine(PaintInfo, Y, Y + H, X + HalfWidth);
  13080. DrawDottedHLine(PaintInfo, X + HalfWidth, X + TargetX, Y + VAlign);
  13081. end;
  13082. ltRight:
  13083. DrawDottedHLine(PaintInfo, X + HalfWidth, X + TargetX, Y + VAlign);
  13084. ltTopRight:
  13085. begin
  13086. DrawDottedVLine(PaintInfo, Y, Y + VAlign, X + HalfWidth);
  13087. DrawDottedHLine(PaintInfo, X + HalfWidth, X + TargetX, Y + VAlign);
  13088. end;
  13089. ltLeft: // left can also mean right for RTL context
  13090. if Reverse then
  13091. DrawDottedVLine(PaintInfo, Y, Y + H, X + Integer(FIndent))
  13092. else
  13093. DrawDottedVLine(PaintInfo, Y, Y + H, X);
  13094. ltLeftBottom:
  13095. if Reverse then
  13096. begin
  13097. DrawDottedVLine(PaintInfo, Y, Y + H, X + Integer(FIndent));
  13098. DrawDottedHLine(PaintInfo, X, X + Integer(FIndent), Y + H);
  13099. end
  13100. else
  13101. begin
  13102. DrawDottedVLine(PaintInfo, Y, Y + H, X);
  13103. DrawDottedHLine(PaintInfo, X, X + Integer(FIndent), Y + H);
  13104. end;
  13105. end;
  13106. end;
  13107. end;
  13108. //----------------------------------------------------------------------------------------------------------------------
  13109. function TBaseVirtualTree.FindInPositionCache(Node: PVirtualNode; var CurrentPos: Cardinal): PVirtualNode;
  13110. // Looks through the position cache and returns the node whose top position is the largest one which is smaller or equal
  13111. // to the position of the given node.
  13112. var
  13113. L, H, I: Integer;
  13114. begin
  13115. L := 0;
  13116. H := High(FPositionCache);
  13117. while L <= H do
  13118. begin
  13119. I := (L + H) shr 1;
  13120. if CompareNodePositions(FPositionCache[I].Node, Node) <= 0 then
  13121. L := I + 1
  13122. else
  13123. H := I - 1;
  13124. end;
  13125. if L = 0 then // High(FPositionCache) = -1
  13126. begin
  13127. Result := nil;
  13128. CurrentPos := 0;
  13129. end
  13130. else
  13131. begin
  13132. Result := FPositionCache[L - 1].Node;
  13133. CurrentPos := FPositionCache[L - 1].AbsoluteTop;
  13134. end;
  13135. end;
  13136. //----------------------------------------------------------------------------------------------------------------------
  13137. function TBaseVirtualTree.FindInPositionCache(Position: Cardinal; var CurrentPos: Cardinal): PVirtualNode;
  13138. // Looks through the position cache and returns the node whose top position is the largest one which is smaller or equal
  13139. // to the given vertical position.
  13140. // The returned node does not necessarily occupy the given position but is the nearest one to start
  13141. // iterating from to approach the real node for a given position. CurrentPos receives the actual position of the found
  13142. // node which is needed for further iteration.
  13143. var
  13144. L, H, I: Integer;
  13145. begin
  13146. L := 0;
  13147. H := High(FPositionCache);
  13148. while L <= H do
  13149. begin
  13150. I := (L + H) shr 1;
  13151. if FPositionCache[I].AbsoluteTop <= Position then
  13152. L := I + 1
  13153. else
  13154. H := I - 1;
  13155. end;
  13156. if L = 0 then // High(FPositionCache) = -1
  13157. begin
  13158. Result := nil;
  13159. CurrentPos := 0;
  13160. end
  13161. else
  13162. begin
  13163. Result := FPositionCache[L - 1].Node;
  13164. CurrentPos := FPositionCache[L - 1].AbsoluteTop;
  13165. end;
  13166. end;
  13167. //----------------------------------------------------------------------------------------------------------------------
  13168. procedure TBaseVirtualTree.FixupTotalCount(Node: PVirtualNode);
  13169. // Called after loading a subtree from stream. The child count in each node is already set but not
  13170. // their total count.
  13171. var
  13172. Child: PVirtualNode;
  13173. begin
  13174. // Initial total count is set to one on node creation.
  13175. Child := Node.FirstChild;
  13176. while Assigned(Child) do
  13177. begin
  13178. FixupTotalCount(Child);
  13179. Inc(Node.TotalCount, Child.TotalCount);
  13180. Child := Child.NextSibling;
  13181. end;
  13182. end;
  13183. //----------------------------------------------------------------------------------------------------------------------
  13184. procedure TBaseVirtualTree.FixupTotalHeight(Node: PVirtualNode);
  13185. // Called after loading a subtree from stream. The individual height of each node is set already,
  13186. // but their total height needs an adjustment depending on their visibility state.
  13187. var
  13188. Child: PVirtualNode;
  13189. begin
  13190. // Initial total height is set to the node height on load.
  13191. Child := Node.FirstChild;
  13192. if vsExpanded in Node.States then
  13193. begin
  13194. while Assigned(Child) do
  13195. begin
  13196. FixupTotalHeight(Child);
  13197. if vsVisible in Child.States then
  13198. Inc(Node.TotalHeight, Child.TotalHeight);
  13199. Child := Child.NextSibling;
  13200. end;
  13201. end
  13202. else
  13203. begin
  13204. // The node is collapsed, so just update the total height of its child nodes.
  13205. while Assigned(Child) do
  13206. begin
  13207. FixupTotalHeight(Child);
  13208. Child := Child.NextSibling;
  13209. end;
  13210. end;
  13211. end;
  13212. //----------------------------------------------------------------------------------------------------------------------
  13213. function TBaseVirtualTree.GetBottomNode: PVirtualNode;
  13214. begin
  13215. Result := GetNodeAt(0, ClientHeight - 1);
  13216. end;
  13217. //----------------------------------------------------------------------------------------------------------------------
  13218. function TBaseVirtualTree.GetCheckedCount: Integer;
  13219. var
  13220. Node: PVirtualNode;
  13221. begin
  13222. Result := 0;
  13223. Node := GetFirstChecked;
  13224. while Assigned(Node) do
  13225. begin
  13226. Inc(Result);
  13227. Node := GetNextChecked(Node);
  13228. end;
  13229. end;
  13230. //----------------------------------------------------------------------------------------------------------------------
  13231. function TBaseVirtualTree.GetCheckState(Node: PVirtualNode): TCheckState;
  13232. begin
  13233. Result := Node.CheckState;
  13234. end;
  13235. //----------------------------------------------------------------------------------------------------------------------
  13236. function TBaseVirtualTree.GetCheckType(Node: PVirtualNode): TCheckType;
  13237. begin
  13238. Result := Node.CheckType;
  13239. end;
  13240. //----------------------------------------------------------------------------------------------------------------------
  13241. function TBaseVirtualTree.GetChildCount(Node: PVirtualNode): Cardinal;
  13242. begin
  13243. if (Node = nil) or (Node = FRoot) then
  13244. Result := FRoot.ChildCount
  13245. else
  13246. Result := Node.ChildCount;
  13247. end;
  13248. //----------------------------------------------------------------------------------------------------------------------
  13249. function TBaseVirtualTree.GetChildrenInitialized(Node: PVirtualNode): Boolean;
  13250. begin
  13251. Result := not (vsHasChildren in Node.States) or (Node.ChildCount > 0);
  13252. end;
  13253. //----------------------------------------------------------------------------------------------------------------------
  13254. function TBaseVirtualTree.GetCutCopyCount: Integer;
  13255. var
  13256. Node: PVirtualNode;
  13257. begin
  13258. Result := 0;
  13259. Node := GetFirstCutCopy;
  13260. while Assigned(Node) do
  13261. begin
  13262. Inc(Result);
  13263. Node := GetNextCutCopy(Node);
  13264. end;
  13265. end;
  13266. //----------------------------------------------------------------------------------------------------------------------
  13267. function TBaseVirtualTree.GetDisabled(Node: PVirtualNode): Boolean;
  13268. begin
  13269. Result := Assigned(Node) and (vsDisabled in Node.States);
  13270. end;
  13271. //----------------------------------------------------------------------------------------------------------------------
  13272. function TBaseVirtualTree.GetDragManager: IVTDragManager;
  13273. // Returns the internal drag manager interface. If this does not yet exist then it is created here.
  13274. begin
  13275. if FDragManager = nil then
  13276. begin
  13277. FDragManager := DoCreateDragManager;
  13278. if FDragManager = nil then
  13279. FDragManager := TVTDragManager.Create(Self);
  13280. end;
  13281. Result := FDragManager;
  13282. end;
  13283. //----------------------------------------------------------------------------------------------------------------------
  13284. function TBaseVirtualTree.GetExpanded(Node: PVirtualNode): Boolean;
  13285. begin
  13286. if Assigned(Node) then
  13287. Result := vsExpanded in Node.States
  13288. else
  13289. Result := False;
  13290. end;
  13291. //----------------------------------------------------------------------------------------------------------------------
  13292. function TBaseVirtualTree.GetFiltered(Node: PVirtualNode): Boolean;
  13293. begin
  13294. Result := vsFiltered in Node.States;
  13295. end;
  13296. //----------------------------------------------------------------------------------------------------------------------
  13297. function TBaseVirtualTree.GetFullyVisible(Node: PVirtualNode): Boolean;
  13298. // Determines whether the given node has the visibility flag set as well as all its parents are expanded.
  13299. begin
  13300. Assert(Assigned(Node), 'Invalid parameter.');
  13301. Result := vsVisible in Node.States;
  13302. if Result and (Node <> FRoot) then
  13303. Result := VisiblePath[Node];
  13304. end;
  13305. //----------------------------------------------------------------------------------------------------------------------
  13306. function TBaseVirtualTree.GetHasChildren(Node: PVirtualNode): Boolean;
  13307. begin
  13308. if Assigned(Node) then
  13309. Result := vsHasChildren in Node.States
  13310. else
  13311. Result := vsHasChildren in FRoot.States;
  13312. end;
  13313. //----------------------------------------------------------------------------------------------------------------------
  13314. function TBaseVirtualTree.GetMultiline(Node: PVirtualNode): Boolean;
  13315. begin
  13316. Result := Assigned(Node) and (Node <> FRoot) and (vsMultiline in Node.States);
  13317. end;
  13318. //----------------------------------------------------------------------------------------------------------------------
  13319. function TBaseVirtualTree.GetNodeHeight(Node: PVirtualNode): Cardinal;
  13320. begin
  13321. if Assigned(Node) and (Node <> FRoot) then
  13322. begin
  13323. if (toVariableNodeHeight in FOptions.FMiscOptions) and not (vsDeleting in Node.States) then
  13324. begin
  13325. if not (vsInitialized in Node.States) then
  13326. InitNode(Node);
  13327. // Ensure the node's height is determined.
  13328. MeasureItemHeight(Self.Canvas, Node);
  13329. end;
  13330. Result := Node.NodeHeight;
  13331. end
  13332. else
  13333. Result := 0;
  13334. end;
  13335. //----------------------------------------------------------------------------------------------------------------------
  13336. function TBaseVirtualTree.GetNodeParent(Node: PVirtualNode): PVirtualNode;
  13337. begin
  13338. if Assigned(Node) and (Node.Parent <> FRoot) then
  13339. Result := Node.Parent
  13340. else
  13341. Result := nil;
  13342. end;
  13343. //----------------------------------------------------------------------------------------------------------------------
  13344. function TBaseVirtualTree.GetOffsetXY: TPoint;
  13345. begin
  13346. Result := Point(FOffsetX, FOffsetY);
  13347. end;
  13348. //----------------------------------------------------------------------------------------------------------------------
  13349. function TBaseVirtualTree.GetRangeX: Cardinal;
  13350. begin
  13351. Result := Max(0, FRangeX);
  13352. end;
  13353. function TBaseVirtualTree.GetRootNodeCount: Cardinal;
  13354. begin
  13355. Result := FRoot.ChildCount;
  13356. end;
  13357. //----------------------------------------------------------------------------------------------------------------------
  13358. function TBaseVirtualTree.GetSelected(Node: PVirtualNode): Boolean;
  13359. begin
  13360. Result := Assigned(Node) and (vsSelected in Node.States);
  13361. end;
  13362. //----------------------------------------------------------------------------------------------------------------------
  13363. function TBaseVirtualTree.GetTopNode: PVirtualNode;
  13364. var
  13365. Dummy: Integer;
  13366. begin
  13367. Result := GetNodeAt(0, 0, True, Dummy);
  13368. end;
  13369. //----------------------------------------------------------------------------------------------------------------------
  13370. function TBaseVirtualTree.GetTotalCount: Cardinal;
  13371. begin
  13372. Inc(FUpdateCount);
  13373. try
  13374. ValidateNode(FRoot, True);
  13375. finally
  13376. Dec(FUpdateCount);
  13377. end;
  13378. // The root node itself doesn't count as node.
  13379. Result := FRoot.TotalCount - 1;
  13380. end;
  13381. //----------------------------------------------------------------------------------------------------------------------
  13382. function TBaseVirtualTree.GetVerticalAlignment(Node: PVirtualNode): Byte;
  13383. begin
  13384. Result := Node.Align;
  13385. end;
  13386. //----------------------------------------------------------------------------------------------------------------------
  13387. function TBaseVirtualTree.GetVisible(Node: PVirtualNode): Boolean;
  13388. // Determines if the given node is marked as being visible.
  13389. begin
  13390. if Node = nil then
  13391. Node := FRoot;
  13392. if not (vsInitialized in Node.States) then
  13393. InitNode(Node);
  13394. Result := vsVisible in Node.States;
  13395. end;
  13396. //----------------------------------------------------------------------------------------------------------------------
  13397. function TBaseVirtualTree.GetVisiblePath(Node: PVirtualNode): Boolean;
  13398. // Determines if all parents of the given node are expanded and have the visibility flag set.
  13399. begin
  13400. Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameters.');
  13401. // FRoot is always expanded
  13402. repeat
  13403. Node := Node.Parent;
  13404. until (Node = FRoot) or not (vsExpanded in Node.States) or not (vsVisible in Node.States);
  13405. Result := Node = FRoot;
  13406. end;
  13407. //----------------------------------------------------------------------------------------------------------------------
  13408. procedure TBaseVirtualTree.HandleClickSelection(LastFocused, NewNode: PVirtualNode; Shift: TShiftState;
  13409. DragPending: Boolean);
  13410. // Handles multi-selection with mouse click.
  13411. begin
  13412. // Ctrl key down
  13413. if ssCtrl in Shift then
  13414. begin
  13415. if ssShift in Shift then
  13416. begin
  13417. SelectNodes(FRangeAnchor, NewNode, True);
  13418. end
  13419. else
  13420. begin
  13421. if not (toSiblingSelectConstraint in FOptions.SelectionOptions) then
  13422. FRangeAnchor := NewNode;
  13423. // Delay selection change if a drag operation is pending.
  13424. // Otherwise switch selection state here.
  13425. if DragPending then
  13426. DoStateChange([tsToggleFocusedSelection])
  13427. else
  13428. if vsSelected in NewNode.States then
  13429. RemoveFromSelection(NewNode)
  13430. else
  13431. AddToSelection(NewNode);
  13432. end;
  13433. Invalidate();
  13434. end
  13435. else
  13436. // Shift key down
  13437. if ssShift in Shift then
  13438. begin
  13439. if FRangeAnchor = nil then
  13440. FRangeAnchor := FRoot.FirstChild;
  13441. // select node range
  13442. if Assigned(FRangeAnchor) then
  13443. begin
  13444. SelectNodes(FRangeAnchor, NewNode, False);
  13445. Invalidate;
  13446. end;
  13447. end
  13448. else
  13449. begin
  13450. // any other case
  13451. if not (vsSelected in NewNode.States) then
  13452. begin
  13453. AddToSelection(NewNode);
  13454. InvalidateNode(NewNode);
  13455. end;
  13456. // assign new reference item
  13457. FRangeAnchor := NewNode;
  13458. end;
  13459. end;
  13460. //----------------------------------------------------------------------------------------------------------------------
  13461. function TBaseVirtualTree.HandleDrawSelection(X, Y: Integer): Boolean;
  13462. // Handles multi-selection with a focus rectangle.
  13463. // Result is True if something changed in selection.
  13464. var
  13465. OldRect,
  13466. NewRect: TRect;
  13467. MainColumn: TColumnIndex;
  13468. MaxValue: Integer;
  13469. // limits of a node and its text
  13470. NodeLeft,
  13471. NodeRight: Integer;
  13472. // alignment and directionality
  13473. CurrentBidiMode: TBidiMode;
  13474. CurrentAlignment: TAlignment;
  13475. begin
  13476. Result := False;
  13477. // Selection changes are only done if the user drew a selection rectangle large
  13478. // enough to exceed the threshold.
  13479. if (FRoot.TotalCount > 1) and (tsDrawSelecting in FStates) then
  13480. begin
  13481. // Effective handling of node selection is done by using two rectangles stored in FSelectRec.
  13482. OldRect := OrderRect(FLastSelRect);
  13483. NewRect := OrderRect(FNewSelRect);
  13484. ClearTempCache;
  13485. MainColumn := FHeader.MainColumn;
  13486. // Alignment and bidi mode determine where the node text is located within a node.
  13487. if MainColumn <= NoColumn then
  13488. begin
  13489. CurrentBidiMode := BidiMode;
  13490. CurrentAlignment := Alignment;
  13491. end
  13492. else
  13493. begin
  13494. CurrentBidiMode := FHeader.FColumns[MainColumn].BidiMode;
  13495. CurrentAlignment := FHeader.FColumns[MainColumn].Alignment;
  13496. end;
  13497. // Determine initial left border of first node (take column reordering into account).
  13498. if FHeader.UseColumns then
  13499. begin
  13500. // The mouse coordinates don't include any horizontal scrolling hence take this also
  13501. // out from the returned column position.
  13502. NodeLeft := FHeader.FColumns[MainColumn].Left - FEffectiveOffsetX;
  13503. NodeRight := NodeLeft + FHeader.FColumns[MainColumn].Width;
  13504. end
  13505. else
  13506. begin
  13507. NodeLeft := 0;
  13508. NodeRight := ClientWidth;
  13509. end;
  13510. if CurrentBidiMode = bdLeftToRight then
  13511. Result := CollectSelectedNodesLTR(MainColumn, NodeLeft, NodeRight, CurrentAlignment, OldRect, NewRect)
  13512. else
  13513. Result := CollectSelectedNodesRTL(MainColumn, NodeLeft, NodeRight, CurrentAlignment, OldRect, NewRect);
  13514. end;
  13515. if Result then
  13516. begin
  13517. // Do some housekeeping if there was a change.
  13518. MaxValue := PackArray(FSelection, FSelectionCount);
  13519. if MaxValue > -1 then
  13520. begin
  13521. FSelectionCount := MaxValue;
  13522. SetLength(FSelection, FSelectionCount);
  13523. end;
  13524. if FTempNodeCount > 0 then
  13525. begin
  13526. AddToSelection(FTempNodeCache, FTempNodeCount);
  13527. ClearTempCache;
  13528. end;
  13529. Change(nil);
  13530. end;
  13531. end;
  13532. //----------------------------------------------------------------------------------------------------------------------
  13533. function TBaseVirtualTree.HasVisibleNextSibling(Node: PVirtualNode): Boolean;
  13534. // Helper method to determine if the given node has a visible next sibling. This is needed to
  13535. // draw correct tree lines.
  13536. begin
  13537. // Check if there is a sibling at all.
  13538. Result := Assigned(Node.NextSibling);
  13539. if Result then
  13540. begin
  13541. repeat
  13542. Node := Node.NextSibling;
  13543. Result := IsEffectivelyVisible[Node];
  13544. until Result or (Node.NextSibling = nil);
  13545. end;
  13546. end;
  13547. //----------------------------------------------------------------------------------------------------------------------
  13548. function TBaseVirtualTree.HasVisiblePreviousSibling(Node: PVirtualNode): Boolean;
  13549. // Helper method to determine if the given node has a visible previous sibling. This is needed to
  13550. // draw correct tree lines.
  13551. begin
  13552. // Check if there is a sibling at all.
  13553. Result := Assigned(Node.PrevSibling);
  13554. if Result then
  13555. begin
  13556. repeat
  13557. Node := Node.PrevSibling;
  13558. Result := IsEffectivelyVisible[Node];
  13559. until Result or (Node.PrevSibling = nil);
  13560. end;
  13561. end;
  13562. //----------------------------------------------------------------------------------------------------------------------
  13563. procedure TBaseVirtualTree.ImageListChange(Sender: TObject);
  13564. begin
  13565. if not (csDestroying in ComponentState) then
  13566. Invalidate;
  13567. end;
  13568. //----------------------------------------------------------------------------------------------------------------------
  13569. procedure TBaseVirtualTree.InitializeFirstColumnValues(var PaintInfo: TVTPaintInfo);
  13570. // Determines initial index, position and cell size of the first visible column.
  13571. begin
  13572. PaintInfo.Column := FHeader.FColumns.GetFirstVisibleColumn;
  13573. with FHeader.FColumns, PaintInfo do
  13574. begin
  13575. if Column > NoColumn then
  13576. begin
  13577. CellRect.Right := CellRect.Left + Items[Column].Width;
  13578. Position := Items[Column].Position;
  13579. end
  13580. else
  13581. Position := 0;
  13582. end;
  13583. end;
  13584. //----------------------------------------------------------------------------------------------------------------------
  13585. procedure TBaseVirtualTree.InitRootNode(OldSize: Cardinal = 0);
  13586. // Reinitializes the root node.
  13587. var
  13588. NewSize: Cardinal;
  13589. begin
  13590. NewSize := TreeNodeSize + FTotalInternalDataSize;
  13591. if FRoot = nil then
  13592. FRoot := AllocMem(NewSize)
  13593. else
  13594. begin
  13595. ReallocMem(FRoot, NewSize);
  13596. ZeroMemory(PByte(FRoot) + OldSize, NewSize - OldSize);
  13597. end;
  13598. with FRoot^ do
  13599. begin
  13600. // Indication that this node is the root node.
  13601. PrevSibling := FRoot;
  13602. NextSibling := FRoot;
  13603. Parent := Pointer(Self);
  13604. States := [vsInitialized, vsExpanded, vsHasChildren, vsVisible];
  13605. TotalHeight := FDefaultNodeHeight;
  13606. TotalCount := 1;
  13607. NodeHeight := FDefaultNodeHeight;
  13608. Align := 50;
  13609. end;
  13610. end;
  13611. //----------------------------------------------------------------------------------------------------------------------
  13612. procedure TBaseVirtualTree.InterruptValidation;
  13613. var
  13614. WasValidating: Boolean;
  13615. begin
  13616. DoStateChange([tsStopValidation], [tsUseCache]);
  13617. // Check the worker thread existance. It might already be gone (usually on destruction of the last tree).
  13618. if Assigned(WorkerThread) then
  13619. begin
  13620. WasValidating := (tsValidating in FStates);
  13621. WorkerThread.RemoveTree(Self);
  13622. if WasValidating then
  13623. InvalidateCache();
  13624. end;
  13625. end;
  13626. //----------------------------------------------------------------------------------------------------------------------
  13627. function TBaseVirtualTree.IsFirstVisibleChild(Parent, Node: PVirtualNode): Boolean;
  13628. // Helper method to check if Node is the same as the first visible child of Parent.
  13629. var
  13630. Run: PVirtualNode;
  13631. begin
  13632. // Find first visible child.
  13633. Run := Parent.FirstChild;
  13634. while Assigned(Run) and not IsEffectivelyVisible[Run] do
  13635. Run := Run.NextSibling;
  13636. Result := Assigned(Run) and (Run = Node);
  13637. end;
  13638. //----------------------------------------------------------------------------------------------------------------------
  13639. function TBaseVirtualTree.IsLastVisibleChild(Parent, Node: PVirtualNode): Boolean;
  13640. // Helper method to check if Node is the same as the last visible child of Parent.
  13641. var
  13642. Run: PVirtualNode;
  13643. begin
  13644. // Find last visible child.
  13645. Run := Parent.LastChild;
  13646. while Assigned(Run) and not IsEffectivelyVisible[Run] do
  13647. Run := Run.PrevSibling;
  13648. Result := Assigned(Run) and (Run = Node);
  13649. end;
  13650. //----------------------------------------------------------------------------------------------------------------------
  13651. function TBaseVirtualTree.MakeNewNode: PVirtualNode;
  13652. var
  13653. Size: Cardinal;
  13654. begin
  13655. Size := TreeNodeSize;
  13656. if not (csDesigning in ComponentState) then
  13657. begin
  13658. // Make sure FNodeDataSize is valid.
  13659. if FNodeDataSize = -1 then
  13660. ValidateNodeDataSize(FNodeDataSize);
  13661. // Take record alignment into account.
  13662. Inc(Size, FNodeDataSize);
  13663. end;
  13664. Result := AllocMem(Size + FTotalInternalDataSize);
  13665. // Fill in some default values.
  13666. with Result^ do
  13667. begin
  13668. TotalCount := 1;
  13669. TotalHeight := FDefaultNodeHeight;
  13670. NodeHeight := FDefaultNodeHeight;
  13671. States := [vsVisible];
  13672. Align := 50;
  13673. end;
  13674. end;
  13675. //----------------------------------------------------------------------------------------------------------------------
  13676. function TBaseVirtualTree.PackArray({*}const TheArray: TNodeArray; Count: Integer): Integer; assembler;
  13677. // *This is an optimization to get as near as possible with the PUREPASCAL code without the
  13678. // compiler generating a _DynArrayAddRef call. We still modify the array's content via pointers.
  13679. // Removes all entries from the selection array which are no longer in use. The selection array must be sorted for this
  13680. // algo to work. Values which must be removed are marked with bit 0 (LSB) set. This little trick works because memory
  13681. // is always allocated DWORD aligned. Since the selection array must be sorted while determining the entries to be
  13682. // removed it is much more efficient to increment the entry in question instead of setting it to nil (which would break
  13683. // the ordered appearance of the list).
  13684. //
  13685. // On enter EAX contains self reference, EDX the address to TheArray and ECX Count
  13686. // The returned value is the number of remaining entries in the array, so the caller can reallocate (shorten)
  13687. // the selection array if needed or -1 if nothing needs to be changed.
  13688. {$ifdef CPUX64}
  13689. var
  13690. Source, Dest: ^PVirtualNode;
  13691. ConstOne: NativeInt;
  13692. begin
  13693. Source := Pointer(TheArray);
  13694. ConstOne := 1;
  13695. Result := 0;
  13696. // Do the fastest scan possible to find the first entry
  13697. while (Count <> 0) and {not Odd(NativeInt(Source^))} (NativeInt(Source^) and ConstOne = 0) do
  13698. begin
  13699. Inc(Result);
  13700. Inc(Source);
  13701. Dec(Count);
  13702. end;
  13703. if Count <> 0 then
  13704. begin
  13705. Dest := Source;
  13706. repeat
  13707. // Skip odd entries
  13708. if {not Odd(NativeInt(Source^))} NativeInt(Source^) and ConstOne = 0 then
  13709. begin
  13710. Dest^ := Source^;
  13711. Inc(Result);
  13712. Inc(Dest);
  13713. end;
  13714. Inc(Source); // Point to the next entry
  13715. Dec(Count);
  13716. until Count = 0;
  13717. end;
  13718. end;
  13719. {$else}
  13720. asm
  13721. PUSH EBX
  13722. PUSH EDI
  13723. PUSH ESI
  13724. MOV ESI, EDX
  13725. MOV EDX, -1
  13726. JCXZ @@Finish // Empty list?
  13727. INC EDX // init remaining entries counter
  13728. MOV EDI, ESI // source and destination point to the list memory
  13729. MOV EBX, 1 // use a register instead of immediate operant to check against
  13730. @@PreScan:
  13731. TEST [ESI], EBX // do the fastest scan possible to find the first entry
  13732. // which must be removed
  13733. JNZ @@DoMainLoop
  13734. INC EDX
  13735. ADD ESI, 4
  13736. DEC ECX
  13737. JNZ @@PreScan
  13738. JMP @@Finish
  13739. @@DoMainLoop:
  13740. MOV EDI, ESI
  13741. @@MainLoop:
  13742. TEST [ESI], EBX // odd entry?
  13743. JNE @@Skip // yes, so skip this one
  13744. MOVSD // else move the entry to new location
  13745. INC EDX // count the moved entries
  13746. DEC ECX
  13747. JNZ @@MainLoop // do it until all entries are processed
  13748. JMP @@Finish
  13749. @@Skip:
  13750. ADD ESI, 4 // point to the next entry
  13751. DEC ECX
  13752. JNZ @@MainLoop // do it until all entries are processed
  13753. @@Finish:
  13754. MOV EAX, EDX // prepare return value
  13755. POP ESI
  13756. POP EDI
  13757. POP EBX
  13758. end;
  13759. {$endif CPUX64}
  13760. //----------------------------------------------------------------------------------------------------------------------
  13761. procedure TBaseVirtualTree.PrepareBitmaps(NeedButtons, NeedLines: Boolean);
  13762. // initializes the contents of the internal bitmaps
  13763. const
  13764. LineBitsDotted: array [0..8] of Word = ($55, $AA, $55, $AA, $55, $AA, $55, $AA, $55);
  13765. LineBitsSolid: array [0..7] of Word = (0, 0, 0, 0, 0, 0, 0, 0);
  13766. var
  13767. PatternBitmap: HBITMAP;
  13768. Bits: Pointer;
  13769. Size: TSize;
  13770. Theme: HTHEME;
  13771. R: TRect;
  13772. //--------------- local function --------------------------------------------
  13773. procedure FillBitmap (ABitmap: TBitmap);
  13774. begin
  13775. with ABitmap, Canvas do
  13776. begin
  13777. Width := Size.cx;
  13778. Height := Size.cy;
  13779. if IsWinVistaOrAbove and (tsUseThemes in FStates) and (toUseExplorerTheme in FOptions.FPaintOptions) or VclStyleEnabled then
  13780. begin
  13781. if (FHeader.MainColumn > NoColumn) and not (coParentColor in FHeader.FColumns[FHeader.MainColumn].Options) then
  13782. Brush.Color := FHeader.FColumns[FHeader.MainColumn].Color
  13783. else
  13784. Brush.Color := FColors.BackGroundColor;
  13785. end
  13786. else
  13787. Brush.Color := clFuchsia;
  13788. Transparent := True;
  13789. TransparentColor := Brush.Color;
  13790. FillRect(Rect(0, 0, Width, Height));
  13791. end;
  13792. end;
  13793. //--------------- end local function ----------------------------------------
  13794. begin
  13795. Size.cx := 9;
  13796. Size.cy := 9;
  13797. if tsUseThemes in FStates then
  13798. begin
  13799. R := Rect(0, 0, 100, 100);
  13800. Theme := OpenThemeData(Handle, 'TREEVIEW');
  13801. GetThemePartSize(Theme, FPlusBM.Canvas.Handle, TVP_GLYPH, GLPS_OPENED, @R, TS_TRUE, Size);
  13802. end
  13803. else
  13804. Theme := 0;
  13805. if NeedButtons then
  13806. begin
  13807. with FMinusBM, Canvas do
  13808. begin
  13809. // box is always of odd size
  13810. FillBitmap(FMinusBM);
  13811. FillBitmap(FHotMinusBM);
  13812. // Weil die selbstgezeichneten Bitmaps sehen im Vcl Style scheiße aus
  13813. if (not VclStyleEnabled) or (Theme = 0) then
  13814. begin
  13815. if not(tsUseExplorerTheme in FStates) then
  13816. begin
  13817. if FButtonStyle = bsTriangle then
  13818. begin
  13819. Brush.Color := clBlack;
  13820. Pen.Color := clBlack;
  13821. Polygon([Point(0, 2), Point(8, 2), Point(4, 6)]);
  13822. end
  13823. else
  13824. begin
  13825. // Button style is rectangular. Now ButtonFillMode determines how to fill the interior.
  13826. if FButtonFillMode in [fmTreeColor, fmWindowColor, fmTransparent] then
  13827. begin
  13828. case FButtonFillMode of
  13829. fmTreeColor:
  13830. Brush.Color := FColors.BackGroundColor;
  13831. fmWindowColor:
  13832. Brush.Color := clWindow;
  13833. end;
  13834. Pen.Color := FColors.TreeLineColor;
  13835. Rectangle(0, 0, Width, Height);
  13836. Pen.Color := FColors.NodeFontColor;
  13837. MoveTo(2, Width div 2);
  13838. LineTo(Width - 2, Width div 2);
  13839. end
  13840. else
  13841. FMinusBM.Handle := LoadBitmap(HInstance, 'VT_XPBUTTONMINUS');
  13842. FHotMinusBM.Canvas.Draw(0, 0, FMinusBM);
  13843. end;
  13844. end;
  13845. end;
  13846. end;
  13847. with FPlusBM, Canvas do
  13848. begin
  13849. FillBitmap(FPlusBM);
  13850. FillBitmap(FHotPlusBM);
  13851. if (not VclStyleEnabled) or (Theme = 0) then
  13852. begin
  13853. if not(tsUseExplorerTheme in FStates) then
  13854. begin
  13855. if FButtonStyle = bsTriangle then
  13856. begin
  13857. Brush.Color := clBlack;
  13858. Pen.Color := clBlack;
  13859. Polygon([Point(2, 0), Point(6, 4), Point(2, 8)]);
  13860. end
  13861. else
  13862. begin
  13863. // Button style is rectangular. Now ButtonFillMode determines how to fill the interior.
  13864. if FButtonFillMode in [fmTreeColor, fmWindowColor, fmTransparent] then
  13865. begin
  13866. case FButtonFillMode of
  13867. fmTreeColor:
  13868. Brush.Color := FColors.BackGroundColor;
  13869. fmWindowColor:
  13870. Brush.Color := clWindow;
  13871. end;
  13872. Pen.Color := FColors.TreeLineColor;
  13873. Rectangle(0, 0, Width, Height);
  13874. Pen.Color := FColors.NodeFontColor;
  13875. MoveTo(2, Width div 2);
  13876. LineTo(Width - 2, Width div 2);
  13877. MoveTo(Width div 2, 2);
  13878. LineTo(Width div 2, Width - 2);
  13879. end
  13880. else
  13881. FPlusBM.Handle := LoadBitmap(HInstance, 'VT_XPBUTTONPLUS');
  13882. FHotPlusBM.Canvas.Draw(0, 0, FPlusBM);
  13883. end;
  13884. end;
  13885. end;
  13886. end;
  13887. // Overwrite glyph images if theme is active.
  13888. if (tsUseThemes in FStates) and (Theme <> 0) then
  13889. begin
  13890. R := Rect(0, 0, Size.cx, Size.cy);
  13891. DrawThemeBackground(Theme, FPlusBM.Canvas.Handle, TVP_GLYPH, GLPS_CLOSED, R, nil);
  13892. DrawThemeBackground(Theme, FMinusBM.Canvas.Handle, TVP_GLYPH, GLPS_OPENED, R, nil);
  13893. if tsUseExplorerTheme in FStates then
  13894. begin
  13895. DrawThemeBackground(Theme, FHotPlusBM.Canvas.Handle, TVP_HOTGLYPH, GLPS_CLOSED, R, nil);
  13896. DrawThemeBackground(Theme, FHotMinusBM.Canvas.Handle, TVP_HOTGLYPH, GLPS_OPENED, R, nil);
  13897. end
  13898. else
  13899. begin
  13900. FHotPlusBM.Canvas.Draw(0, 0, FPlusBM);
  13901. FHotMinusBM.Canvas.Draw(0, 0, FMinusBM);
  13902. end;
  13903. end;
  13904. end;
  13905. if NeedLines then
  13906. begin
  13907. if FDottedBrush <> 0 then
  13908. DeleteObject(FDottedBrush);
  13909. case FLineStyle of
  13910. lsDotted:
  13911. Bits := @LineBitsDotted;
  13912. lsSolid:
  13913. Bits := @LineBitsSolid;
  13914. else // lsCustomStyle
  13915. Bits := @LineBitsDotted;
  13916. DoGetLineStyle(Bits);
  13917. end;
  13918. PatternBitmap := CreateBitmap(8, 8, 1, 1, Bits);
  13919. FDottedBrush := CreatePatternBrush(PatternBitmap);
  13920. DeleteObject(PatternBitmap);
  13921. end;
  13922. if tsUseThemes in FStates then
  13923. CloseThemeData(Theme);
  13924. end;
  13925. //----------------------------------------------------------------------------------------------------------------------
  13926. type
  13927. TOldVTOption = (voAcceptOLEDrop, voAnimatedToggle, voAutoDropExpand, voAutoExpand, voAutoScroll,
  13928. voAutoSort, voAutoSpanColumns, voAutoTristateTracking, voCheckSupport, voDisableDrawSelection, voEditable,
  13929. voExtendedFocus, voFullRowSelect, voGridExtensions, voHideFocusRect, voHideSelection, voHotTrack, voInitOnSave,
  13930. voLevelSelectConstraint, voMiddleClickSelect, voMultiSelect, voRightClickSelect, voPopupMode, voShowBackground,
  13931. voShowButtons, voShowDropmark, voShowHorzGridLines, voShowRoot, voShowTreeLines, voShowVertGridLines,
  13932. voSiblingSelectConstraint, voToggleOnDblClick);
  13933. const
  13934. OptionMap: array[TOldVTOption] of Integer = (
  13935. Ord(toAcceptOLEDrop), Ord(toAnimatedToggle), Ord(toAutoDropExpand), Ord(toAutoExpand), Ord(toAutoScroll),
  13936. Ord(toAutoSort), Ord(toAutoSpanColumns), Ord(toAutoTristateTracking), Ord(toCheckSupport), Ord(toDisableDrawSelection),
  13937. Ord(toEditable), Ord(toExtendedFocus), Ord(toFullRowSelect), Ord(toGridExtensions), Ord(toHideFocusRect),
  13938. Ord(toHideSelection), Ord(toHotTrack), Ord(toInitOnSave), Ord(toLevelSelectConstraint), Ord(toMiddleClickSelect),
  13939. Ord(toMultiSelect), Ord(toRightClickSelect), Ord(toPopupMode), Ord(toShowBackground),
  13940. Ord(toShowButtons), Ord(toShowDropmark), Ord(toShowHorzGridLines), Ord(toShowRoot), Ord(toShowTreeLines),
  13941. Ord(toShowVertGridLines), Ord(toSiblingSelectConstraint), Ord(toToggleOnDblClick)
  13942. );
  13943. procedure TBaseVirtualTree.ReadOldOptions(Reader: TReader);
  13944. // Migration helper routine to silently convert forms containing the old tree options member into the new
  13945. // sub-options structure.
  13946. var
  13947. OldOption: TOldVTOption;
  13948. EnumName: string;
  13949. begin
  13950. // If we are at design time currently then let the designer know we changed something.
  13951. UpdateDesigner;
  13952. // It should never happen at this place that there is something different than the old set.
  13953. if Reader.ReadValue = vaSet then
  13954. begin
  13955. // Remove all default values set by the constructor.
  13956. FOptions.AnimationOptions := [];
  13957. FOptions.AutoOptions := [];
  13958. FOptions.MiscOptions := [];
  13959. FOptions.PaintOptions := [];
  13960. FOptions.SelectionOptions := [];
  13961. while True do
  13962. begin
  13963. // Sets are stored with their members as simple strings. Read them one by one and map them to the new option
  13964. // in the correct sub-option set.
  13965. EnumName := Reader.ReadStr;
  13966. if EnumName = '' then
  13967. Break;
  13968. OldOption := TOldVTOption(GetEnumValue(TypeInfo(TOldVTOption), EnumName));
  13969. case OldOption of
  13970. voAcceptOLEDrop, voCheckSupport, voEditable, voGridExtensions, voInitOnSave, voToggleOnDblClick:
  13971. FOptions.MiscOptions := FOptions.FMiscOptions + [TVTMiscOption(OptionMap[OldOption])];
  13972. voAnimatedToggle:
  13973. FOptions.AnimationOptions := FOptions.FAnimationOptions + [TVTAnimationOption(OptionMap[OldOption])];
  13974. voAutoDropExpand, voAutoExpand, voAutoScroll, voAutoSort, voAutoSpanColumns, voAutoTristateTracking:
  13975. FOptions.AutoOptions := FOptions.FAutoOptions + [TVTAutoOption(OptionMap[OldOption])];
  13976. voDisableDrawSelection, voExtendedFocus, voFullRowSelect, voLevelSelectConstraint,
  13977. voMiddleClickSelect, voMultiSelect, voRightClickSelect, voSiblingSelectConstraint:
  13978. FOptions.SelectionOptions := FOptions.FSelectionOptions + [TVTSelectionOption(OptionMap[OldOption])];
  13979. voHideFocusRect, voHideSelection, voHotTrack, voPopupMode, voShowBackground, voShowButtons,
  13980. voShowDropmark, voShowHorzGridLines, voShowRoot, voShowTreeLines, voShowVertGridLines:
  13981. FOptions.PaintOptions := FOptions.FPaintOptions + [TVTPaintOption(OptionMap[OldOption])];
  13982. end;
  13983. end;
  13984. end;
  13985. end;
  13986. //----------------------------------------------------------------------------------------------------------------------
  13987. procedure TBaseVirtualTree.SetAlignment(const Value: TAlignment);
  13988. begin
  13989. if FAlignment <> Value then
  13990. begin
  13991. FAlignment := Value;
  13992. if not (csLoading in ComponentState) then
  13993. Invalidate;
  13994. end;
  13995. end;
  13996. //----------------------------------------------------------------------------------------------------------------------
  13997. procedure TBaseVirtualTree.SetAnimationDuration(const Value: Cardinal);
  13998. begin
  13999. FAnimationDuration := Value;
  14000. if FAnimationDuration = 0 then
  14001. Exclude(FOptions.FAnimationOptions, toAnimatedToggle)
  14002. else
  14003. Include(FOptions.FAnimationOptions, toAnimatedToggle);
  14004. end;
  14005. //----------------------------------------------------------------------------------------------------------------------
  14006. procedure TBaseVirtualTree.SetBackground(const Value: TPicture);
  14007. begin
  14008. FBackground.Assign(Value);
  14009. Invalidate;
  14010. end;
  14011. //----------------------------------------------------------------------------------------------------------------------
  14012. procedure TBaseVirtualTree.SetBackgroundOffset(const Index, Value: Integer);
  14013. begin
  14014. case Index of
  14015. 0:
  14016. if FBackgroundOffsetX <> Value then
  14017. begin
  14018. FBackgroundOffsetX := Value;
  14019. Invalidate;
  14020. end;
  14021. 1:
  14022. if FBackgroundOffsetY <> Value then
  14023. begin
  14024. FBackgroundOffsetY := Value;
  14025. Invalidate;
  14026. end;
  14027. end;
  14028. end;
  14029. //----------------------------------------------------------------------------------------------------------------------
  14030. procedure TBaseVirtualTree.SetBorderStyle(Value: TBorderStyle);
  14031. begin
  14032. if FBorderStyle <> Value then
  14033. begin
  14034. FBorderStyle := Value;
  14035. RecreateWnd;
  14036. end;
  14037. end;
  14038. //----------------------------------------------------------------------------------------------------------------------
  14039. procedure TBaseVirtualTree.SetBottomNode(Node: PVirtualNode);
  14040. var
  14041. Run: PVirtualNode;
  14042. R: TRect;
  14043. begin
  14044. if Assigned(Node) then
  14045. begin
  14046. // make sure all parents of the node are expanded
  14047. Run := Node.Parent;
  14048. while Run <> FRoot do
  14049. begin
  14050. if not (vsExpanded in Run.States) then
  14051. ToggleNode(Run);
  14052. Run := Run.Parent;
  14053. end;
  14054. R := GetDisplayRect(Node, FHeader.MainColumn, True);
  14055. DoSetOffsetXY(Point(FOffsetX, FOffsetY + ClientHeight - R.Top - Integer(NodeHeight[Node])),
  14056. [suoRepaintScrollBars, suoUpdateNCArea]);
  14057. end;
  14058. end;
  14059. //----------------------------------------------------------------------------------------------------------------------
  14060. procedure TBaseVirtualTree.SetBottomSpace(const Value: Cardinal);
  14061. begin
  14062. if FBottomSpace <> Value then
  14063. begin
  14064. FBottomSpace := Value;
  14065. UpdateVerticalScrollBar(True);
  14066. end;
  14067. end;
  14068. //----------------------------------------------------------------------------------------------------------------------
  14069. procedure TBaseVirtualTree.SetButtonFillMode(const Value: TVTButtonFillMode);
  14070. begin
  14071. if FButtonFillMode <> Value then
  14072. begin
  14073. FButtonFillMode := Value;
  14074. if not (csLoading in ComponentState) then
  14075. begin
  14076. PrepareBitmaps(True, False);
  14077. if HandleAllocated then
  14078. Invalidate;
  14079. end;
  14080. end;
  14081. end;
  14082. //----------------------------------------------------------------------------------------------------------------------
  14083. procedure TBaseVirtualTree.SetButtonStyle(const Value: TVTButtonStyle);
  14084. begin
  14085. if FButtonStyle <> Value then
  14086. begin
  14087. FButtonStyle := Value;
  14088. if not (csLoading in ComponentState) then
  14089. begin
  14090. PrepareBitmaps(True, False);
  14091. if HandleAllocated then
  14092. Invalidate;
  14093. end;
  14094. end;
  14095. end;
  14096. //----------------------------------------------------------------------------------------------------------------------
  14097. procedure TBaseVirtualTree.SetCheckImageKind(Value: TCheckImageKind);
  14098. begin
  14099. if FCheckImageKind <> Value then
  14100. begin
  14101. FCheckImageKind := Value;
  14102. FCheckImages := GetCheckImageListFor(Value);
  14103. if not Assigned(FCheckImages) then
  14104. FCheckImages := FCustomCheckImages;
  14105. if HandleAllocated and (FUpdateCount = 0) and not (csLoading in ComponentState) then
  14106. InvalidateRect(Handle, nil, False);
  14107. end;
  14108. end;
  14109. //----------------------------------------------------------------------------------------------------------------------
  14110. procedure TBaseVirtualTree.SetCheckState(Node: PVirtualNode; Value: TCheckState);
  14111. begin
  14112. if (Node.CheckState <> Value) and not (vsDisabled in Node.States) and DoChecking(Node, Value) then
  14113. DoCheckClick(Node, Value);
  14114. end;
  14115. //----------------------------------------------------------------------------------------------------------------------
  14116. procedure TBaseVirtualTree.SetCheckType(Node: PVirtualNode; Value: TCheckType);
  14117. begin
  14118. if (Node.CheckType <> Value) and not (toReadOnly in FOptions.FMiscOptions) then
  14119. begin
  14120. Node.CheckType := Value;
  14121. if (Value <> ctTriStateCheckBox) and (Node.CheckState in [csMixedNormal, csMixedPressed]) then
  14122. Node.CheckState := csUncheckedNormal;// reset check state if it doesn't fit the new check type
  14123. // For check boxes with tri-state check box parents we have to initialize differently.
  14124. if (toAutoTriStateTracking in FOptions.FAutoOptions) and (Value in [ctCheckBox, ctTriStateCheckBox]) and
  14125. (Node.Parent <> FRoot) then
  14126. begin
  14127. if not (vsInitialized in Node.Parent.States) then
  14128. InitNode(Node.Parent);
  14129. if (Node.Parent.CheckType = ctTriStateCheckBox) and
  14130. (Node.Parent.CheckState in [csUncheckedNormal, csCheckedNormal]) then
  14131. CheckState[Node] := Node.Parent.CheckState;
  14132. end;
  14133. InvalidateNode(Node);
  14134. end;
  14135. end;
  14136. //----------------------------------------------------------------------------------------------------------------------
  14137. procedure TBaseVirtualTree.SetChildCount(Node: PVirtualNode; NewChildCount: Cardinal);
  14138. // Changes a node's child structure to accomodate the new child count. This is used to add or delete
  14139. // child nodes to/from the end of the node's child list. To insert or delete a specific node a separate
  14140. // routine is used.
  14141. var
  14142. Remaining: Cardinal;
  14143. Index: Cardinal;
  14144. Child: PVirtualNode;
  14145. Count: Integer;
  14146. NewHeight: Integer;
  14147. lNodeHeight: Integer;
  14148. begin
  14149. if not (toReadOnly in FOptions.FMiscOptions) then
  14150. begin
  14151. if Node = nil then
  14152. Node := FRoot;
  14153. if NewChildCount = 0 then
  14154. DeleteChildren(Node)
  14155. else
  14156. begin
  14157. // If nothing changed then do nothing.
  14158. if NewChildCount <> Node.ChildCount then
  14159. begin
  14160. InterruptValidation;
  14161. NewHeight := 0;
  14162. if NewChildCount > Node.ChildCount then
  14163. begin
  14164. Remaining := NewChildCount - Node.ChildCount;
  14165. Count := Remaining;
  14166. // New nodes to add.
  14167. if Assigned(Node.LastChild) then
  14168. Index := Node.LastChild.Index + 1
  14169. else
  14170. begin
  14171. Index := 0;
  14172. Include(Node.States, vsHasChildren);
  14173. end;
  14174. Node.States := Node.States - [vsAllChildrenHidden, vsHeightMeasured];
  14175. // New nodes are by default always visible, so we don't need to check the visibility.
  14176. while Remaining > 0 do
  14177. begin
  14178. Child := MakeNewNode;
  14179. Child.Index := Index;
  14180. Child.PrevSibling := Node.LastChild;
  14181. if Assigned(Node.LastChild) then
  14182. Node.LastChild.NextSibling := Child;
  14183. Child.Parent := Node;
  14184. Node.LastChild := Child;
  14185. if Node.FirstChild = nil then
  14186. Node.FirstChild := Child;
  14187. Dec(Remaining);
  14188. Inc(Index);
  14189. if (toVariableNodeHeight in FOptions.FMiscOptions) then
  14190. begin
  14191. lNodeHeight := Child.NodeHeight;
  14192. DoMeasureItem(Canvas, Child, lNodeHeight);
  14193. Child.NodeHeight := lNodeHeight;
  14194. Child.TotalHeight := lNodeHeight;
  14195. end;
  14196. Inc(NewHeight, Child.NodeHeight);
  14197. end;
  14198. if vsExpanded in Node.States then
  14199. begin
  14200. AdjustTotalHeight(Node, NewHeight, True);
  14201. if FullyVisible[Node] then
  14202. Inc(Integer(FVisibleCount), Count);
  14203. end;
  14204. AdjustTotalCount(Node, Count, True);
  14205. Node.ChildCount := NewChildCount;
  14206. if (FUpdateCount = 0) and (toAutoSort in FOptions.FAutoOptions) and (FHeader.FSortColumn > InvalidColumn) then
  14207. Sort(Node, FHeader.FSortColumn, FHeader.FSortDirection, True);
  14208. InvalidateCache;
  14209. end
  14210. else
  14211. begin
  14212. // Nodes have to be deleted.
  14213. Remaining := Node.ChildCount - NewChildCount;
  14214. while Remaining > 0 do
  14215. begin
  14216. DeleteNode(Node.LastChild);
  14217. Dec(Remaining);
  14218. end;
  14219. end;
  14220. if FUpdateCount = 0 then
  14221. begin
  14222. ValidateCache;
  14223. UpdateScrollBars(True);
  14224. Invalidate;
  14225. end;
  14226. if Node = FRoot then
  14227. StructureChange(nil, crChildAdded)
  14228. else
  14229. StructureChange(Node, crChildAdded);
  14230. end;
  14231. end;
  14232. end;
  14233. end;
  14234. //----------------------------------------------------------------------------------------------------------------------
  14235. procedure TBaseVirtualTree.SetClipboardFormats(const Value: TClipboardFormats);
  14236. var
  14237. I: Integer;
  14238. begin
  14239. // Add string by string instead doing an Assign or AddStrings because the list may return -1 for
  14240. // invalid entries which cause trouble for the standard implementation.
  14241. FClipboardFormats.Clear;
  14242. for I := 0 to Value.Count - 1 do
  14243. FClipboardFormats.Add(Value[I]);
  14244. end;
  14245. //----------------------------------------------------------------------------------------------------------------------
  14246. procedure TBaseVirtualTree.SetColors(const Value: TVTColors);
  14247. begin
  14248. FColors.Assign(Value);
  14249. end;
  14250. //----------------------------------------------------------------------------------------------------------------------
  14251. procedure TBaseVirtualTree.SetCustomCheckImages(const Value: TCustomImageList);
  14252. begin
  14253. if FCustomCheckImages <> Value then
  14254. begin
  14255. if Assigned(FCustomCheckImages) then
  14256. begin
  14257. FCustomCheckImages.UnRegisterChanges(FCustomCheckChangeLink);
  14258. FCustomCheckImages.RemoveFreeNotification(Self);
  14259. // Reset the internal check image list reference too, if necessary.
  14260. if FCheckImages = FCustomCheckImages then
  14261. FCheckImages := nil;
  14262. end;
  14263. FCustomCheckImages := Value;
  14264. if Assigned(FCustomCheckImages) then
  14265. begin
  14266. FCustomCheckImages.RegisterChanges(FCustomCheckChangeLink);
  14267. FCustomCheckImages.FreeNotification(Self);
  14268. end;
  14269. // Check if currently custom check images are active.
  14270. if FCheckImageKind = ckCustom then
  14271. FCheckImages := Value;
  14272. if not (csLoading in ComponentState) then
  14273. Invalidate;
  14274. end;
  14275. end;
  14276. //----------------------------------------------------------------------------------------------------------------------
  14277. procedure TBaseVirtualTree.SetDefaultNodeHeight(Value: Cardinal);
  14278. begin
  14279. if Value = 0 then
  14280. Value := 18;
  14281. if FDefaultNodeHeight <> Value then
  14282. begin
  14283. Inc(Integer(FRoot.TotalHeight), Integer(Value) - Integer(FDefaultNodeHeight));
  14284. Inc(SmallInt(FRoot.NodeHeight), Integer(Value) - Integer(FDefaultNodeHeight));
  14285. FDefaultNodeHeight := Value;
  14286. InvalidateCache;
  14287. if (FUpdateCount = 0) and HandleAllocated and not (csLoading in ComponentState) then
  14288. begin
  14289. ValidateCache;
  14290. UpdateScrollBars(True);
  14291. ScrollIntoView(FFocusedNode, toCenterScrollIntoView in FOptions.SelectionOptions, True);
  14292. Invalidate;
  14293. end;
  14294. end;
  14295. end;
  14296. //----------------------------------------------------------------------------------------------------------------------
  14297. procedure TBaseVirtualTree.SetDisabled(Node: PVirtualNode; Value: Boolean);
  14298. begin
  14299. if Assigned(Node) and (Value xor (vsDisabled in Node.States)) then
  14300. begin
  14301. if Value then
  14302. Include(Node.States, vsDisabled)
  14303. else
  14304. Exclude(Node.States, vsDisabled);
  14305. if FUpdateCount = 0 then
  14306. InvalidateNode(Node);
  14307. end;
  14308. end;
  14309. //----------------------------------------------------------------------------------------------------------------------
  14310. procedure TBaseVirtualTree.SetDoubleBuffered(const Value: Boolean);
  14311. begin
  14312. // empty by intention, we do our own buffering
  14313. end;
  14314. //----------------------------------------------------------------------------------------------------------------------
  14315. function TBaseVirtualTree.GetDoubleBuffered: Boolean;
  14316. begin
  14317. Result := True; // we do our own buffering
  14318. end;
  14319. //----------------------------------------------------------------------------------------------------------------------
  14320. procedure TBaseVirtualTree.SetEmptyListMessage(const Value: UnicodeString);
  14321. begin
  14322. if Value <> EmptyListMessage then
  14323. begin
  14324. FEmptyListMessage := Value;
  14325. Invalidate;
  14326. end;
  14327. end;
  14328. //----------------------------------------------------------------------------------------------------------------------
  14329. procedure TBaseVirtualTree.SetExpanded(Node: PVirtualNode; Value: Boolean);
  14330. begin
  14331. if Assigned(Node) and (Node <> FRoot) and (Value xor (vsExpanded in Node.States)) then
  14332. ToggleNode(Node);
  14333. end;
  14334. //----------------------------------------------------------------------------------------------------------------------
  14335. procedure TBaseVirtualTree.SetFocusedColumn(Value: TColumnIndex);
  14336. begin
  14337. if (FFocusedColumn <> Value) and
  14338. DoFocusChanging(FFocusedNode, FFocusedNode, FFocusedColumn, Value) then
  14339. begin
  14340. CancelEditNode;
  14341. InvalidateColumn(FFocusedColumn);
  14342. InvalidateColumn(Value);
  14343. FFocusedColumn := Value;
  14344. if Assigned(FFocusedNode) and not (toDisableAutoscrollOnFocus in FOptions.FAutoOptions) then
  14345. begin
  14346. if ScrollIntoView(FFocusedNode, toCenterScrollIntoView in FOptions.SelectionOptions, True) then
  14347. InvalidateNode(FFocusedNode);
  14348. end;
  14349. if Assigned(FDropTargetNode) then
  14350. InvalidateNode(FDropTargetNode);
  14351. DoFocusChange(FFocusedNode, FFocusedColumn);
  14352. end;
  14353. end;
  14354. //----------------------------------------------------------------------------------------------------------------------
  14355. procedure TBaseVirtualTree.SetFocusedNode(Value: PVirtualNode);
  14356. var
  14357. WasDifferent: Boolean;
  14358. begin
  14359. WasDifferent := Value <> FFocusedNode;
  14360. DoFocusNode(Value, True);
  14361. // Do change event only if there was actually a change.
  14362. if WasDifferent and (FFocusedNode = Value) then
  14363. DoFocusChange(FFocusedNode, FFocusedColumn);
  14364. end;
  14365. //----------------------------------------------------------------------------------------------------------------------
  14366. procedure TBaseVirtualTree.SetFullyVisible(Node: PVirtualNode; Value: Boolean);
  14367. // This method ensures that a node is visible and all its parent nodes are expanded and also visible
  14368. // if Value is True. Otherwise the visibility flag of the node is reset but the expand state
  14369. // of the parent nodes stays untouched.
  14370. begin
  14371. Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameter');
  14372. IsVisible[Node] := Value;
  14373. if Value then
  14374. begin
  14375. repeat
  14376. Node := Node.Parent;
  14377. if Node = FRoot then
  14378. Break;
  14379. if not (vsExpanded in Node.States) then
  14380. ToggleNode(Node);
  14381. if not (vsVisible in Node.States) then
  14382. IsVisible[Node] := True;
  14383. until False;
  14384. end;
  14385. end;
  14386. //----------------------------------------------------------------------------------------------------------------------
  14387. procedure TBaseVirtualTree.SetHasChildren(Node: PVirtualNode; Value: Boolean);
  14388. begin
  14389. if Assigned(Node) and not (toReadOnly in FOptions.FMiscOptions) then
  14390. begin
  14391. if Value then
  14392. Include(Node.States, vsHasChildren)
  14393. else
  14394. begin
  14395. Exclude(Node.States, vsHasChildren);
  14396. DeleteChildren(Node);
  14397. end;
  14398. end;
  14399. end;
  14400. //----------------------------------------------------------------------------------------------------------------------
  14401. procedure TBaseVirtualTree.SetHeader(const Value: TVTHeader);
  14402. begin
  14403. FHeader.Assign(Value);
  14404. end;
  14405. //----------------------------------------------------------------------------------------------------------------------
  14406. procedure TBaseVirtualTree.SetFiltered(Node: PVirtualNode; Value: Boolean);
  14407. // Sets the 'filtered' flag of the given node according to Value and updates all dependent states.
  14408. var
  14409. NeedUpdate: Boolean;
  14410. begin
  14411. Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameter.');
  14412. // Initialize the node if necessary as this might change the filtered state.
  14413. if not (vsInitialized in Node.States) then
  14414. InitNode(Node);
  14415. if Value <> (vsFiltered in Node.States) then
  14416. begin
  14417. InterruptValidation;
  14418. NeedUpdate := False;
  14419. if Value then
  14420. begin
  14421. Include(Node.States, vsFiltered);
  14422. if not (toShowFilteredNodes in FOptions.FPaintOptions) then
  14423. begin
  14424. AdjustTotalHeight(Node, -Integer(NodeHeight[Node]), True);
  14425. if FullyVisible[Node] then
  14426. begin
  14427. Dec(FVisibleCount);
  14428. NeedUpdate := True;
  14429. end;
  14430. end;
  14431. if FUpdateCount = 0 then
  14432. DetermineHiddenChildrenFlag(Node.Parent)
  14433. else
  14434. Include(FStates, tsUpdateHiddenChildrenNeeded);
  14435. end
  14436. else
  14437. begin
  14438. Exclude(Node.States, vsFiltered);
  14439. if not (toShowFilteredNodes in FOptions.FPaintOptions) then
  14440. begin
  14441. AdjustTotalHeight(Node, Integer(NodeHeight[Node]), True);
  14442. if FullyVisible[Node] then
  14443. begin
  14444. Inc(FVisibleCount);
  14445. NeedUpdate := True;
  14446. end;
  14447. end;
  14448. if vsVisible in Node.States then
  14449. // Update the hidden children flag of the parent.
  14450. // Since this node is now visible we simply have to remove the flag.
  14451. Exclude(Node.Parent.States, vsAllChildrenHidden);
  14452. end;
  14453. InvalidateCache;
  14454. if NeedUpdate and (FUpdateCount = 0) then
  14455. begin
  14456. ValidateCache;
  14457. UpdateScrollBars(True);
  14458. Invalidate;
  14459. end;
  14460. end;
  14461. end;
  14462. //----------------------------------------------------------------------------------------------------------------------
  14463. procedure TBaseVirtualTree.SetImages(const Value: TCustomImageList);
  14464. begin
  14465. if FImages <> Value then
  14466. begin
  14467. if Assigned(FImages) then
  14468. begin
  14469. FImages.UnRegisterChanges(FImageChangeLink);
  14470. FImages.RemoveFreeNotification(Self);
  14471. end;
  14472. FImages := Value;
  14473. if Assigned(FImages) then
  14474. begin
  14475. FImages.RegisterChanges(FImageChangeLink);
  14476. FImages.FreeNotification(Self);
  14477. end;
  14478. if not (csLoading in ComponentState) then
  14479. Invalidate;
  14480. end;
  14481. end;
  14482. //----------------------------------------------------------------------------------------------------------------------
  14483. procedure TBaseVirtualTree.SetIndent(Value: Cardinal);
  14484. begin
  14485. if FIndent <> Value then
  14486. begin
  14487. FIndent := Value;
  14488. if not (csLoading in ComponentState) and (FUpdateCount = 0) and HandleAllocated then
  14489. begin
  14490. UpdateScrollBars(True);
  14491. Invalidate;
  14492. end;
  14493. end;
  14494. end;
  14495. //----------------------------------------------------------------------------------------------------------------------
  14496. procedure TBaseVirtualTree.SetLineMode(const Value: TVTLineMode);
  14497. begin
  14498. if FLineMode <> Value then
  14499. begin
  14500. FLineMode := Value;
  14501. if HandleAllocated and not (csLoading in ComponentState) then
  14502. Invalidate;
  14503. end;
  14504. end;
  14505. //----------------------------------------------------------------------------------------------------------------------
  14506. procedure TBaseVirtualTree.SetLineStyle(const Value: TVTLineStyle);
  14507. begin
  14508. if FLineStyle <> Value then
  14509. begin
  14510. FLineStyle := Value;
  14511. if not (csLoading in ComponentState) then
  14512. begin
  14513. PrepareBitmaps(False, True);
  14514. if HandleAllocated then
  14515. Invalidate;
  14516. end;
  14517. end;
  14518. end;
  14519. //----------------------------------------------------------------------------------------------------------------------
  14520. procedure TBaseVirtualTree.SetMargin(Value: Integer);
  14521. begin
  14522. if FMargin <> Value then
  14523. begin
  14524. FMargin := Value;
  14525. if HandleAllocated and not (csLoading in ComponentState) then
  14526. Invalidate;
  14527. end;
  14528. end;
  14529. //----------------------------------------------------------------------------------------------------------------------
  14530. procedure TBaseVirtualTree.SetMultiline(Node: PVirtualNode; const Value: Boolean);
  14531. begin
  14532. if Assigned(Node) and (Node <> FRoot) then
  14533. if Value <> (vsMultiline in Node.States) then
  14534. begin
  14535. if Value then
  14536. Include(Node.States, vsMultiline)
  14537. else
  14538. Exclude(Node.States, vsMultiline);
  14539. if FUpdateCount = 0 then
  14540. InvalidateNode(Node);
  14541. end;
  14542. end;
  14543. //----------------------------------------------------------------------------------------------------------------------
  14544. procedure TBaseVirtualTree.SetNodeAlignment(const Value: TVTNodeAlignment);
  14545. begin
  14546. if FNodeAlignment <> Value then
  14547. begin
  14548. FNodeAlignment := Value;
  14549. if HandleAllocated and not (csReading in ComponentState) then
  14550. Invalidate;
  14551. end;
  14552. end;
  14553. //----------------------------------------------------------------------------------------------------------------------
  14554. procedure TBaseVirtualTree.SetNodeDataSize(Value: Integer);
  14555. var
  14556. LastRootCount: Cardinal;
  14557. begin
  14558. if Value < -1 then
  14559. Value := -1;
  14560. if FNodeDataSize <> Value then
  14561. begin
  14562. FNodeDataSize := Value;
  14563. if not (csLoading in ComponentState) and not (csDesigning in ComponentState) then
  14564. begin
  14565. LastRootCount := FRoot.ChildCount;
  14566. Clear;
  14567. SetRootNodeCount(LastRootCount);
  14568. end;
  14569. end;
  14570. end;
  14571. //----------------------------------------------------------------------------------------------------------------------
  14572. procedure TBaseVirtualTree.SetNodeHeight(Node: PVirtualNode; Value: Cardinal);
  14573. var
  14574. Difference: Integer;
  14575. begin
  14576. if Assigned(Node) and (Node <> FRoot) and (Node.NodeHeight <> Value) and not (toReadOnly in FOptions.FMiscOptions) then
  14577. begin
  14578. Difference := Integer(Value) - Integer(Node.NodeHeight);
  14579. Node.NodeHeight := Value;
  14580. // If the node is effectively filtered out, nothing else has to be done, as it is not visible anyway.
  14581. if not IsEffectivelyFiltered[Node] then
  14582. begin
  14583. AdjustTotalHeight(Node, Difference, True);
  14584. // If an edit operation is currently active then update the editors boundaries as well.
  14585. UpdateEditBounds;
  14586. InvalidateCache;
  14587. // Stay away from touching the node cache while it is being validated.
  14588. if not (tsValidating in FStates) and FullyVisible[Node] and not IsEffectivelyFiltered[Node] then
  14589. begin
  14590. if (FUpdateCount = 0) and ([tsPainting, tsSizing] * FStates = []) then
  14591. begin
  14592. ValidateCache;
  14593. InvalidateToBottom(Node);
  14594. UpdateScrollBars(True);
  14595. end;
  14596. end;
  14597. end;
  14598. end;
  14599. end;
  14600. //----------------------------------------------------------------------------------------------------------------------
  14601. procedure TBaseVirtualTree.SetNodeParent(Node: PVirtualNode; const Value: PVirtualNode);
  14602. begin
  14603. if Assigned(Node) and Assigned(Value) and (Node.Parent <> Value) then
  14604. MoveTo(Node, Value, amAddChildLast, False);
  14605. end;
  14606. //----------------------------------------------------------------------------------------------------------------------
  14607. procedure TBaseVirtualTree.SetOffsetX(const Value: Integer);
  14608. begin
  14609. DoSetOffsetXY(Point(Value, FOffsetY), DefaultScrollUpdateFlags);
  14610. end;
  14611. //----------------------------------------------------------------------------------------------------------------------
  14612. procedure TBaseVirtualTree.SetOffsetXY(const Value: TPoint);
  14613. begin
  14614. DoSetOffsetXY(Value, DefaultScrollUpdateFlags);
  14615. end;
  14616. //----------------------------------------------------------------------------------------------------------------------
  14617. procedure TBaseVirtualTree.SetOffsetY(const Value: Integer);
  14618. begin
  14619. DoSetOffsetXY(Point(FOffsetX, Value), DefaultScrollUpdateFlags);
  14620. end;
  14621. //----------------------------------------------------------------------------------------------------------------------
  14622. procedure TBaseVirtualTree.SetOptions(const Value: TCustomVirtualTreeOptions);
  14623. begin
  14624. FOptions.Assign(Value);
  14625. end;
  14626. //----------------------------------------------------------------------------------------------------------------------
  14627. procedure TBaseVirtualTree.SetRootNodeCount(Value: Cardinal);
  14628. begin
  14629. // Don't set the root node count until all other properties (in particular the OnInitNode event) have been set.
  14630. if csLoading in ComponentState then
  14631. begin
  14632. FRoot.ChildCount := Value;
  14633. DoStateChange([tsNeedRootCountUpdate]);
  14634. end
  14635. else
  14636. if FRoot.ChildCount <> Value then
  14637. begin
  14638. BeginUpdate;
  14639. InterruptValidation;
  14640. SetChildCount(FRoot, Value);
  14641. EndUpdate;
  14642. end;
  14643. end;
  14644. //----------------------------------------------------------------------------------------------------------------------
  14645. procedure TBaseVirtualTree.SetScrollBarOptions(Value: TScrollBarOptions);
  14646. begin
  14647. FScrollBarOptions.Assign(Value);
  14648. end;
  14649. //----------------------------------------------------------------------------------------------------------------------
  14650. procedure TBaseVirtualTree.SetSearchOption(const Value: TVTIncrementalSearch);
  14651. begin
  14652. if FIncrementalSearch <> Value then
  14653. begin
  14654. FIncrementalSearch := Value;
  14655. if FIncrementalSearch = isNone then
  14656. begin
  14657. StopTimer(SearchTimer);
  14658. FSearchBuffer := '';
  14659. FLastSearchNode := nil;
  14660. end;
  14661. end;
  14662. end;
  14663. //----------------------------------------------------------------------------------------------------------------------
  14664. procedure TBaseVirtualTree.SetSelected(Node: PVirtualNode; Value: Boolean);
  14665. begin
  14666. if not FSelectionLocked and Assigned(Node) and (Node <> FRoot) and (Value xor (vsSelected in Node.States)) then
  14667. begin
  14668. if Value then
  14669. begin
  14670. if FSelectionCount = 0 then
  14671. FRangeAnchor := Node
  14672. else
  14673. if not (toMultiSelect in FOptions.FSelectionOptions) then
  14674. ClearSelection;
  14675. AddToSelection(Node);
  14676. // Make sure there is a valid column selected (if there are columns at all).
  14677. if ((FFocusedColumn < 0) or not (coVisible in FHeader.Columns[FFocusedColumn].Options)) and
  14678. (FHeader.MainColumn > NoColumn) then
  14679. if ([coVisible, coAllowFocus] * FHeader.Columns[FHeader.MainColumn].Options = [coVisible, coAllowFocus]) then
  14680. FFocusedColumn := FHeader.MainColumn
  14681. else
  14682. FFocusedColumn := FHeader.Columns.GetFirstVisibleColumn(True);
  14683. if FRangeAnchor = nil then
  14684. FRangeAnchor := Node;
  14685. end
  14686. else
  14687. begin
  14688. RemoveFromSelection(Node);
  14689. if FSelectionCount = 0 then
  14690. ResetRangeAnchor;
  14691. end;
  14692. if FullyVisible[Node] and not IsEffectivelyFiltered[Node] then
  14693. InvalidateNode(Node);
  14694. end;
  14695. end;
  14696. //----------------------------------------------------------------------------------------------------------------------
  14697. procedure TBaseVirtualTree.SetSelectionCurveRadius(const Value: Cardinal);
  14698. begin
  14699. if FSelectionCurveRadius <> Value then
  14700. begin
  14701. FSelectionCurveRadius := Value;
  14702. if HandleAllocated and not (csLoading in ComponentState) then
  14703. Invalidate;
  14704. end;
  14705. end;
  14706. //----------------------------------------------------------------------------------------------------------------------
  14707. procedure TBaseVirtualTree.SetStateImages(const Value: TCustomImageList);
  14708. begin
  14709. if FStateImages <> Value then
  14710. begin
  14711. if Assigned(FStateImages) then
  14712. begin
  14713. FStateImages.UnRegisterChanges(FStateChangeLink);
  14714. FStateImages.RemoveFreeNotification(Self);
  14715. end;
  14716. FStateImages := Value;
  14717. if Assigned(FStateImages) then
  14718. begin
  14719. FStateImages.RegisterChanges(FStateChangeLink);
  14720. FStateImages.FreeNotification(Self);
  14721. end;
  14722. if HandleAllocated and not (csLoading in ComponentState) then
  14723. Invalidate;
  14724. end;
  14725. end;
  14726. //----------------------------------------------------------------------------------------------------------------------
  14727. procedure TBaseVirtualTree.SetTextMargin(Value: Integer);
  14728. begin
  14729. if FTextMargin <> Value then
  14730. begin
  14731. FTextMargin := Value;
  14732. if not (csLoading in ComponentState) then
  14733. Invalidate;
  14734. end;
  14735. end;
  14736. //----------------------------------------------------------------------------------------------------------------------
  14737. procedure TBaseVirtualTree.SetTopNode(Node: PVirtualNode);
  14738. var
  14739. R: TRect;
  14740. Run: PVirtualNode;
  14741. begin
  14742. if Assigned(Node) then
  14743. begin
  14744. // make sure all parents of the node are expanded
  14745. Run := Node.Parent;
  14746. while Run <> FRoot do
  14747. begin
  14748. if not (vsExpanded in Run.States) then
  14749. ToggleNode(Run);
  14750. Run := Run.Parent;
  14751. end;
  14752. R := GetDisplayRect(Node, FHeader.MainColumn, True);
  14753. SetOffsetY(FOffsetY - R.Top);
  14754. end;
  14755. end;
  14756. //----------------------------------------------------------------------------------------------------------------------
  14757. procedure TBaseVirtualTree.SetUpdateState(Updating: Boolean);
  14758. begin
  14759. // The check for visibility is necessary otherwise the tree is automatically shown when
  14760. // updating is allowed. As this happens internally the VCL does not get notified and
  14761. // still assumes the control is hidden. This results in weird "cannot focus invisible control" errors.
  14762. if Visible and HandleAllocated and (FUpdateCount = 0) then
  14763. SendMessage(Handle, WM_SETREDRAW, Ord(not Updating), 0);
  14764. end;
  14765. //----------------------------------------------------------------------------------------------------------------------
  14766. procedure TBaseVirtualTree.SetVerticalAlignment(Node: PVirtualNode; Value: Byte);
  14767. begin
  14768. if Value > 100 then
  14769. Value := 100;
  14770. if Node.Align <> Value then
  14771. begin
  14772. Node.Align := Value;
  14773. if FullyVisible[Node] and not IsEffectivelyFiltered[Node] then
  14774. InvalidateNode(Node);
  14775. end;
  14776. end;
  14777. //----------------------------------------------------------------------------------------------------------------------
  14778. procedure TBaseVirtualTree.SetVisible(Node: PVirtualNode; Value: Boolean);
  14779. // Sets the visibility style of the given node according to Value.
  14780. var
  14781. NeedUpdate: Boolean;
  14782. begin
  14783. Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameter.');
  14784. if Value <> (vsVisible in Node.States) then
  14785. begin
  14786. InterruptValidation;
  14787. NeedUpdate := False;
  14788. if Value then
  14789. begin
  14790. Include(Node.States, vsVisible);
  14791. if vsExpanded in Node.Parent.States then
  14792. AdjustTotalHeight(Node.Parent, Node.TotalHeight, True);
  14793. if VisiblePath[Node] then
  14794. begin
  14795. Inc(FVisibleCount, CountVisibleChildren(Node) + Cardinal(IfThen(IsEffectivelyVisible[Node], 1)));
  14796. NeedUpdate := True;
  14797. end;
  14798. // Update the hidden children flag of the parent.
  14799. // Since this node is now visible we simply have to remove the flag.
  14800. if not IsEffectivelyFiltered[Node] then
  14801. Exclude(Node.Parent.States, vsAllChildrenHidden);
  14802. end
  14803. else
  14804. begin
  14805. if vsExpanded in Node.Parent.States then
  14806. AdjustTotalHeight(Node.Parent, -Integer(Node.TotalHeight), True);
  14807. if VisiblePath[Node] then
  14808. begin
  14809. Dec(FVisibleCount, CountVisibleChildren(Node) + Cardinal(IfThen(IsEffectivelyVisible[Node], 1)));
  14810. NeedUpdate := True;
  14811. end;
  14812. Exclude(Node.States, vsVisible);
  14813. if FUpdateCount = 0 then
  14814. DetermineHiddenChildrenFlag(Node.Parent)
  14815. else
  14816. Include(FStates, tsUpdateHiddenChildrenNeeded);
  14817. end;
  14818. InvalidateCache;
  14819. if NeedUpdate and (FUpdateCount = 0) then
  14820. begin
  14821. ValidateCache;
  14822. UpdateScrollBars(True);
  14823. Invalidate;
  14824. end;
  14825. end;
  14826. end;
  14827. //----------------------------------------------------------------------------------------------------------------------
  14828. procedure TBaseVirtualTree.SetVisiblePath(Node: PVirtualNode; Value: Boolean);
  14829. // If Value is True then all parent nodes of Node are expanded.
  14830. begin
  14831. Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameter.');
  14832. if Value then
  14833. begin
  14834. repeat
  14835. Node := Node.Parent;
  14836. if Node = FRoot then
  14837. Break;
  14838. if not (vsExpanded in Node.States) then
  14839. ToggleNode(Node);
  14840. until False;
  14841. end;
  14842. end;
  14843. //----------------------------------------------------------------------------------------------------------------------
  14844. procedure TBaseVirtualTree.StaticBackground(Source: TBitmap; Target: TCanvas; OffsetPosition: TPoint; R: TRect);
  14845. // Draws the given source graphic so that it stays static in the given rectangle which is relative to the target bitmap.
  14846. // The graphic is aligned so that it always starts at the upper left corner of the target canvas.
  14847. // Offset gives the position of the target window as a possible superordinated surface.
  14848. const
  14849. DST = $00AA0029; // Ternary Raster Operation - Destination unchanged
  14850. var
  14851. PicRect: TRect;
  14852. AreaRect: TRect;
  14853. DrawRect: TRect;
  14854. begin
  14855. // clear background
  14856. Target.Brush.Color := Color;
  14857. Target.FillRect(R);
  14858. // Picture rect in relation to client viewscreen.
  14859. PicRect := Rect(FBackgroundOffsetX, FBackgroundOffsetY, FBackgroundOffsetX + Source.Width, FBackgroundOffsetY + Source.Height);
  14860. // Area to be draw in relation to client viewscreen.
  14861. AreaRect := Rect(OffsetPosition.X + R.Left, OffsetPosition.Y + R.Top, OffsetPosition.X + R.Right, OffsetPosition.Y + R.Bottom);
  14862. // If picture falls in AreaRect, return intersection (DrawRect).
  14863. if IntersectRect(DrawRect, PicRect, AreaRect) then
  14864. begin
  14865. // Draw portion of image which falls in canvas area.
  14866. if Source.Transparent then
  14867. begin
  14868. // Leave transparent area as destination unchanged (DST), copy non-transparent areas to canvas (SRCCOPY).
  14869. MaskBlt(Target.Handle, DrawRect.Left - OffsetPosition.X, DrawRect.Top - OffsetPosition.Y, (DrawRect.Right - OffsetPosition.X) - (DrawRect.Left - OffsetPosition.X),
  14870. (DrawRect.Bottom - OffsetPosition.Y) - (DrawRect.Top - OffsetPosition.Y), Source.Canvas.Handle, DrawRect.Left - PicRect.Left, DrawRect.Top - PicRect.Top,
  14871. Source.MaskHandle, DrawRect.Left - PicRect.Left, DrawRect.Top - PicRect.Top, MakeROP4(DST, SRCCOPY));
  14872. end
  14873. else
  14874. begin
  14875. // copy image to destination
  14876. BitBlt(Target.Handle, DrawRect.Left - OffsetPosition.X, DrawRect.Top - OffsetPosition.Y, (DrawRect.Right - OffsetPosition.X) - (DrawRect.Left - OffsetPosition.X),
  14877. (DrawRect.Bottom - OffsetPosition.Y) - (DrawRect.Top - OffsetPosition.Y) + R.Top, Source.Canvas.Handle, DrawRect.Left - PicRect.Left, DrawRect.Top - PicRect.Top,
  14878. SRCCOPY);
  14879. end;
  14880. end;
  14881. end;
  14882. //----------------------------------------------------------------------------------------------------------------------
  14883. procedure TBaseVirtualTree.StopTimer(ID: Integer);
  14884. begin
  14885. if HandleAllocated then
  14886. KillTimer(Handle, ID);
  14887. end;
  14888. //----------------------------------------------------------------------------------------------------------------------
  14889. procedure TBaseVirtualTree.SetWindowTheme(Theme: UnicodeString);
  14890. begin
  14891. FChangingTheme := True;
  14892. UxTheme.SetWindowTheme(Handle, PWideChar(Theme), nil);
  14893. end;
  14894. //----------------------------------------------------------------------------------------------------------------------
  14895. procedure TBaseVirtualTree.TileBackground(Source: TBitmap; Target: TCanvas; Offset: TPoint; R: TRect);
  14896. // Draws the given source graphic so that it tiles into the given rectangle which is relative to the target bitmap.
  14897. // The graphic is aligned so that it always starts at the upper left corner of the target canvas.
  14898. // Offset gives the position of the target window in an possible superordinated surface.
  14899. var
  14900. SourceX,
  14901. SourceY,
  14902. TargetX,
  14903. DeltaY: Integer;
  14904. begin
  14905. with Target do
  14906. begin
  14907. SourceY := (R.Top + Offset.Y + FBackgroundOffsetY) mod Source.Height;
  14908. // Always wrap the source coordinates into positive range.
  14909. if SourceY < 0 then
  14910. SourceY := Source.Height + SourceY;
  14911. // Tile image vertically until target rect is filled.
  14912. while R.Top < R.Bottom do
  14913. begin
  14914. SourceX := (R.Left + Offset.X + FBackgroundOffsetX) mod Source.Width;
  14915. // always wrap the source coordinates into positive range
  14916. if SourceX < 0 then
  14917. SourceX := Source.Width + SourceX;
  14918. TargetX := R.Left;
  14919. // height of strip to draw
  14920. DeltaY := Min(R.Bottom - R.Top, Source.Height - SourceY);
  14921. // tile the image horizontally
  14922. while TargetX < R.Right do
  14923. begin
  14924. BitBlt(Handle, TargetX, R.Top, Min(R.Right - TargetX, Source.Width - SourceX), DeltaY,
  14925. Source.Canvas.Handle, SourceX, SourceY, SRCCOPY);
  14926. Inc(TargetX, Source.Width - SourceX);
  14927. SourceX := 0;
  14928. end;
  14929. Inc(R.Top, Source.Height - SourceY);
  14930. SourceY := 0;
  14931. end;
  14932. end;
  14933. end;
  14934. //----------------------------------------------------------------------------------------------------------------------
  14935. function TBaseVirtualTree.ToggleCallback(Step, StepSize: Integer; Data: Pointer): Boolean;
  14936. var
  14937. Column: TColumnIndex;
  14938. Run: TRect;
  14939. SecondaryStepSize: Integer;
  14940. //--------------- local functions -------------------------------------------
  14941. procedure EraseLine;
  14942. var
  14943. LocalBrush: HBRUSH;
  14944. begin
  14945. with TToggleAnimationData(Data^), FHeader.FColumns do
  14946. begin
  14947. // Iterate through all columns and erase background in their local color.
  14948. // LocalBrush is a brush in the color of the particular column.
  14949. Column := GetFirstVisibleColumn;
  14950. while (Column > InvalidColumn) and (Run.Left < ClientWidth) do
  14951. begin
  14952. GetColumnBounds(Column, Run.Left, Run.Right);
  14953. if coParentColor in Items[Column].FOptions then
  14954. FillRect(DC, Run, Brush)
  14955. else
  14956. begin
  14957. if VclStyleEnabled then
  14958. LocalBrush := CreateSolidBrush(ColorToRGB(FColors.BackGroundColor))
  14959. else
  14960. LocalBrush := CreateSolidBrush(ColorToRGB(Items[Column].Color));
  14961. FillRect(DC, Run, LocalBrush);
  14962. DeleteObject(LocalBrush);
  14963. end;
  14964. Column := GetNextVisibleColumn(Column);
  14965. end;
  14966. end;
  14967. end;
  14968. //---------------------------------------------------------------------------
  14969. procedure DoScrollUp(DC: HDC; Brush: HBRUSH; Area: TRect; Steps: Integer);
  14970. begin
  14971. ScrollDC(DC, 0, -Steps, Area, Area, 0, nil);
  14972. if Step = 0 then
  14973. if not FHeader.UseColumns then
  14974. FillRect(DC, Rect(Area.Left, Area.Bottom - Steps - 1, Area.Right, Area.Bottom), Brush)
  14975. else
  14976. begin
  14977. Run := Rect(Area.Left, Area.Bottom - Steps - 1, Area.Right, Area.Bottom);
  14978. EraseLine;
  14979. end;
  14980. end;
  14981. //---------------------------------------------------------------------------
  14982. procedure DoScrollDown(DC: HDC; Brush: HBRUSH; Area: TRect; Steps: Integer);
  14983. begin
  14984. ScrollDC(DC, 0, Steps, Area, Area, 0, nil);
  14985. if Step = 0 then
  14986. if not FHeader.UseColumns then
  14987. FillRect(DC, Rect(Area.Left, Area.Top, Area.Right, Area.Top + Steps + 1), Brush)
  14988. else
  14989. begin
  14990. Run := Rect(Area.Left, Area.Top, Area.Right, Area.Top + Steps + 1);
  14991. EraseLine;
  14992. end;
  14993. end;
  14994. //--------------- end local functions ---------------------------------------
  14995. begin
  14996. Result := True;
  14997. if StepSize > 0 then
  14998. begin
  14999. SecondaryStepSize := 0;
  15000. with TToggleAnimationData(Data^) do
  15001. begin
  15002. if Mode1 <> tamNoScroll then
  15003. begin
  15004. if Mode1 = tamScrollUp then
  15005. DoScrollUp(DC, Brush, R1, StepSize)
  15006. else
  15007. DoScrollDown(DC, Brush, R1, StepSize);
  15008. if (Mode2 <> tamNoScroll) and (ScaleFactor > 0) then
  15009. begin
  15010. // As this routine is able to scroll two independent areas at once, the missing StepSize is
  15011. // computed in that case. To ensure the maximal accuracy the rounding error is accumulated.
  15012. SecondaryStepSize := Round((StepSize + MissedSteps) * ScaleFactor);
  15013. MissedSteps := MissedSteps + StepSize * ScaleFactor - SecondaryStepSize;
  15014. end;
  15015. end
  15016. else
  15017. SecondaryStepSize := StepSize;
  15018. if Mode2 <> tamNoScroll then
  15019. if Mode2 = tamScrollUp then
  15020. DoScrollUp(DC, Brush, R2, SecondaryStepSize)
  15021. else
  15022. DoScrollDown(DC, Brush, R2, SecondaryStepSize);
  15023. end;
  15024. end;
  15025. end;
  15026. //----------------------------------------------------------------------------------------------------------------------
  15027. procedure TBaseVirtualTree.CMColorChange(var Message: TMessage);
  15028. begin
  15029. if not (csLoading in ComponentState) then
  15030. begin
  15031. PrepareBitmaps(True, False);
  15032. if HandleAllocated then
  15033. Invalidate;
  15034. end;
  15035. end;
  15036. //----------------------------------------------------------------------------------------------------------------------
  15037. procedure TBaseVirtualTree.CMCtl3DChanged(var Message: TMessage);
  15038. begin
  15039. inherited;
  15040. if FBorderStyle = bsSingle then
  15041. RecreateWnd;
  15042. end;
  15043. //----------------------------------------------------------------------------------------------------------------------
  15044. procedure TBaseVirtualTree.CMBiDiModeChanged(var Message: TMessage);
  15045. begin
  15046. inherited;
  15047. if UseRightToLeftAlignment then
  15048. FEffectiveOffsetX := Integer(FRangeX) - ClientWidth + FOffsetX
  15049. else
  15050. FEffectiveOffsetX := -FOffsetX;
  15051. if FEffectiveOffsetX < 0 then
  15052. FEffectiveOffsetX := 0;
  15053. if toAutoBidiColumnOrdering in FOptions.FAutoOptions then
  15054. FHeader.FColumns.ReorderColumns(UseRightToLeftAlignment);
  15055. FHeader.Invalidate(nil);
  15056. end;
  15057. {$if CompilerVersion >= 23 }
  15058. procedure TBaseVirtualTree.CMBorderChanged(var Message: TMessage);
  15059. begin
  15060. inherited;
  15061. // For XE2+ themes
  15062. if not FSetOrRestoreBevelKindAndBevelWidth then
  15063. begin
  15064. FSavedBevelKind := BevelKind;
  15065. FSavedBorderWidth := BorderWidth;
  15066. end;
  15067. end;
  15068. procedure TBaseVirtualTree.CMStyleChanged(var Message: TMessage);
  15069. begin
  15070. VclStyleChanged;
  15071. RecreateWnd;
  15072. end;
  15073. procedure TBaseVirtualTree.CMParentDoubleBufferedChange(var Message: TMessage);
  15074. begin
  15075. // empty by intention, we do our own buffering
  15076. end;
  15077. {$ifend}
  15078. //----------------------------------------------------------------------------------------------------------------------
  15079. procedure TBaseVirtualTree.CMDenySubclassing(var Message: TMessage);
  15080. // If a Windows XP Theme Manager component is used in the application it will try to subclass all controls which do not
  15081. // explicitly deny this. Virtual Treeview knows how to handle XP themes so it does not need subclassing.
  15082. begin
  15083. Message.Result := 1;
  15084. end;
  15085. //----------------------------------------------------------------------------------------------------------------------
  15086. procedure TBaseVirtualTree.CMDrag(var Message: TCMDrag);
  15087. var
  15088. S: TObject;
  15089. ShiftState: Integer;
  15090. P: TPoint;
  15091. Formats: TFormatArray;
  15092. Effect: Integer;
  15093. begin
  15094. with Message, DragRec^ do
  15095. begin
  15096. S := Source;
  15097. Formats := nil;
  15098. // Let the ancestor handle dock operations.
  15099. if S is TDragDockObject then
  15100. inherited
  15101. else
  15102. begin
  15103. // We need an extra check for the control drag object as there might be other objects not derived from
  15104. // this class (e.g. TActionDragObject).
  15105. if not (tsUserDragObject in FStates) and (S is TBaseDragControlObject) then
  15106. S := (S as TBaseDragControlObject).Control;
  15107. case DragMessage of
  15108. dmDragEnter, dmDragLeave, dmDragMove:
  15109. begin
  15110. if DragMessage = dmDragEnter then
  15111. DoStateChange([tsVCLDragging]);
  15112. if DragMessage = dmDragLeave then
  15113. DoStateChange([tsVCLDragFinished], [tsVCLDragging]);
  15114. if DragMessage = dmDragMove then
  15115. with ScreenToClient(Pos) do
  15116. DoAutoScroll(X, Y);
  15117. ShiftState := 0;
  15118. // Alt key will be queried by the KeysToShiftState function in DragOver.
  15119. if GetKeyState(VK_SHIFT) < 0 then
  15120. ShiftState := ShiftState or MK_SHIFT;
  15121. if GetKeyState(VK_CONTROL) < 0 then
  15122. ShiftState := ShiftState or MK_CONTROL;
  15123. // Allowed drop effects are simulated for VCL dd.
  15124. Effect := DROPEFFECT_MOVE or DROPEFFECT_COPY;
  15125. DragOver(S, ShiftState, TDragState(DragMessage), Pos, Effect);
  15126. FLastVCLDragTarget := FDropTargetNode;
  15127. FVCLDragEffect := Effect;
  15128. if (DragMessage = dmDragLeave) and Assigned(FDropTargetNode) then
  15129. begin
  15130. InvalidateNode(FDropTargetNode);
  15131. FDropTargetNode := nil;
  15132. end;
  15133. Result := LRESULT(Effect);
  15134. end;
  15135. dmDragDrop:
  15136. begin
  15137. ShiftState := 0;
  15138. // Alt key will be queried by the KeysToShiftState function in DragOver
  15139. if GetKeyState(VK_SHIFT) < 0 then
  15140. ShiftState := ShiftState or MK_SHIFT;
  15141. if GetKeyState(VK_CONTROL) < 0 then
  15142. ShiftState := ShiftState or MK_CONTROL;
  15143. // allowed drop effects are simulated for VCL dd,
  15144. // replace target node with cached node from other VCL dd messages
  15145. if Assigned(FDropTargetNode) then
  15146. InvalidateNode(FDropTargetNode);
  15147. FDropTargetNode := FLastVCLDragTarget;
  15148. P := Point(Pos.X, Pos.Y);
  15149. P := ScreenToClient(P);
  15150. try
  15151. DoDragDrop(S, nil, Formats, KeysToShiftState(ShiftState), P, FVCLDragEffect, FLastDropMode);
  15152. finally
  15153. if Assigned(FDropTargetNode) then
  15154. begin
  15155. InvalidateNode(FDropTargetNode);
  15156. FDropTargetNode := nil;
  15157. end;
  15158. end;
  15159. end;
  15160. dmFindTarget:
  15161. begin
  15162. Result := LRESULT(ControlAtPos(ScreenToClient(Pos), False));
  15163. if Result = 0 then
  15164. Result := LRESULT(Self);
  15165. // This is a reliable place to check whether VCL drag has
  15166. // really begun.
  15167. if tsVCLDragPending in FStates then
  15168. DoStateChange([tsVCLDragging], [tsVCLDragPending, tsEditPending, tsClearPending]);
  15169. end;
  15170. end;
  15171. end;
  15172. end;
  15173. end;
  15174. //----------------------------------------------------------------------------------------------------------------------
  15175. procedure TBaseVirtualTree.CMEnabledChanged(var Message: TMessage);
  15176. begin
  15177. inherited;
  15178. // Need to invalidate the non-client area as well, since the header must be redrawn too.
  15179. if csDesigning in ComponentState then
  15180. RedrawWindow(Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE or RDW_NOERASE or RDW_NOCHILDREN);
  15181. end;
  15182. //----------------------------------------------------------------------------------------------------------------------
  15183. procedure TBaseVirtualTree.CMFontChanged(var Message: TMessage);
  15184. var
  15185. HeaderMessage: TMessage;
  15186. begin
  15187. inherited;
  15188. if not (csLoading in ComponentState) then
  15189. begin
  15190. AutoScale();
  15191. PrepareBitmaps(True, False);
  15192. if HandleAllocated then
  15193. Invalidate;
  15194. end;
  15195. HeaderMessage.Msg := CM_PARENTFONTCHANGED;
  15196. HeaderMessage.WParam := 0;
  15197. HeaderMessage.LParam := 0;
  15198. HeaderMessage.Result := 0;
  15199. FHeader.HandleMessage(HeaderMessage);
  15200. end;
  15201. //----------------------------------------------------------------------------------------------------------------------
  15202. procedure TBaseVirtualTree.CMHintShow(var Message: TCMHintShow);
  15203. // Determines hint message (tooltip) and out-of-hint rect.
  15204. // Note: A special handling is needed here because we cannot pass wide strings back to the caller.
  15205. // I had to introduce the hint data record anyway so we can use this to pass the hint string.
  15206. // We still need to set a dummy hint string in the message to make the VCL showing the hint window.
  15207. var
  15208. NodeRect: TRect;
  15209. SpanColumn,
  15210. Dummy,
  15211. ColLeft,
  15212. ColRight: Integer;
  15213. HitInfo: THitInfo;
  15214. ShowOwnHint: Boolean;
  15215. IsFocusedOrEditing: Boolean;
  15216. ParentForm: TCustomForm;
  15217. BottomRightCellContentMargin: TPoint;
  15218. DummyLineBreakStyle: TVTTooltipLineBreakStyle;
  15219. HintKind: TVTHintKind;
  15220. begin
  15221. with Message do
  15222. begin
  15223. Result := 1;
  15224. if PtInRect(FLastHintRect, HintInfo.CursorPos) then
  15225. Exit;
  15226. // Determine node for which to show hint/tooltip.
  15227. with HintInfo^ do
  15228. GetHitTestInfoAt(CursorPos.X, CursorPos.Y, True, HitInfo);
  15229. // Make sure a hint is only shown if the tree or at least its parent form is active.
  15230. // Active editing is ok too as long as we don't want the hint for the current edit node.
  15231. if IsEditing then
  15232. IsFocusedOrEditing := HitInfo.HitNode <> FFocusedNode
  15233. else
  15234. begin
  15235. IsFocusedOrEditing := Focused;
  15236. ParentForm := GetParentForm(Self);
  15237. if Assigned(ParentForm) then
  15238. IsFocusedOrEditing := ParentForm.Focused or Application.Active;
  15239. end;
  15240. if (GetCapture = 0) and ShowHint and not (Dragging or IsMouseSelecting) and ([tsScrolling] * FStates = []) and
  15241. (FHeader.States = []) and IsFocusedOrEditing then
  15242. begin
  15243. with HintInfo^ do
  15244. begin
  15245. Result := 0;
  15246. ShowOwnHint := False;
  15247. // Assign a dummy string otherwise the VCL will not show the hint window.
  15248. if GetHintWindowClass.InheritsFrom(TVirtualTreeHintWindow) then
  15249. HintStr := ' '
  15250. else
  15251. begin
  15252. //workaround for issue #291
  15253. //it duplicates parts of the following code and code in TVirtualTreeHintWindow
  15254. HintStr := '';
  15255. if FHeader.UseColumns and (hoShowHint in FHeader.FOptions) and FHeader.InHeader(CursorPos) then
  15256. begin
  15257. CursorRect := FHeaderRect;
  15258. // Convert the cursor rectangle into real client coordinates.
  15259. OffsetRect(CursorRect, 0, -Integer(FHeader.FHeight));
  15260. HitInfo.HitColumn := FHeader.FColumns.GetColumnAndBounds(CursorPos, CursorRect.Left, CursorRect.Right);
  15261. if (HitInfo.HitColumn > -1) and not (csLButtonDown in ControlState) and
  15262. (FHeader.FColumns[HitInfo.HitColumn].FHint <> '') then
  15263. HintStr := FHeader.FColumns[HitInfo.HitColumn].FHint;
  15264. end
  15265. else
  15266. if HintMode = hmDefault then
  15267. HintStr := GetShortHint(Hint)
  15268. else
  15269. if Assigned(HitInfo.HitNode) and (HitInfo.HitColumn > InvalidColumn) then
  15270. begin
  15271. if HintMode = hmToolTip then
  15272. HintStr := DoGetNodeToolTip(HitInfo.HitNode, HitInfo.HitColumn, DummyLineBreakStyle)
  15273. else
  15274. HintStr := DoGetNodeHint(HitInfo.HitNode, HitInfo.HitColumn, DummyLineBreakStyle);
  15275. end;
  15276. end;
  15277. // First check whether there is a header hint to show.
  15278. if FHeader.UseColumns and (hoShowHint in FHeader.FOptions) and FHeader.InHeader(CursorPos) then
  15279. begin
  15280. CursorRect := FHeaderRect;
  15281. // Convert the cursor rectangle into real client coordinates.
  15282. OffsetRect(CursorRect, 0, -Integer(FHeader.FHeight));
  15283. HitInfo.HitColumn := FHeader.FColumns.GetColumnAndBounds(CursorPos, CursorRect.Left, CursorRect.Right);
  15284. // align the vertical hint position on the bottom bound of the header, but
  15285. // avoid overlapping of mouse cursor and hint
  15286. HintPos.Y := Max(HintPos.Y, ClientToScreen(Point(0, CursorRect.Bottom)).Y);
  15287. // Note: the test for the left mouse button in ControlState might cause problems whenever the VCL does not
  15288. // realize when the button is released. This, for instance, happens when doing OLE drag'n drop and
  15289. // cancel this with ESC.
  15290. if (HitInfo.HitColumn > -1) and not (csLButtonDown in ControlState) then
  15291. begin
  15292. FHintData.DefaultHint := FHeader.FColumns[HitInfo.HitColumn].FHint;
  15293. if FHintData.DefaultHint <> '' then
  15294. ShowOwnHint := True
  15295. else
  15296. Result := 1;
  15297. end
  15298. else
  15299. Result := 1;
  15300. end
  15301. else
  15302. begin
  15303. // Default mode is handled as would the tree be a usual VCL control (no own hint window necessary).
  15304. if FHintMode = hmDefault then
  15305. HintStr := GetShortHint(Hint)
  15306. else
  15307. begin
  15308. if Assigned(HitInfo.HitNode) and (HitInfo.HitColumn > InvalidColumn) then
  15309. begin
  15310. // An owner-draw tree should only display a hint when at least
  15311. // its OnGetHintSize event handler is assigned.
  15312. DoGetHintKind(HitInfo.HitNode, HitInfo.HitColumn, HintKind);
  15313. FHintData.HintRect := Rect(0, 0, 0, 0);
  15314. if (HintKind = vhkOwnerDraw) then
  15315. begin
  15316. DoGetHintSize(HitInfo.HitNode, HitInfo.HitColumn, FHintData.HintRect);
  15317. ShowOwnHint := not IsRectEmpty(FHintData.HintRect);
  15318. end
  15319. else
  15320. // For trees displaying text hints, a decision about showing the hint or not is based
  15321. // on the hint string (if it is empty then no hint is shown).
  15322. ShowOwnHint := True;
  15323. if ShowOwnHint then
  15324. begin
  15325. if HitInfo.HitColumn > NoColumn then
  15326. begin
  15327. FHeader.FColumns.GetColumnBounds(HitInfo.HitColumn, ColLeft, ColRight);
  15328. // The right column border might be extended if column spanning is enabled.
  15329. if toAutoSpanColumns in FOptions.FAutoOptions then
  15330. begin
  15331. SpanColumn := HitInfo.HitColumn;
  15332. repeat
  15333. Dummy := FHeader.FColumns.GetNextVisibleColumn(SpanColumn);
  15334. if (Dummy = InvalidColumn) or not ColumnIsEmpty(HitInfo.HitNode, Dummy) then
  15335. Break;
  15336. SpanColumn := Dummy;
  15337. until False;
  15338. if SpanColumn <> HitInfo.HitColumn then
  15339. FHeader.FColumns.GetColumnBounds(SpanColumn, Dummy, ColRight);
  15340. end;
  15341. end
  15342. else
  15343. begin
  15344. ColLeft := 0;
  15345. ColRight := ClientWidth;
  15346. end;
  15347. FHintData.DefaultHint := '';
  15348. if FHintMode <> hmTooltip then
  15349. begin
  15350. // Node specific hint text.
  15351. CursorRect := GetDisplayRect(HitInfo.HitNode, HitInfo.HitColumn, False);
  15352. CursorRect.Left := ColLeft;
  15353. CursorRect.Right := ColRight;
  15354. // Align the vertical hint position on the bottom bound of the node, but
  15355. // avoid overlapping of mouse cursor and hint.
  15356. HintPos.Y := Max(HintPos.Y, ClientToScreen(CursorRect.BottomRight).Y) + 2;
  15357. end
  15358. else
  15359. begin
  15360. // Tool tip to show. This means the full caption of the node must be displayed.
  15361. if vsMultiline in HitInfo.HitNode.States then
  15362. begin
  15363. if hiOnItemLabel in HitInfo.HitPositions then
  15364. begin
  15365. ShowOwnHint := True;
  15366. NodeRect := GetDisplayRect(HitInfo.HitNode, HitInfo.HitColumn, True, False);
  15367. end;
  15368. end
  15369. else
  15370. begin
  15371. NodeRect := GetDisplayRect(HitInfo.HitNode, HitInfo.HitColumn, True, True, True);
  15372. BottomRightCellContentMargin := DoGetCellContentMargin(HitInfo.HitNode, HitInfo.HitColumn, ccmtBottomRightOnly);
  15373. ShowOwnHint := (HitInfo.HitColumn > InvalidColumn) and PtInRect(NodeRect, CursorPos) and
  15374. (CursorPos.X <= ColRight) and (CursorPos.X >= ColLeft) and
  15375. (
  15376. // Show hint also if the node text is partially out of the client area.
  15377. // "ColRight - 1", since the right column border is not part of this cell.
  15378. ( (NodeRect.Right + BottomRightCellContentMargin.X) > Min(ColRight - 1, ClientWidth) ) or
  15379. (NodeRect.Left < Max(ColLeft, 0)) or
  15380. ( (NodeRect.Bottom + BottomRightCellContentMargin.Y) > ClientHeight ) or
  15381. (NodeRect.Top < 0)
  15382. );
  15383. end;
  15384. if ShowOwnHint then
  15385. begin
  15386. // Node specific hint text given will be retrieved when needed.
  15387. FHintData.DefaultHint := '';
  15388. HintPos := ClientToScreen(Point(NodeRect.Left, NodeRect.Top));
  15389. CursorRect := NodeRect;
  15390. end
  15391. else
  15392. // nothing to show
  15393. Result := 1;
  15394. end;
  15395. end
  15396. else
  15397. Result := 1; // Avoid hint if this is a draw tree returning an empty hint rectangle.
  15398. end
  15399. else
  15400. begin
  15401. // No node so fall back to control's hint (if indicated) or show nothing.
  15402. if FHintMode = hmHintAndDefault then
  15403. begin
  15404. FHintData.DefaultHint := GetShortHint(Hint);
  15405. if Length(FHintData.DefaultHint) = 0 then
  15406. Result := 1
  15407. else
  15408. ShowOwnHint := True;
  15409. end
  15410. else
  15411. Result := 1;
  15412. end;
  15413. end;
  15414. end;
  15415. // Set our own hint window class and prepare structure to be passed to the hint window.
  15416. if ShowOwnHint and (Result = 0) then
  15417. begin
  15418. HintWindowClass := GetHintWindowClass;
  15419. FHintData.Tree := Self;
  15420. FHintData.Column := HitInfo.HitColumn;
  15421. FHintData.Node := HitInfo.HitNode;
  15422. FLastHintRect := CursorRect;
  15423. HintData := @FHintData;
  15424. end
  15425. else
  15426. FLastHintRect := Rect(0, 0, 0, 0);
  15427. end;
  15428. // Remind that a hint is about to show.
  15429. if Result = 0 then
  15430. DoStateChange([tsHint])
  15431. else
  15432. DoStateChange([], [tsHint]);
  15433. end;
  15434. end;
  15435. end;
  15436. //----------------------------------------------------------------------------------------------------------------------
  15437. procedure TBaseVirtualTree.CMHintShowPause(var Message: TCMHintShowPause);
  15438. // Tells the application that the tree (and only the tree) does not want a delayed tool tip.
  15439. // Normal hints / header hints use the default delay (except for the first time).
  15440. var
  15441. P: TPoint;
  15442. begin
  15443. // A little workaround is needed here to make the application class using the correct hint window class.
  15444. // Once the application gets ShowHint set to true (which is the case when we want to show hints in the tree) then
  15445. // an internal hint window will be created which is not our own class (because we don't set an application wide
  15446. // hint window class but only one for the tree). Unfortunately, this default hint window class will prevent
  15447. // hints for the non-client area to show up (e.g. for the header) by calling CancelHint whenever certain messages
  15448. // arrive. By setting the hint show pause to 0 if our hint class was not used recently we make sure
  15449. // that the hint timer (in Forms.pas) is not used and our class is created immediately.
  15450. if FHintWindowDestroyed then
  15451. begin
  15452. GetCursorPos(P);
  15453. // Check if the mouse is in the header or tool tips are enabled, which must be shown without delay anyway.
  15454. if FHeader.UseColumns and (hoShowHint in FHeader.FOptions) and FHeader.InHeader(ScreenToClient(P)) or
  15455. (FHintMode = hmToolTip) then
  15456. Message.Pause^ := 0;
  15457. end
  15458. else
  15459. if FHintMode = hmToolTip then
  15460. Message.Pause^ := 0;
  15461. end;
  15462. //----------------------------------------------------------------------------------------------------------------------
  15463. procedure TBaseVirtualTree.CMMouseEnter(var Message: TMessage);
  15464. begin
  15465. DoMouseEnter();
  15466. inherited;
  15467. end;
  15468. //----------------------------------------------------------------------------------------------------------------------
  15469. procedure TBaseVirtualTree.CMMouseLeave(var Message: TMessage);
  15470. var
  15471. LeaveStates: TVirtualTreeStates;
  15472. begin
  15473. // Reset the last used hint rectangle in case the mouse enters the window within the bounds
  15474. if Assigned(FHintData.Tree) then
  15475. FHintData.Tree.FLastHintRect := Rect(0, 0, 0, 0);
  15476. LeaveStates := [tsHint];
  15477. if [tsWheelPanning, tsWheelScrolling] * FStates = [] then
  15478. begin
  15479. StopTimer(ScrollTimer);
  15480. LeaveStates := LeaveStates + [tsScrollPending, tsScrolling];
  15481. end;
  15482. DoStateChange([], LeaveStates);
  15483. if Assigned(FCurrentHotNode) then
  15484. begin
  15485. DoHotChange(FCurrentHotNode, nil);
  15486. if (toHotTrack in FOptions.PaintOptions) or (toCheckSupport in FOptions.FMiscOptions) then
  15487. InvalidateNode(FCurrentHotNode);
  15488. FCurrentHotNode := nil;
  15489. end;
  15490. if Assigned(Header) then
  15491. begin
  15492. Header.FColumns.FDownIndex := NoColumn;
  15493. Header.FColumns.FHoverIndex := NoColumn;
  15494. Header.FColumns.FCheckBoxHit := False;
  15495. end;
  15496. DoMouseLeave();
  15497. inherited;
  15498. end;
  15499. //----------------------------------------------------------------------------------------------------------------------
  15500. procedure TBaseVirtualTree.CMMouseWheel(var Message: TCMMouseWheel);
  15501. var
  15502. ScrollAmount: Integer;
  15503. ScrollLines: DWORD;
  15504. RTLFactor: Integer;
  15505. WheelFactor: Double;
  15506. begin
  15507. StopWheelPanning;
  15508. inherited;
  15509. if Message.Result = 0 then
  15510. begin
  15511. with Message do
  15512. begin
  15513. Result := 1;
  15514. WheelFactor := WheelDelta / WHEEL_DELTA;
  15515. if (FRangeY > Cardinal(ClientHeight)) and (not (ssShift in ShiftState)) then
  15516. begin
  15517. // Scroll vertically if there's something to scroll...
  15518. if ssCtrl in ShiftState then
  15519. ScrollAmount := Trunc(WheelFactor * ClientHeight)
  15520. else
  15521. begin
  15522. SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, @ScrollLines, 0);
  15523. if ScrollLines = WHEEL_PAGESCROLL then
  15524. ScrollAmount := Trunc(WheelFactor * ClientHeight)
  15525. else
  15526. ScrollAmount := Integer(Trunc(WheelFactor * ScrollLines * FDefaultNodeHeight));
  15527. end;
  15528. SetOffsetY(FOffsetY + ScrollAmount);
  15529. end
  15530. else
  15531. begin
  15532. // ...else scroll horizontally if there's something to scroll.
  15533. if UseRightToLeftAlignment then
  15534. RTLFactor := -1
  15535. else
  15536. RTLFactor := 1;
  15537. if ssCtrl in ShiftState then
  15538. ScrollAmount := Trunc(WheelFactor * (ClientWidth - FHeader.Columns.GetVisibleFixedWidth))
  15539. else
  15540. begin
  15541. SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, @ScrollLines, 0);
  15542. ScrollAmount := Trunc(WheelFactor * ScrollLines * FHeader.Columns.GetScrollWidth);
  15543. end;
  15544. SetOffsetX(FOffsetX + RTLFactor * ScrollAmount);
  15545. end;
  15546. end;
  15547. end;
  15548. end;
  15549. //----------------------------------------------------------------------------------------------------------------------
  15550. procedure TBaseVirtualTree.CMSysColorChange(var Message: TMessage);
  15551. begin
  15552. inherited;
  15553. ConvertImageList(LightCheckImages, 'VT_CHECK_LIGHT');
  15554. ConvertImageList(DarkCheckImages, 'VT_CHECK_DARK');
  15555. ConvertImageList(LightTickImages, 'VT_TICK_LIGHT');
  15556. ConvertImageList(DarkTickImages, 'VT_TICK_DARK');
  15557. ConvertImageList(FlatImages, 'VT_FLAT');
  15558. ConvertImageList(UtilityImages, 'VT_UTILITIES');
  15559. // XP images do not need to be converted.
  15560. // System check images do not need to be converted.
  15561. Message.Msg := WM_SYSCOLORCHANGE;
  15562. DefaultHandler(Message);
  15563. end;
  15564. //----------------------------------------------------------------------------------------------------------------------
  15565. procedure TBaseVirtualTree.TVMGetItem(var Message: TMessage);
  15566. // Screen reader support function. The method returns information about a particular node.
  15567. const
  15568. StateMask = TVIS_STATEIMAGEMASK or TVIS_OVERLAYMASK or TVIS_EXPANDED or TVIS_DROPHILITED or TVIS_CUT or
  15569. TVIS_SELECTED or TVIS_FOCUSED;
  15570. var
  15571. Item: PTVItemEx;
  15572. Node: PVirtualNode;
  15573. Ghosted: Boolean;
  15574. ImageIndex: Integer;
  15575. R: TRect;
  15576. Text: UnicodeString;
  15577. {$ifndef UNICODE}
  15578. ANSIText: ANSIString;
  15579. {$endif}
  15580. begin
  15581. // We can only return valid data if a nodes reference is given.
  15582. Item := Pointer(Message.LParam);
  15583. Message.Result := Ord(((Item.mask and TVIF_HANDLE) <> 0) and Assigned(Item.hItem));
  15584. if Message.Result = 1 then
  15585. begin
  15586. Node := Pointer(Item.hItem);
  15587. // Child count requested?
  15588. if (Item.mask and TVIF_CHILDREN) <> 0 then
  15589. Item.cChildren := Node.ChildCount;
  15590. // Index for normal image requested?
  15591. if (Item.mask and TVIF_IMAGE) <> 0 then
  15592. begin
  15593. Item.iImage := -1;
  15594. DoGetImageIndex(Node, ikNormal, -1, Ghosted, Item.iImage);
  15595. end;
  15596. // Index for selected image requested?
  15597. if (Item.mask and TVIF_SELECTEDIMAGE) <> 0 then
  15598. begin
  15599. Item.iSelectedImage := -1;
  15600. DoGetImageIndex(Node, ikSelected, -1, Ghosted, Item.iSelectedImage);
  15601. end;
  15602. // State info requested?
  15603. if (Item.mask and TVIF_STATE) <> 0 then
  15604. begin
  15605. // Everything, which is possible is returned.
  15606. Item.stateMask := StateMask;
  15607. Item.state := 0;
  15608. if Node = FFocusedNode then
  15609. Item.state := Item.state or TVIS_FOCUSED;
  15610. if vsSelected in Node.States then
  15611. Item.state := Item.state or TVIS_SELECTED;
  15612. if vsCutOrCopy in Node.States then
  15613. Item.state := Item.state or TVIS_CUT;
  15614. if Node = FDropTargetNode then
  15615. Item.state := Item.state or TVIS_DROPHILITED;
  15616. if vsExpanded in Node.States then
  15617. Item.state := Item.state or TVIS_EXPANDED;
  15618. // Construct state image and overlay image indices. They are one based, btw.
  15619. // and zero means there is no image.
  15620. ImageIndex := -1;
  15621. DoGetImageIndex(Node, ikState, -1, Ghosted, ImageIndex);
  15622. Item.state := Item.state or Byte(IndexToStateImageMask(ImageIndex + 1));
  15623. ImageIndex := -1;
  15624. DoGetImageIndex(Node, ikOverlay, -1, Ghosted, ImageIndex);
  15625. Item.state := Item.state or Byte(IndexToOverlayMask(ImageIndex + 1));
  15626. end;
  15627. // Node caption requested?
  15628. if (Item.mask and TVIF_TEXT) <> 0 then
  15629. begin
  15630. GetTextInfo(Node, -1, Font, R, Text);
  15631. {$ifdef UNICODE}
  15632. StrLCopy(Item.pszText, PWideChar(Text), Item.cchTextMax - 1);
  15633. Item.pszText[Length(Text)] := #0;
  15634. {$else}
  15635. // Convert the Unicode implicitely to ANSI using the current locale.
  15636. ANSIText := Text;
  15637. StrLCopy(Item.pszText, PChar(ANSIText), Item.cchTextMax - 1);
  15638. Item.pszText[Length(ANSIText)] := #0;
  15639. {$endif}
  15640. end;
  15641. end;
  15642. end;
  15643. //----------------------------------------------------------------------------------------------------------------------
  15644. procedure TBaseVirtualTree.TVMGetItemRect(var Message: TMessage);
  15645. // Screen read support function. This method returns a node's display rectangle.
  15646. var
  15647. TextOnly: Boolean;
  15648. Node: PVirtualNode;
  15649. begin
  15650. // The lparam member is used two-way. On enter it contains a pointer to the item (node).
  15651. // On exit it is to be considered as pointer to a rectangle structure.
  15652. Node := Pointer(Pointer(Message.LParam)^);
  15653. Message.Result := Ord(IsVisible[Node]);
  15654. if Message.Result <> 0 then
  15655. begin
  15656. TextOnly := Message.WParam <> 0;
  15657. PRect(Message.LParam)^ := GetDisplayRect(Node, -1, TextOnly);
  15658. end;
  15659. end;
  15660. //----------------------------------------------------------------------------------------------------------------------
  15661. procedure TBaseVirtualTree.TVMGetNextItem(var Message: TMessage);
  15662. // Screen read support function. This method returns a node depending on the requested case.
  15663. var
  15664. Node: PVirtualNode;
  15665. begin
  15666. // Start with a nil result.
  15667. Message.Result := 0;
  15668. Node := Pointer(Message.LParam);
  15669. case Message.WParam of
  15670. TVGN_CARET:
  15671. Message.Result := LRESULT(FFocusedNode);
  15672. TVGN_CHILD:
  15673. if Assigned(Node) then
  15674. Message.Result := LRESULT(GetFirstChild(Node));
  15675. TVGN_DROPHILITE:
  15676. Message.Result := LRESULT(FDropTargetNode);
  15677. TVGN_FIRSTVISIBLE:
  15678. Message.Result := LRESULT(GetFirstVisible(nil, True));
  15679. TVGN_LASTVISIBLE:
  15680. Message.Result := LRESULT(GetLastVisible(nil, True));
  15681. TVGN_NEXT:
  15682. if Assigned(Node) then
  15683. Message.Result := LRESULT(GetNextSibling(Node));
  15684. TVGN_NEXTVISIBLE:
  15685. if Assigned(Node) then
  15686. Message.Result := LRESULT(GetNextVisible(Node, True));
  15687. TVGN_PARENT:
  15688. if Assigned(Node) and (Node <> FRoot) and (Node.Parent <> FRoot) then
  15689. Message.Result := LRESULT(Node.Parent);
  15690. TVGN_PREVIOUS:
  15691. if Assigned(Node) then
  15692. Message.Result := LRESULT(GetPreviousSibling(Node));
  15693. TVGN_PREVIOUSVISIBLE:
  15694. if Assigned(Node) then
  15695. Message.Result := LRESULT(GetPreviousVisible(Node, True));
  15696. TVGN_ROOT:
  15697. Message.Result := LRESULT(GetFirst);
  15698. end;
  15699. end;
  15700. //----------------------------------------------------------------------------------------------------------------------
  15701. procedure TBaseVirtualTree.WMCancelMode(var Message: TWMCancelMode);
  15702. begin
  15703. // Clear any transient state.
  15704. StopTimer(ExpandTimer);
  15705. StopTimer(EditTimer);
  15706. StopTimer(HeaderTimer);
  15707. StopTimer(ScrollTimer);
  15708. StopTimer(SearchTimer);
  15709. StopTimer(ThemeChangedTimer);
  15710. FSearchBuffer := '';
  15711. FLastSearchNode := nil;
  15712. DoStateChange([], [tsClearPending, tsEditPending, tsOLEDragPending, tsVCLDragPending, tsDrawSelecting,
  15713. tsDrawSelPending, tsIncrementalSearching]);
  15714. inherited;
  15715. end;
  15716. //----------------------------------------------------------------------------------------------------------------------
  15717. procedure TBaseVirtualTree.WMChangeState(var Message: TMessage);
  15718. var
  15719. EnterStates,
  15720. LeaveStates: TVirtualTreeStates;
  15721. begin
  15722. EnterStates := [];
  15723. if csStopValidation in TChangeStates(Byte(Message.WParam)) then
  15724. Include(EnterStates, tsStopValidation);
  15725. if csUseCache in TChangeStates(Byte(Message.WParam)) then
  15726. Include(EnterStates, tsUseCache);
  15727. if csValidating in TChangeStates(Byte(Message.WParam)) then
  15728. Include(EnterStates, tsValidating);
  15729. if csValidationNeeded in TChangeStates(Byte(Message.WParam)) then
  15730. Include(EnterStates, tsValidationNeeded);
  15731. LeaveStates := [];
  15732. if csStopValidation in TChangeStates(Byte(Message.LParam)) then
  15733. Include(LeaveStates, tsStopValidation);
  15734. if csUseCache in TChangeStates(Byte(Message.LParam)) then
  15735. Include(LeaveStates, tsUseCache);
  15736. if csValidating in TChangeStates(Byte(Message.LParam)) then
  15737. Include(LeaveStates, tsValidating);
  15738. if csValidationNeeded in TChangeStates(Byte(Message.LParam)) then
  15739. Include(LeaveStates, tsValidationNeeded);
  15740. DoStateChange(EnterStates, LeaveStates);
  15741. end;
  15742. //----------------------------------------------------------------------------------------------------------------------
  15743. procedure TBaseVirtualTree.WMChar(var Message: TWMChar);
  15744. begin
  15745. if tsIncrementalSearchPending in FStates then
  15746. begin
  15747. HandleIncrementalSearch(Message.CharCode);
  15748. DoStateChange([], [tsIncrementalSearchPending]);
  15749. end;
  15750. inherited;
  15751. end;
  15752. //----------------------------------------------------------------------------------------------------------------------
  15753. procedure TBaseVirtualTree.WMContextMenu(var Message: TWMContextMenu);
  15754. // This method is called when a popup menu is about to be displayed.
  15755. // We have to cancel some pending states here to avoid interferences.
  15756. begin
  15757. DoStateChange([], [tsClearPending, tsEditPending, tsOLEDragPending, tsVCLDragPending]);
  15758. if not (tsPopupMenuShown in FStates) then
  15759. inherited;
  15760. end;
  15761. //----------------------------------------------------------------------------------------------------------------------
  15762. procedure TBaseVirtualTree.WMCopy(var Message: TWMCopy);
  15763. begin
  15764. CopyToClipboard;
  15765. end;
  15766. //----------------------------------------------------------------------------------------------------------------------
  15767. procedure TBaseVirtualTree.WMCut(var Message: TWMCut);
  15768. begin
  15769. CutToClipboard;
  15770. end;
  15771. //----------------------------------------------------------------------------------------------------------------------
  15772. procedure TBaseVirtualTree.WMEnable(var Message: TWMEnable);
  15773. begin
  15774. inherited;
  15775. RedrawWindow(Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE or RDW_NOERASE or RDW_NOCHILDREN);
  15776. end;
  15777. //----------------------------------------------------------------------------------------------------------------------
  15778. procedure TBaseVirtualTree.WMEraseBkgnd(var Message: TWMEraseBkgnd);
  15779. begin
  15780. Message.Result := 1;
  15781. end;
  15782. //----------------------------------------------------------------------------------------------------------------------
  15783. procedure TBaseVirtualTree.WMGetDlgCode(var Message: TWMGetDlgCode);
  15784. begin
  15785. Message.Result := DLGC_WANTCHARS or DLGC_WANTARROWS;
  15786. if FWantTabs then
  15787. Message.Result := Message.Result or DLGC_WANTTAB;
  15788. end;
  15789. //----------------------------------------------------------------------------------------------------------------------
  15790. procedure TBaseVirtualTree.WMGetObject(var Message: TMessage);
  15791. begin
  15792. if GetAccessibilityFactory <> nil then
  15793. begin
  15794. // Create the IAccessibles for the tree view and tree view items, if necessary.
  15795. if FAccessible = nil then
  15796. FAccessible := GetAccessibilityFactory.CreateIAccessible(Self);
  15797. if FAccessibleItem = nil then
  15798. FAccessibleItem := GetAccessibilityFactory.CreateIAccessible(Self);
  15799. if Cardinal(Message.LParam) = OBJID_CLIENT then
  15800. {$if CompilerVersion >= 18}
  15801. if Assigned(Accessible) then
  15802. Message.Result := LresultFromObject(IID_IAccessible, Message.WParam, FAccessible)
  15803. else
  15804. {$ifend}
  15805. Message.Result := 0;
  15806. end;
  15807. end;
  15808. //----------------------------------------------------------------------------------------------------------------------
  15809. procedure TBaseVirtualTree.WMHScroll(var Message: TWMHScroll);
  15810. //--------------- local functions -------------------------------------------
  15811. function GetRealScrollPosition: Integer;
  15812. var
  15813. SI: TScrollInfo;
  15814. Code: Integer;
  15815. begin
  15816. SI.cbSize := SizeOf(TScrollInfo);
  15817. SI.fMask := SIF_TRACKPOS;
  15818. Code := SB_HORZ;
  15819. GetScrollInfo(Handle, Code, SI);
  15820. Result := SI.nTrackPos;
  15821. end;
  15822. //--------------- end local functions ---------------------------------------
  15823. var
  15824. RTLFactor: Integer;
  15825. begin
  15826. if UseRightToLeftAlignment then
  15827. RTLFactor := -1
  15828. else
  15829. RTLFactor := 1;
  15830. case Message.ScrollCode of
  15831. SB_BOTTOM:
  15832. SetOffsetX(-Integer(FRangeX));
  15833. SB_ENDSCROLL:
  15834. begin
  15835. DoStateChange([], [tsThumbTracking]);
  15836. // avoiding to adjust the vertical scroll position while tracking makes it much smoother
  15837. // but we need to adjust the final position here then
  15838. UpdateHorizontalScrollBar(False);
  15839. end;
  15840. SB_LINELEFT:
  15841. SetOffsetX(FOffsetX + RTLFactor * FScrollBarOptions.FIncrementX);
  15842. SB_LINERIGHT:
  15843. SetOffsetX(FOffsetX - RTLFactor * FScrollBarOptions.FIncrementX);
  15844. SB_PAGELEFT:
  15845. SetOffsetX(FOffsetX + RTLFactor * (ClientWidth - FHeader.Columns.GetVisibleFixedWidth));
  15846. SB_PAGERIGHT:
  15847. SetOffsetX(FOffsetX - RTLFactor * (ClientWidth - FHeader.Columns.GetVisibleFixedWidth));
  15848. SB_THUMBPOSITION,
  15849. SB_THUMBTRACK:
  15850. begin
  15851. DoStateChange([tsThumbTracking]);
  15852. if UseRightToLeftAlignment then
  15853. SetOffsetX(-Integer(FRangeX) + ClientWidth + GetRealScrollPosition)
  15854. else
  15855. SetOffsetX(-GetRealScrollPosition);
  15856. end;
  15857. SB_TOP:
  15858. SetOffsetX(0);
  15859. end;
  15860. Message.Result := 0;
  15861. end;
  15862. //----------------------------------------------------------------------------------------------------------------------
  15863. procedure TBaseVirtualTree.WMKeyDown(var Message: TWMKeyDown);
  15864. // Keyboard event handling for node focus, selection, node specific popup menus and help invokation.
  15865. // For a detailed description of every action done here read the help.
  15866. var
  15867. Shift: TShiftState;
  15868. Node, Temp,
  15869. LastFocused: PVirtualNode;
  15870. Offset: Integer;
  15871. ClearPending,
  15872. NeedInvalidate,
  15873. DoRangeSelect,
  15874. HandleMultiSelect: Boolean;
  15875. Context: Integer;
  15876. ParentControl: TWinControl;
  15877. R: TRect;
  15878. NewCheckState: TCheckState;
  15879. TempColumn,
  15880. NewColumn: TColumnIndex;
  15881. ActAsGrid: Boolean;
  15882. ForceSelection: Boolean;
  15883. NewWidth,
  15884. NewHeight: Integer;
  15885. RTLFactor: Integer;
  15886. // for tabulator handling
  15887. GetStartColumn: function(ConsiderAllowFocus: Boolean = False): TColumnIndex of object;
  15888. GetNextColumn: function(Column: TColumnIndex; ConsiderAllowFocus: Boolean = False): TColumnIndex of object;
  15889. GetNextNode: TGetNextNodeProc;
  15890. KeyState: TKeyboardState;
  15891. Buffer: array[0..1] of AnsiChar;
  15892. begin
  15893. // Make form key preview work and let application modify the key if it wants this.
  15894. inherited;
  15895. with Message do
  15896. begin
  15897. Shift := KeyDataToShiftState(KeyData);
  15898. // Ask the application if the default key handling is desired.
  15899. if DoKeyAction(CharCode, Shift) then
  15900. begin
  15901. if (tsKeyCheckPending in FStates) and (CharCode <> VK_SPACE) then
  15902. begin
  15903. DoStateChange([], [tskeyCheckPending]);
  15904. FCheckNode.CheckState := UnpressedState[FCheckNode.CheckState];
  15905. RepaintNode(FCheckNode);
  15906. FCheckNode := nil;
  15907. end;
  15908. if (CharCode in [VK_HOME, VK_END, VK_PRIOR, VK_NEXT, VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT, VK_BACK, VK_TAB]) and (RootNode.FirstChild <> nil) then
  15909. begin
  15910. HandleMultiSelect := (ssShift in Shift) and (toMultiSelect in FOptions.FSelectionOptions) and not IsEditing;
  15911. // Flag to avoid range selection in case of single node advance.
  15912. DoRangeSelect := (CharCode in [VK_HOME, VK_END, VK_PRIOR, VK_NEXT]) and HandleMultiSelect and not IsEditing;
  15913. NeedInvalidate := DoRangeSelect or (FSelectionCount > 1);
  15914. ActAsGrid := toGridExtensions in FOptions.FMiscOptions;
  15915. ClearPending := (Shift = []) or (ActAsGrid and not (ssShift in Shift)) or
  15916. not (toMultiSelect in FOptions.FSelectionOptions) or (CharCode in [VK_TAB, VK_BACK]);
  15917. // Keep old focused node for range selection. Use a default node if none was focused until now.
  15918. LastFocused := FFocusedNode;
  15919. if (LastFocused = nil) and (Shift <> []) then
  15920. LastFocused := GetFirstVisible(nil, True);
  15921. // Set an initial range anchor if there is not yet one.
  15922. if FRangeAnchor = nil then
  15923. FRangeAnchor := GetFirstSelected;
  15924. if FRangeAnchor = nil then
  15925. FRangeAnchor := GetFirst;
  15926. if UseRightToLeftAlignment then
  15927. RTLFactor := -1
  15928. else
  15929. RTLFactor := 1;
  15930. // Determine new focused node.
  15931. case CharCode of
  15932. VK_HOME, VK_END:
  15933. begin
  15934. if (CharCode = VK_END) xor UseRightToLeftAlignment then
  15935. begin
  15936. GetStartColumn := FHeader.FColumns.GetLastVisibleColumn;
  15937. GetNextColumn := FHeader.FColumns.GetPreviousVisibleColumn;
  15938. GetNextNode := GetPreviousVisible;
  15939. Node := GetLastVisible(nil, True);
  15940. end
  15941. else
  15942. begin
  15943. GetStartColumn := FHeader.FColumns.GetFirstVisibleColumn;
  15944. GetNextColumn := FHeader.FColumns.GetNextVisibleColumn;
  15945. GetNextNode := GetNextVisible;
  15946. Node := GetFirstVisible(nil, True);
  15947. end;
  15948. // Advance to next/previous visible column.
  15949. if FHeader.UseColumns then
  15950. NewColumn := GetStartColumn
  15951. else
  15952. NewColumn := NoColumn;
  15953. // Find a column for the new/current node which can be focused.
  15954. // Make the 'DoFocusChanging' for finding a valid column
  15955. // identifiable from the 'DoFocusChanging' raised later on by
  15956. // "FocusedNode := Node;"
  15957. while (NewColumn > NoColumn) and not DoFocusChanging(FFocusedNode, FFocusedNode, FFocusedColumn, NewColumn) do
  15958. NewColumn := GetNextColumn(NewColumn);
  15959. if NewColumn > InvalidColumn then
  15960. begin
  15961. if (Shift = [ssCtrl]) and not ActAsGrid then
  15962. begin
  15963. ScrollIntoView(Node, toCenterScrollIntoView in FOptions.SelectionOptions,
  15964. not (toDisableAutoscrollOnFocus in FOptions.FAutoOptions));
  15965. if (CharCode = VK_HOME) and not UseRightToLeftAlignment then
  15966. SetOffsetX(0)
  15967. else
  15968. SetOffsetX(-MaxInt);
  15969. end
  15970. else
  15971. begin
  15972. if not ActAsGrid or (ssCtrl in Shift) then
  15973. FocusedNode := Node;
  15974. if ActAsGrid and not (toFullRowSelect in FOptions.FSelectionOptions) then
  15975. FocusedColumn := NewColumn;
  15976. end;
  15977. end;
  15978. end;
  15979. VK_PRIOR:
  15980. if Shift = [ssCtrl, ssShift] then
  15981. SetOffsetX(FOffsetX + ClientWidth)
  15982. else
  15983. if [ssShift, ssAlt] = Shift then
  15984. begin
  15985. if FFocusedColumn <= NoColumn then
  15986. NewColumn := FHeader.FColumns.GetFirstVisibleColumn
  15987. else
  15988. begin
  15989. Offset := FHeader.FColumns.GetVisibleFixedWidth;
  15990. NewColumn := FFocusedColumn;
  15991. while True do
  15992. begin
  15993. TempColumn := FHeader.FColumns.GetPreviousVisibleColumn(NewColumn);
  15994. NewWidth := FHeader.FColumns[NewColumn].Width;
  15995. if (TempColumn <= NoColumn) or
  15996. (Offset + NewWidth >= ClientWidth) or
  15997. (coFixed in FHeader.FColumns[TempColumn].FOptions) then
  15998. Break;
  15999. NewColumn := TempColumn;
  16000. Inc(Offset, NewWidth);
  16001. end;
  16002. end;
  16003. SetFocusedColumn(NewColumn);
  16004. end
  16005. else
  16006. if ssCtrl in Shift then
  16007. SetOffsetY(FOffsetY + ClientHeight)
  16008. else
  16009. begin
  16010. Offset := 0;
  16011. // If there's no focused node then just take the very first visible one.
  16012. if FFocusedNode = nil then
  16013. Node := GetFirstVisible(nil, True)
  16014. else
  16015. begin
  16016. // Go up as many nodes as comprise together a size of ClientHeight.
  16017. Node := FFocusedNode;
  16018. while True do
  16019. begin
  16020. Temp := GetPreviousVisible(Node, True);
  16021. NewHeight := NodeHeight[Node];
  16022. if (Temp = nil) or (Offset + NewHeight >= ClientHeight) then
  16023. Break;
  16024. Node := Temp;
  16025. Inc(Offset, NodeHeight[Node]);
  16026. end;
  16027. end;
  16028. FocusedNode := Node;
  16029. end;
  16030. VK_NEXT:
  16031. if Shift = [ssCtrl, ssShift] then
  16032. SetOffsetX(FOffsetX - ClientWidth)
  16033. else
  16034. if [ssShift, ssAlt] = Shift then
  16035. begin
  16036. if FFocusedColumn <= NoColumn then
  16037. NewColumn := FHeader.FColumns.GetFirstVisibleColumn
  16038. else
  16039. begin
  16040. Offset := FHeader.FColumns.GetVisibleFixedWidth;
  16041. NewColumn := FFocusedColumn;
  16042. while True do
  16043. begin
  16044. TempColumn := FHeader.FColumns.GetNextVisibleColumn(NewColumn);
  16045. NewWidth := FHeader.FColumns[NewColumn].Width;
  16046. if (TempColumn <= NoColumn) or
  16047. (Offset + NewWidth >= ClientWidth) or
  16048. (coFixed in FHeader.FColumns[TempColumn].FOptions) then
  16049. Break;
  16050. NewColumn := TempColumn;
  16051. Inc(Offset, NewWidth);
  16052. end;
  16053. end;
  16054. SetFocusedColumn(NewColumn);
  16055. end
  16056. else
  16057. if ssCtrl in Shift then
  16058. SetOffsetY(FOffsetY - ClientHeight)
  16059. else
  16060. begin
  16061. Offset := 0;
  16062. // If there's no focused node then just take the very last one.
  16063. if FFocusedNode = nil then
  16064. Node := GetLastVisible(nil, True)
  16065. else
  16066. begin
  16067. // Go up as many nodes as comprise together a size of ClientHeight.
  16068. Node := FFocusedNode;
  16069. while True do
  16070. begin
  16071. Temp := GetNextVisible(Node, True);
  16072. NewHeight := NodeHeight[Node];
  16073. if (Temp = nil) or (Offset + NewHeight >= ClientHeight) then
  16074. Break;
  16075. Node := Temp;
  16076. Inc(Offset, NewHeight);
  16077. end;
  16078. end;
  16079. FocusedNode := Node;
  16080. end;
  16081. VK_UP:
  16082. begin
  16083. // scrolling without selection change
  16084. if ssCtrl in Shift then
  16085. SetOffsetY(FOffsetY + Integer(FDefaultNodeHeight))
  16086. else
  16087. begin
  16088. if FFocusedNode = nil then
  16089. Node := GetLastVisible(nil, True)
  16090. else
  16091. Node := GetPreviousVisible(FFocusedNode, True);
  16092. if Assigned(Node) then
  16093. begin
  16094. EndEditNode;
  16095. if HandleMultiSelect and (CompareNodePositions(LastFocused, FRangeAnchor) > 0) and
  16096. Assigned(FFocusedNode) then
  16097. RemoveFromSelection(FFocusedNode);
  16098. if FFocusedColumn <= NoColumn then
  16099. FFocusedColumn := FHeader.MainColumn;
  16100. FocusedNode := Node;
  16101. end
  16102. else
  16103. if Assigned(FFocusedNode) then
  16104. InvalidateNode(FFocusedNode);
  16105. end;
  16106. end;
  16107. VK_DOWN:
  16108. begin
  16109. // scrolling without selection change
  16110. if ssCtrl in Shift then
  16111. SetOffsetY(FOffsetY - Integer(FDefaultNodeHeight))
  16112. else
  16113. begin
  16114. if FFocusedNode = nil then
  16115. Node := GetFirstVisible(nil, True)
  16116. else
  16117. Node := GetNextVisible(FFocusedNode, True);
  16118. if Assigned(Node) then
  16119. begin
  16120. EndEditNode;
  16121. if HandleMultiSelect and (CompareNodePositions(LastFocused, FRangeAnchor) < 0) and
  16122. Assigned(FFocusedNode) then
  16123. RemoveFromSelection(FFocusedNode);
  16124. if FFocusedColumn <= NoColumn then
  16125. FFocusedColumn := FHeader.MainColumn;
  16126. FocusedNode := Node;
  16127. end
  16128. else
  16129. if Assigned(FFocusedNode) then
  16130. InvalidateNode(FFocusedNode);
  16131. end;
  16132. end;
  16133. VK_LEFT:
  16134. begin
  16135. // special handling
  16136. if ssCtrl in Shift then
  16137. SetOffsetX(FOffsetX + RTLFactor * FHeader.Columns.GetScrollWidth)
  16138. else
  16139. begin
  16140. // other special cases
  16141. Context := NoColumn;
  16142. if (toExtendedFocus in FOptions.FSelectionOptions) and (toGridExtensions in FOptions.FMiscOptions) then
  16143. begin
  16144. Context := FHeader.Columns.GetPreviousVisibleColumn(FFocusedColumn, True);
  16145. if Context > -1 then
  16146. FocusedColumn := Context;
  16147. end
  16148. else
  16149. if Assigned(FFocusedNode) and (vsExpanded in FFocusedNode.States) and
  16150. (Shift = []) and (vsHasChildren in FFocusedNode.States) then
  16151. ToggleNode(FFocusedNode)
  16152. else
  16153. begin
  16154. if FFocusedNode = nil then
  16155. FocusedNode := GetFirstVisible(nil, True)
  16156. else
  16157. begin
  16158. if FFocusedNode.Parent <> FRoot then
  16159. Node := FFocusedNode.Parent
  16160. else
  16161. Node := nil;
  16162. if Assigned(Node) then
  16163. begin
  16164. if HandleMultiSelect then
  16165. begin
  16166. // and a third special case
  16167. if FFocusedNode.Index > 0 then
  16168. DoRangeSelect := True
  16169. else
  16170. if CompareNodePositions(Node, FRangeAnchor) > 0 then
  16171. RemoveFromSelection(FFocusedNode);
  16172. end;
  16173. FocusedNode := Node;
  16174. end;
  16175. end;
  16176. end;
  16177. end;
  16178. end;
  16179. VK_RIGHT:
  16180. begin
  16181. // special handling
  16182. if ssCtrl in Shift then
  16183. SetOffsetX(FOffsetX - RTLFactor * FHeader.Columns.GetScrollWidth)
  16184. else
  16185. begin
  16186. // other special cases
  16187. Context := NoColumn;
  16188. if (toExtendedFocus in FOptions.FSelectionOptions) and (toGridExtensions in FOptions.FMiscOptions) then
  16189. begin
  16190. Context := FHeader.Columns.GetNextVisibleColumn(FFocusedColumn, True);
  16191. if Context > -1 then
  16192. FocusedColumn := Context;
  16193. end
  16194. else
  16195. if Assigned(FFocusedNode) and not (vsExpanded in FFocusedNode.States) and
  16196. (Shift = []) and (vsHasChildren in FFocusedNode.States) then
  16197. ToggleNode(FFocusedNode)
  16198. else
  16199. begin
  16200. if FFocusedNode = nil then
  16201. FocusedNode := GetFirstVisible(nil, True)
  16202. else
  16203. begin
  16204. Node := GetFirstVisibleChild(FFocusedNode);
  16205. if Assigned(Node) then
  16206. begin
  16207. if HandleMultiSelect and (CompareNodePositions(Node, FRangeAnchor) < 0) then
  16208. RemoveFromSelection(FFocusedNode);
  16209. FocusedNode := Node;
  16210. end;
  16211. end;
  16212. end;
  16213. end;
  16214. end;
  16215. VK_BACK:
  16216. if tsIncrementalSearching in FStates then
  16217. DoStateChange([tsIncrementalSearchPending])
  16218. else
  16219. if Assigned(FFocusedNode) and (FFocusedNode.Parent <> FRoot) then
  16220. FocusedNode := FocusedNode.Parent;
  16221. VK_TAB:
  16222. if (toExtendedFocus in FOptions.FSelectionOptions) and FHeader.UseColumns then
  16223. begin
  16224. // In order to avoid duplicating source code just to change the direction
  16225. // we use function variables.
  16226. if ssShift in Shift then
  16227. begin
  16228. GetStartColumn := FHeader.FColumns.GetLastVisibleColumn;
  16229. GetNextColumn := FHeader.FColumns.GetPreviousVisibleColumn;
  16230. GetNextNode := GetPreviousVisible;
  16231. end
  16232. else
  16233. begin
  16234. GetStartColumn := FHeader.FColumns.GetFirstVisibleColumn;
  16235. GetNextColumn := FHeader.FColumns.GetNextVisibleColumn;
  16236. GetNextNode := GetNextVisible;
  16237. end;
  16238. // Advance to next/previous visible column/node.
  16239. Node := FFocusedNode;
  16240. NewColumn := GetNextColumn(FFocusedColumn, True);
  16241. repeat
  16242. // Find a column for the current node which can be focused.
  16243. while (NewColumn > NoColumn) and not DoFocusChanging(FFocusedNode, Node, FFocusedColumn, NewColumn) do
  16244. NewColumn := GetNextColumn(NewColumn, True);
  16245. if NewColumn > NoColumn then
  16246. begin
  16247. // Set new node and column in one go.
  16248. SetFocusedNodeAndColumn(Node, NewColumn);
  16249. Break;
  16250. end;
  16251. // No next column was accepted for the current node. So advance to next node and try again.
  16252. Node := GetNextNode(Node);
  16253. NewColumn := GetStartColumn;
  16254. until Node = nil;
  16255. end;
  16256. end;
  16257. // Clear old selection if required but take care to select the new focused node if it was not selected before.
  16258. ForceSelection := False;
  16259. if ClearPending and ((LastFocused <> FFocusedNode) or (FSelectionCount <> 1)) then
  16260. begin
  16261. ClearSelection;
  16262. ForceSelection := True;
  16263. end;
  16264. // Determine new selection anchor.
  16265. if Shift = [] then
  16266. begin
  16267. FRangeAnchor := FFocusedNode;
  16268. FLastSelectionLevel := GetNodeLevel(FFocusedNode);
  16269. end;
  16270. if Assigned(FFocusedNode) then
  16271. begin
  16272. // Finally change the selection for a specific range of nodes.
  16273. if DoRangeSelect then
  16274. ToggleSelection(LastFocused, FFocusedNode);
  16275. // Make sure the new focused node is also selected.
  16276. if (LastFocused <> FFocusedNode) or ForceSelection then
  16277. AddToSelection(FFocusedNode);
  16278. end;
  16279. // If a repaint is needed then paint the entire tree because of the ClearSelection call,
  16280. if NeedInvalidate then
  16281. Invalidate;
  16282. end
  16283. else
  16284. begin
  16285. // Second chance for keys not directly concerned with selection changes.
  16286. // For +, -, /, * keys on the main keyboard (not numpad) there is no virtual key code defined.
  16287. // We have to do special processing to get them working too.
  16288. GetKeyboardState(KeyState);
  16289. // Avoid conversion to control characters. We have captured the control key state already in Shift.
  16290. KeyState[VK_CONTROL] := 0;
  16291. if ToASCII(Message.CharCode, (Message.KeyData shr 16) and 7, KeyState, PChar(@Buffer), 0) > 0 then
  16292. begin
  16293. case Buffer[0] of
  16294. '*':
  16295. CharCode := VK_MULTIPLY;
  16296. '+':
  16297. CharCode := VK_ADD;
  16298. '/':
  16299. CharCode := VK_DIVIDE;
  16300. '-':
  16301. CharCode := VK_SUBTRACT;
  16302. end;
  16303. end;
  16304. // According to https://web.archive.org/web/20041129085958/http://www.it-faq.pl/mskb/99/337.HTM
  16305. // there is a problem with ToASCII when used in conjunction with dead chars.
  16306. // The article recommends to call ToASCII twice to restore a deleted flag in the key message
  16307. // structure under certain circumstances. It turned out it is best to always call ToASCII twice.
  16308. ToASCII(Message.CharCode, (Message.KeyData shr 16) and 7, KeyState, PChar(@Buffer), 0);
  16309. case CharCode of
  16310. VK_F2:
  16311. if (Shift = []) and Assigned(FFocusedNode) and CanEdit(FFocusedNode, FFocusedColumn) then
  16312. begin
  16313. FEditColumn := FFocusedColumn;
  16314. DoEdit;
  16315. end;
  16316. VK_ADD:
  16317. if not (tsIncrementalSearching in FStates) then
  16318. begin
  16319. if ssCtrl in Shift then
  16320. if not (toReverseFullExpandHotKey in TreeOptions.MiscOptions) and (ssShift in Shift) then
  16321. FullExpand
  16322. else
  16323. FHeader.AutoFitColumns
  16324. else
  16325. if Assigned(FFocusedNode) and not (vsExpanded in FFocusedNode.States) then
  16326. ToggleNode(FFocusedNode);
  16327. end
  16328. else
  16329. DoStateChange([tsIncrementalSearchPending]);
  16330. VK_SUBTRACT:
  16331. if not (tsIncrementalSearching in FStates) then
  16332. begin
  16333. if ssCtrl in Shift then
  16334. if not (toReverseFullExpandHotKey in TreeOptions.MiscOptions) and (ssShift in Shift) then
  16335. FullCollapse
  16336. else
  16337. FHeader.RestoreColumns
  16338. else
  16339. if Assigned(FFocusedNode) and (vsExpanded in FFocusedNode.States) then
  16340. ToggleNode(FFocusedNode);
  16341. end
  16342. else
  16343. DoStateChange([tsIncrementalSearchPending]);
  16344. VK_MULTIPLY:
  16345. if not (tsIncrementalSearching in FStates) then
  16346. begin
  16347. if Assigned(FFocusedNode) then
  16348. FullExpand(FFocusedNode);
  16349. end
  16350. else
  16351. DoStateChange([tsIncrementalSearchPending]);
  16352. VK_DIVIDE:
  16353. if not (tsIncrementalSearching in FStates) then
  16354. begin
  16355. if Assigned(FFocusedNode) then
  16356. FullCollapse(FFocusedNode);
  16357. end
  16358. else
  16359. DoStateChange([tsIncrementalSearchPending]);
  16360. VK_ESCAPE: // cancel actions currently in progress
  16361. begin
  16362. if IsMouseSelecting then
  16363. begin
  16364. DoStateChange([], [tsDrawSelecting, tsDrawSelPending]);
  16365. Invalidate;
  16366. end
  16367. else
  16368. if IsEditing then
  16369. CancelEditNode;
  16370. end;
  16371. VK_SPACE:
  16372. if (toCheckSupport in FOptions.FMiscOptions) and Assigned(FFocusedNode) and
  16373. (FFocusedNode.CheckType <> ctNone) then
  16374. begin
  16375. if (FStates * [tsKeyCheckPending, tsMouseCheckPending] = []) and
  16376. not (vsDisabled in FFocusedNode.States) then
  16377. begin
  16378. with FFocusedNode^ do
  16379. NewCheckState := DetermineNextCheckState(CheckType, CheckState);
  16380. if DoChecking(FFocusedNode, NewCheckState) then
  16381. begin
  16382. DoStateChange([tsKeyCheckPending]);
  16383. FCheckNode := FFocusedNode;
  16384. FPendingCheckState := NewCheckState;
  16385. FCheckNode.CheckState := PressedState[FCheckNode.CheckState];
  16386. RepaintNode(FCheckNode);
  16387. end;
  16388. end;
  16389. end
  16390. else
  16391. DoStateChange([tsIncrementalSearchPending]);
  16392. VK_F1:
  16393. if Assigned(FOnGetHelpContext) then
  16394. begin
  16395. Context := 0;
  16396. if Assigned(FFocusedNode) then
  16397. begin
  16398. Node := FFocusedNode;
  16399. // Traverse the tree structure up to the root.
  16400. repeat
  16401. FOnGetHelpContext(Self, Node, IfThen(FFocusedColumn > NoColumn, FFocusedColumn, 0), Context);
  16402. Node := Node.Parent;
  16403. until (Node = FRoot) or (Context <> 0);
  16404. end;
  16405. // If no help context could be found try the tree's one or its parent's contexts.
  16406. ParentControl := Self;
  16407. while Assigned(ParentControl) and (Context = 0) do
  16408. begin
  16409. Context := ParentControl.HelpContext;
  16410. ParentControl := ParentControl.Parent;
  16411. end;
  16412. if Context <> 0 then
  16413. Application.HelpContext(Context);
  16414. end;
  16415. VK_APPS:
  16416. if Assigned(FFocusedNode) then
  16417. begin
  16418. R := GetDisplayRect(FFocusedNode, FFocusedColumn, True);
  16419. Offset := DoGetNodeWidth(FFocusedNode, FFocusedColumn);
  16420. if FFocusedColumn >= 0 then
  16421. begin
  16422. if Offset > FHeader.Columns[FFocusedColumn].Width then
  16423. Offset := FHeader.Columns[FFocusedColumn].Width;
  16424. end
  16425. else
  16426. begin
  16427. if Offset > ClientWidth then
  16428. Offset := ClientWidth;
  16429. end;
  16430. DoPopupMenu(FFocusedNode, FFocusedColumn, Point(R.Left + Offset div 2, (R.Top + R.Bottom) div 2));
  16431. end
  16432. else
  16433. DoPopupMenu(nil, FFocusedColumn, Point(-1, -1));
  16434. Ord('a'), Ord('A'):
  16435. if ssCtrl in Shift then
  16436. SelectAll(True)
  16437. else
  16438. DoStateChange([tsIncrementalSearchPending]);
  16439. else
  16440. begin
  16441. // Use the key for incremental search.
  16442. // Since we are dealing with Unicode all the time there should be a more sophisticated way
  16443. // of checking for valid characters for incremental search.
  16444. // This is available but would require to include a significant amount of Unicode character
  16445. // properties, so we stick with the simple space check.
  16446. if ((Shift * [ssCtrl, ssAlt] = []) or ((Shift * [ssCtrl, ssAlt] = [ssCtrl, ssAlt]))) and (CharCode >= 32) then
  16447. DoStateChange([tsIncrementalSearchPending]);
  16448. end;
  16449. end;
  16450. end;
  16451. end;
  16452. end;
  16453. end;
  16454. //----------------------------------------------------------------------------------------------------------------------
  16455. procedure TBaseVirtualTree.WMKeyUp(var Message: TWMKeyUp);
  16456. begin
  16457. inherited;
  16458. case Message.CharCode of
  16459. VK_SPACE:
  16460. if tsKeyCheckPending in FStates then
  16461. begin
  16462. DoStateChange([], [tskeyCheckPending]);
  16463. if FCheckNode = FFocusedNode then
  16464. DoCheckClick(FCheckNode, FPendingCheckState);
  16465. InvalidateNode(FCheckNode);
  16466. FCheckNode := nil;
  16467. end;
  16468. VK_TAB:
  16469. EnsureNodeFocused(); // Always select a node if the control gets the focus via TAB key, #237
  16470. end;
  16471. end;
  16472. //----------------------------------------------------------------------------------------------------------------------
  16473. procedure TBaseVirtualTree.WMKillFocus(var Msg: TWMKillFocus);
  16474. var
  16475. Form: TCustomForm;
  16476. Control: TWinControl;
  16477. Pos: TSmallPoint;
  16478. Unknown: IUnknown;
  16479. begin
  16480. inherited;
  16481. // Remove hint if shown currently.
  16482. if tsHint in Self.FStates then
  16483. Application.CancelHint;
  16484. // Stop wheel panning if active.
  16485. StopWheelPanning;
  16486. // Don't let any timer continue if the tree is no longer the active control (except change timers).
  16487. StopTimer(ExpandTimer);
  16488. StopTimer(EditTimer);
  16489. StopTimer(HeaderTimer);
  16490. StopTimer(ScrollTimer);
  16491. StopTimer(SearchTimer);
  16492. FSearchBuffer := '';
  16493. FLastSearchNode := nil;
  16494. DoStateChange([], [tsScrollPending, tsScrolling, tsEditPending, tsLeftButtonDown, tsRightButtonDown,
  16495. tsMiddleButtonDown, tsOLEDragPending, tsVCLDragPending, tsIncrementalSearching, tsNodeHeightTrackPending,
  16496. tsNodeHeightTracking]);
  16497. if (FSelectionCount > 0) or not (toGhostedIfUnfocused in FOptions.FPaintOptions) then
  16498. Invalidate
  16499. else
  16500. if Assigned(FFocusedNode) then
  16501. InvalidateNode(FFocusedNode);
  16502. // Workaround for wrapped non-VCL controls (like TWebBrowser), which do not use VCL mechanisms and
  16503. // leave the ActiveControl property in the wrong state, which causes trouble when the control is refocused.
  16504. Form := GetParentForm(Self);
  16505. if Assigned(Form) and (Form.ActiveControl = Self) then
  16506. begin
  16507. Cardinal(Pos) := GetMessagePos;
  16508. Control := FindVCLWindow(SmallPointToPoint(Pos));
  16509. // Every control derived from TOleControl has potentially the focus problem. In order to avoid including
  16510. // the OleCtrls unit (which will, among others, include Variants), which would allow to test for the TOleControl
  16511. // class, the IOleClientSite interface is used for the test, which is supported by TOleControl and a good indicator.
  16512. if Assigned(Control) and Control.GetInterface(IOleClientSite, Unknown) then
  16513. Form.ActiveControl := nil;
  16514. // For other classes the active control should not be modified. Otherwise you need two clicks to select it.
  16515. end;
  16516. end;
  16517. //----------------------------------------------------------------------------------------------------------------------
  16518. procedure TBaseVirtualTree.WMLButtonDblClk(var Message: TWMLButtonDblClk);
  16519. var
  16520. HitInfo: THitInfo;
  16521. begin
  16522. DoStateChange([tsLeftDblClick]);
  16523. inherited;
  16524. // get information about the hit
  16525. GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo);
  16526. HandleMouseDblClick(Message, HitInfo);
  16527. DoStateChange([], [tsLeftDblClick]);
  16528. end;
  16529. //----------------------------------------------------------------------------------------------------------------------
  16530. procedure TBaseVirtualTree.WMLButtonDown(var Message: TWMLButtonDown);
  16531. var
  16532. HitInfo: THitInfo;
  16533. begin
  16534. DoStateChange([tsLeftButtonDown]);
  16535. inherited;
  16536. // get information about the hit
  16537. GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo);
  16538. HandleMouseDown(Message, HitInfo);
  16539. end;
  16540. //----------------------------------------------------------------------------------------------------------------------
  16541. procedure TBaseVirtualTree.WMLButtonUp(var Message: TWMLButtonUp);
  16542. var
  16543. HitInfo: THitInfo;
  16544. begin
  16545. DoStateChange([], [tsLeftButtonDown, tsNodeHeightTracking, tsNodeHeightTrackPending]);
  16546. // get information about the hit
  16547. GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo);
  16548. HandleMouseUp(Message, HitInfo);
  16549. inherited;
  16550. end;
  16551. //----------------------------------------------------------------------------------------------------------------------
  16552. procedure TBaseVirtualTree.WMMButtonDblClk(var Message: TWMMButtonDblClk);
  16553. var
  16554. HitInfo: THitInfo;
  16555. begin
  16556. DoStateChange([tsMiddleDblClick]);
  16557. inherited;
  16558. // get information about the hit
  16559. if toMiddleClickSelect in FOptions.FSelectionOptions then
  16560. begin
  16561. GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo);
  16562. HandleMouseDblClick(Message, HitInfo);
  16563. end;
  16564. DoStateChange([], [tsMiddleDblClick]);
  16565. end;
  16566. //----------------------------------------------------------------------------------------------------------------------
  16567. procedure TBaseVirtualTree.WMMButtonDown(var Message: TWMMButtonDown);
  16568. var
  16569. HitInfo: THitInfo;
  16570. begin
  16571. DoStateChange([tsMiddleButtonDown]);
  16572. if FHeader.FStates = [] then
  16573. begin
  16574. inherited;
  16575. // Start wheel panning or scrolling if not already active, allowed and scrolling is useful at all.
  16576. if (toWheelPanning in FOptions.FMiscOptions) and ([tsWheelScrolling, tsWheelPanning] * FStates = []) and
  16577. ((Integer(FRangeX) > ClientWidth) or (Integer(FRangeY) > ClientHeight)) then
  16578. begin
  16579. FLastClickPos := SmallPointToPoint(Message.Pos);
  16580. StartWheelPanning(FLastClickPos);
  16581. end
  16582. else
  16583. begin
  16584. StopWheelPanning;
  16585. // Get information about the hit.
  16586. if toMiddleClickSelect in FOptions.FSelectionOptions then
  16587. begin
  16588. GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo);
  16589. HandleMouseDown(Message, HitInfo);
  16590. end;
  16591. end;
  16592. end;
  16593. end;
  16594. //----------------------------------------------------------------------------------------------------------------------
  16595. procedure TBaseVirtualTree.WMMButtonUp(var Message: TWMMButtonUp);
  16596. var
  16597. HitInfo: THitInfo;
  16598. begin
  16599. DoStateChange([], [tsMiddleButtonDown]);
  16600. // If wheel panning/scrolling is active and the mouse has not yet been moved then the user starts wheel auto scrolling.
  16601. // Indicate this by removing the panning flag. Otherwise (the mouse has moved meanwhile) stop panning.
  16602. if [tsWheelPanning, tsWheelScrolling] * FStates <> [] then
  16603. begin
  16604. if tsWheelScrolling in FStates then
  16605. DoStateChange([], [tsWheelPanning])
  16606. else
  16607. StopWheelPanning;
  16608. end
  16609. else
  16610. if FHeader.FStates = [] then
  16611. begin
  16612. inherited;
  16613. // get information about the hit
  16614. if toMiddleClickSelect in FOptions.FSelectionOptions then
  16615. begin
  16616. GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo);
  16617. HandleMouseUp(Message, HitInfo);
  16618. end;
  16619. end;
  16620. end;
  16621. //----------------------------------------------------------------------------------------------------------------------
  16622. procedure TBaseVirtualTree.WMNCCalcSize(var Message: TWMNCCalcSize);
  16623. begin
  16624. inherited;
  16625. with FHeader do
  16626. if hoVisible in FHeader.FOptions then
  16627. with Message.CalcSize_Params^ do
  16628. Inc(rgrc[0].Top, FHeight);
  16629. end;
  16630. //----------------------------------------------------------------------------------------------------------------------
  16631. procedure TBaseVirtualTree.WMNCDestroy(var Message: TWMNCDestroy);
  16632. // Used to release a reference of the drag manager. This is the only reliable way we get notified about
  16633. // window destruction, because of the automatic release of a window if its parent window is freed.
  16634. begin
  16635. InterruptValidation;
  16636. StopTimer(ChangeTimer);
  16637. StopTimer(StructureChangeTimer);
  16638. if not (csDesigning in ComponentState) and (toAcceptOLEDrop in FOptions.FMiscOptions) then
  16639. RevokeDragDrop(Handle);
  16640. // Clean up other stuff.
  16641. DeleteObject(FDottedBrush);
  16642. FDottedBrush := 0;
  16643. if tsInAnimation in FStates then
  16644. FHintWindowDestroyed := True; // Stop any pending animation.
  16645. inherited;
  16646. end;
  16647. //----------------------------------------------------------------------------------------------------------------------
  16648. procedure TBaseVirtualTree.WMNCHitTest(var Message: TWMNCHitTest);
  16649. begin
  16650. inherited;
  16651. if (hoVisible in FHeader.FOptions) and
  16652. FHeader.InHeader(ScreenToClient(SmallPointToPoint(Message.Pos))) then
  16653. Message.Result := HTBORDER;
  16654. end;
  16655. //----------------------------------------------------------------------------------------------------------------------
  16656. procedure TBaseVirtualTree.WMNCPaint(var Message: TRealWMNCPaint);
  16657. var
  16658. DC: HDC;
  16659. R: TRect;
  16660. Flags: DWORD;
  16661. ExStyle: Integer;
  16662. TempRgn: HRGN;
  16663. BorderWidth,
  16664. BorderHeight: Integer;
  16665. begin
  16666. if tsUseThemes in FStates then
  16667. begin
  16668. // If theming is enabled and the client edge border is set for the window then prevent the default window proc
  16669. // from painting the old border to avoid flickering.
  16670. ExStyle := GetWindowLong(Handle, GWL_EXSTYLE);
  16671. if (ExStyle and WS_EX_CLIENTEDGE) <> 0 then
  16672. begin
  16673. GetWindowRect(Handle, R);
  16674. // Determine width of the client edge.
  16675. BorderWidth := GetSystemMetrics(SM_CXEDGE);
  16676. BorderHeight := GetSystemMetrics(SM_CYEDGE);
  16677. InflateRect(R, -BorderWidth, -BorderHeight);
  16678. TempRgn := CreateRectRgnIndirect(R);
  16679. // Exclude the border from the message region if there is one. Otherwise just use the inflated
  16680. // window area region.
  16681. if Message.Rgn <> 1 then
  16682. CombineRgn(TempRgn, Message.Rgn, TempRgn, RGN_AND);
  16683. DefWindowProc(Handle, Message.Msg, WPARAM(TempRgn), 0);
  16684. DeleteObject(TempRgn);
  16685. end
  16686. else
  16687. DefaultHandler(Message);
  16688. end
  16689. else
  16690. DefaultHandler(Message);
  16691. Flags := DCX_CACHE or DCX_CLIPSIBLINGS or DCX_WINDOW or DCX_VALIDATE;
  16692. if (Message.Rgn = 1) then
  16693. DC := GetDCEx(Handle, 0, Flags)
  16694. else
  16695. DC := GetDCEx(Handle, Message.Rgn, Flags or DCX_INTERSECTRGN);
  16696. if DC <> 0 then
  16697. begin
  16698. if hoVisible in FHeader.FOptions then
  16699. begin
  16700. R := FHeaderRect;
  16701. FHeader.FColumns.PaintHeader(DC, R, -FEffectiveOffsetX);
  16702. end;
  16703. OriginalWMNCPaint(DC);
  16704. ReleaseDC(Handle, DC);
  16705. end;
  16706. if ((tsUseThemes in FStates) or VclStyleEnabled){$IF CompilerVersion >= 24} and (seBorder in StyleElements) {$IFEND} then
  16707. StyleServices.PaintBorder(Self, False);
  16708. end;
  16709. //----------------------------------------------------------------------------------------------------------------------
  16710. procedure TBaseVirtualTree.WMPaint(var Message: TWMPaint);
  16711. begin
  16712. if tsVCLDragging in FStates then
  16713. ImageList_DragShowNolock(False);
  16714. if csPaintCopy in ControlState then
  16715. FUpdateRect := ClientRect
  16716. else
  16717. GetUpdateRect(Handle, FUpdateRect, True);
  16718. inherited;
  16719. if tsVCLDragging in FStates then
  16720. ImageList_DragShowNolock(True);
  16721. end;
  16722. //----------------------------------------------------------------------------------------------------------------------
  16723. procedure TBaseVirtualTree.WMPaste(var Message: TWMPaste);
  16724. begin
  16725. PasteFromClipboard;
  16726. end;
  16727. //----------------------------------------------------------------------------------------------------------------------
  16728. procedure TBaseVirtualTree.WMPrint(var Message: TWMPrint);
  16729. // This message is sent to request that the tree draws itself to a given device context. This includes not only
  16730. // the client area but also the non-client area (header!).
  16731. begin
  16732. // Draw only if the window is visible or visibility is not required.
  16733. if ((Message.Flags and PRF_CHECKVISIBLE) = 0) or IsWindowVisible(Handle) then
  16734. Header.Columns.PaintHeader(Message.DC, FHeaderRect, -FEffectiveOffsetX);
  16735. inherited;
  16736. end;
  16737. //----------------------------------------------------------------------------------------------------------------------
  16738. procedure TBaseVirtualTree.WMPrintClient(var Message: TWMPrintClient);
  16739. var
  16740. Window: TRect;
  16741. Target: TPoint;
  16742. Canvas: TCanvas;
  16743. begin
  16744. // Draw only if the window is visible or visibility is not required.
  16745. if ((Message.Flags and PRF_CHECKVISIBLE) = 0) or IsWindowVisible(Handle) then
  16746. begin
  16747. // Determine area of the entire tree to be displayed in the control.
  16748. Window := ClientRect;
  16749. Target := Window.TopLeft;
  16750. // The Window rectangle is given in client coordinates. We have to convert it into
  16751. // a sliding window of the tree image.
  16752. OffsetRect(Window, FEffectiveOffsetX, -FOffsetY);
  16753. Canvas := TCanvas.Create;
  16754. try
  16755. Canvas.Handle := Message.DC;
  16756. PaintTree(Canvas, Window, Target, [poBackground, poDrawFocusRect, poDrawDropMark, poDrawSelection, poGridLines]);
  16757. finally
  16758. Canvas.Handle := 0;
  16759. Canvas.Free;
  16760. end;
  16761. end;
  16762. end;
  16763. //----------------------------------------------------------------------------------------------------------------------
  16764. procedure TBaseVirtualTree.WMRButtonDblClk(var Message: TWMRButtonDblClk);
  16765. var
  16766. HitInfo: THitInfo;
  16767. begin
  16768. DoStateChange([tsRightDblClick]);
  16769. inherited;
  16770. // get information about the hit
  16771. if toMiddleClickSelect in FOptions.FSelectionOptions then
  16772. begin
  16773. GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo);
  16774. HandleMouseDblClick(Message, HitInfo);
  16775. end;
  16776. DoStateChange([], [tsRightDblClick]);
  16777. end;
  16778. //----------------------------------------------------------------------------------------------------------------------
  16779. procedure TBaseVirtualTree.WMRButtonDown(var Message: TWMRButtonDown);
  16780. var
  16781. HitInfo: THitInfo;
  16782. begin
  16783. DoStateChange([tsRightButtonDown]);
  16784. if FHeader.FStates = [] then
  16785. begin
  16786. inherited;
  16787. // get information about the hit
  16788. if toRightClickSelect in FOptions.FSelectionOptions then
  16789. begin
  16790. GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo);
  16791. HandleMouseDown(Message, HitInfo);
  16792. end;
  16793. end;
  16794. end;
  16795. //----------------------------------------------------------------------------------------------------------------------
  16796. procedure TBaseVirtualTree.WMRButtonUp(var Message: TWMRButtonUp);
  16797. // handle right click selection and node specific popup menu
  16798. var
  16799. HitInfo: THitInfo;
  16800. begin
  16801. DoStateChange([], [tsPopupMenuShown, tsRightButtonDown]);
  16802. if FHeader.FStates = [] then
  16803. begin
  16804. Application.CancelHint;
  16805. if IsMouseSelecting and Assigned(PopupMenu) then
  16806. begin
  16807. // Reset selection state already here, before the inherited handler opens the default menu.
  16808. DoStateChange([], [tsDrawSelecting, tsDrawSelPending]);
  16809. Invalidate;
  16810. end;
  16811. inherited;
  16812. // get information about the hit
  16813. GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo);
  16814. if toRightClickSelect in FOptions.FSelectionOptions then
  16815. HandleMouseUp(Message, HitInfo);
  16816. if not Assigned(PopupMenu) then
  16817. DoPopupMenu(HitInfo.HitNode, HitInfo.HitColumn, Point(Message.XPos, Message.YPos));
  16818. end;
  16819. end;
  16820. //----------------------------------------------------------------------------------------------------------------------
  16821. procedure TBaseVirtualTree.WMSetCursor(var Message: TWMSetCursor);
  16822. // Sets the hot node mouse cursor for the tree. Cursor changes for the header are handled in Header.HandleMessage.
  16823. var
  16824. NewCursor: TCursor;
  16825. HitInfo: THitInfo;
  16826. P: TPoint;
  16827. Node: PVirtualNode;
  16828. begin
  16829. with Message do
  16830. begin
  16831. // Feature: design-time header #415
  16832. // Allow header to handle cursor and return control's default if it did nothing
  16833. if (CursorWnd = Handle) and
  16834. ([tsWheelPanning, tsWheelScrolling] * FStates = []) then
  16835. begin
  16836. if not FHeader.HandleMessage(TMessage(Message)) then
  16837. begin
  16838. // Apply own cursors only if there is no global cursor set.
  16839. if Screen.Cursor = crDefault then
  16840. begin
  16841. // node resizing and hot tracking - for run-time only
  16842. if not (csDesigning in ComponentState) then
  16843. begin
  16844. NewCursor := crDefault;
  16845. if (toNodeHeightResize in FOptions.FMiscOptions) then
  16846. begin
  16847. GetCursorPos(P);
  16848. P := ScreenToClient(P);
  16849. GetHitTestInfoAt(P.X, P.Y, True, HitInfo);
  16850. if (hiOnItem in HitInfo.HitPositions) and
  16851. ([hiUpperSplitter, hiLowerSplitter] * HitInfo.HitPositions <> []) then
  16852. begin
  16853. if hiUpperSplitter in HitInfo.HitPositions then
  16854. Node := GetPreviousVisible(HitInfo.HitNode, True)
  16855. else
  16856. Node := HitInfo.HitNode;
  16857. if CanSplitterResizeNode(P, Node, HitInfo.HitColumn) then
  16858. NewCursor := crVertSplit;
  16859. end;
  16860. end;
  16861. if (NewCursor = crDefault) then
  16862. if (toHotTrack in FOptions.PaintOptions) and Assigned(FCurrentHotNode) and (FHotCursor <> crDefault) then
  16863. NewCursor := FHotCursor
  16864. else
  16865. NewCursor := Cursor;
  16866. DoGetCursor(NewCursor);
  16867. end
  16868. else
  16869. NewCursor := Cursor;
  16870. Windows.SetCursor(Screen.Cursors[NewCursor]);
  16871. Message.Result := 1;
  16872. end
  16873. else
  16874. inherited;
  16875. end;
  16876. end
  16877. else
  16878. inherited;
  16879. end;
  16880. end;
  16881. //----------------------------------------------------------------------------------------------------------------------
  16882. procedure TBaseVirtualTree.WMSetFocus(var Msg: TWMSetFocus);
  16883. begin
  16884. inherited;
  16885. if (FSelectionCount > 0) or not (toGhostedIfUnfocused in FOptions.FPaintOptions) then
  16886. Invalidate;
  16887. end;
  16888. //----------------------------------------------------------------------------------------------------------------------
  16889. procedure TBaseVirtualTree.WMSize(var Message: TWMSize);
  16890. begin
  16891. inherited;
  16892. // Need to update scroll bars here. This will cause a recursion because of the change of the client area
  16893. // when changing a scrollbar. Usually this is no problem since with the second level recursion no change of the
  16894. // window size happens (the same values for the scrollbars are set, which shouldn't cause a window size change).
  16895. // Appearently, this applies not to all systems, however.
  16896. if HandleAllocated and ([tsSizing, tsWindowCreating] * FStates = []) and (ClientHeight > 0) then
  16897. try
  16898. DoStateChange([tsSizing]);
  16899. // This call will invalidate the entire non-client area which needs recalculation on resize.
  16900. FHeader.RescaleHeader;
  16901. FHeader.UpdateSpringColumns;
  16902. UpdateScrollBars(True);
  16903. if (tsEditing in FStates) and not FHeader.UseColumns then
  16904. UpdateEditBounds;
  16905. finally
  16906. DoStateChange([], [tsSizing]);
  16907. end;
  16908. end;
  16909. //----------------------------------------------------------------------------------------------------------------------
  16910. procedure TBaseVirtualTree.WMThemeChanged(var Message: TMessage);
  16911. begin
  16912. inherited;
  16913. if StyleServices.Enabled and (toThemeAware in TreeOptions.PaintOptions) then
  16914. DoStateChange([tsUseThemes])
  16915. else
  16916. DoStateChange([], [tsUseThemes]);
  16917. // Updating the visuals here will not work correctly. Therefore we postpone
  16918. // the update by using a timer.
  16919. if not FChangingTheme then
  16920. SetTimer(Handle, ThemeChangedTimer, ThemeChangedTimerDelay, nil);
  16921. FChangingTheme := False;
  16922. end;
  16923. //----------------------------------------------------------------------------------------------------------------------
  16924. procedure TBaseVirtualTree.WMTimer(var Message: TWMTimer);
  16925. // centralized timer handling happens here
  16926. begin
  16927. with Message do
  16928. begin
  16929. case TimerID of
  16930. ExpandTimer:
  16931. DoDragExpand;
  16932. EditTimer:
  16933. DoEdit;
  16934. ScrollTimer:
  16935. begin
  16936. if tsScrollPending in FStates then
  16937. begin
  16938. Application.CancelHint;
  16939. // Scroll delay has elapsed, set to normal scroll interval now.
  16940. SetTimer(Handle, ScrollTimer, FAutoScrollInterval, nil);
  16941. DoStateChange([tsScrolling], [tsScrollPending]);
  16942. end;
  16943. DoTimerScroll;
  16944. end;
  16945. ChangeTimer:
  16946. DoChange(FLastChangedNode);
  16947. StructureChangeTimer:
  16948. DoStructureChange(FLastStructureChangeNode, FLastStructureChangeReason);
  16949. SearchTimer:
  16950. begin
  16951. // When this event triggers then the user did not pressed any key for the specified timeout period.
  16952. // Hence incremental searching is stopped.
  16953. DoStateChange([], [tsIncrementalSearching]);
  16954. StopTimer(SearchTimer);
  16955. FSearchBuffer := '';
  16956. FLastSearchNode := nil;
  16957. end;
  16958. ThemeChangedTimer:
  16959. begin
  16960. StopTimer(ThemeChangedTimer);
  16961. RecreateWnd;
  16962. end;
  16963. end;
  16964. end;
  16965. end;
  16966. //----------------------------------------------------------------------------------------------------------------------
  16967. procedure TBaseVirtualTree.WMVScroll(var Message: TWMVScroll);
  16968. //--------------- local functions -------------------------------------------
  16969. function GetRealScrollPosition: Integer;
  16970. var
  16971. SI: TScrollInfo;
  16972. Code: Integer;
  16973. begin
  16974. SI.cbSize := SizeOf(TScrollInfo);
  16975. SI.fMask := SIF_TRACKPOS;
  16976. Code := SB_VERT;
  16977. GetScrollInfo(Handle, Code, SI);
  16978. Result := SI.nTrackPos;
  16979. end;
  16980. //--------------- end local functions ---------------------------------------
  16981. begin
  16982. case Message.ScrollCode of
  16983. SB_BOTTOM:
  16984. SetOffsetY(-Integer(FRoot.TotalHeight));
  16985. SB_ENDSCROLL:
  16986. begin
  16987. DoStateChange([], [tsThumbTracking]);
  16988. // Avoiding to adjust the horizontal scroll position while tracking makes scrolling much smoother
  16989. // but we need to adjust the final position here then.
  16990. UpdateScrollBars(True);
  16991. // Really weird invalidation needed here (and I do it only because it happens so rarely), because
  16992. // when showing the horizontal scrollbar while scrolling down using the down arrow button,
  16993. // the button will be repainted on mouse up (at the wrong place in the far right lower corner)...
  16994. RedrawWindow(Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE or RDW_NOERASE or RDW_NOCHILDREN);
  16995. end;
  16996. SB_LINEUP:
  16997. SetOffsetY(FOffsetY + FScrollBarOptions.FIncrementY);
  16998. SB_LINEDOWN:
  16999. SetOffsetY(FOffsetY - FScrollBarOptions.FIncrementY);
  17000. SB_PAGEUP:
  17001. SetOffsetY(FOffsetY + ClientHeight);
  17002. SB_PAGEDOWN:
  17003. SetOffsetY(FOffsetY - ClientHeight);
  17004. SB_THUMBPOSITION,
  17005. SB_THUMBTRACK:
  17006. begin
  17007. DoStateChange([tsThumbTracking]);
  17008. SetOffsetY(-GetRealScrollPosition);
  17009. end;
  17010. SB_TOP:
  17011. SetOffsetY(0);
  17012. end;
  17013. Message.Result := 0;
  17014. end;
  17015. //----------------------------------------------------------------------------------------------------------------------
  17016. procedure TBaseVirtualTree.AddToSelection(Node: PVirtualNode);
  17017. var
  17018. Changed: Boolean;
  17019. begin
  17020. if not FSelectionLocked then
  17021. begin
  17022. Assert(Assigned(Node), 'Node must not be nil!');
  17023. FSingletonNodeArray[0] := Node;
  17024. Changed := InternalAddToSelection(FSingletonNodeArray, 1, False);
  17025. if Changed then
  17026. begin
  17027. InvalidateNode(Node);
  17028. Change(Node);
  17029. end;
  17030. end;
  17031. end;
  17032. //----------------------------------------------------------------------------------------------------------------------
  17033. procedure TBaseVirtualTree.AddToSelection(const NewItems: TNodeArray; NewLength: Integer; ForceInsert: Boolean = False);
  17034. // Adds the given items all at once into the current selection array. NewLength is the amount of
  17035. // nodes to add (necessary to allow NewItems to be larger than the actual used entries).
  17036. // ForceInsert is True if nodes must be inserted without consideration of level select constraint or
  17037. // already set selected flags (e.g. when loading from stream).
  17038. // Note: In the case ForceInsert is True the caller is responsible for making sure the new nodes aren't already in the
  17039. // selection array!
  17040. var
  17041. Changed: Boolean;
  17042. begin
  17043. Changed := InternalAddToSelection(NewItems, NewLength, ForceInsert);
  17044. if Changed then
  17045. begin
  17046. if NewLength = 1 then
  17047. begin
  17048. InvalidateNode(NewItems[0]);
  17049. Change(NewItems[0]);
  17050. end
  17051. else
  17052. begin
  17053. Invalidate;
  17054. Change(nil);
  17055. end;
  17056. end;
  17057. end;
  17058. //----------------------------------------------------------------------------------------------------------------------
  17059. procedure TBaseVirtualTree.AdjustImageBorder(Images: TCustomImageList; BidiMode: TBidiMode; VAlign: Integer; var R: TRect;
  17060. var ImageInfo: TVTImageInfo);
  17061. // Depending on the width of the image list as well as the given bidi mode R must be adjusted.
  17062. begin
  17063. if BidiMode = bdLeftToRight then
  17064. begin
  17065. ImageInfo.XPos := R.Left;
  17066. Inc(R.Left, Images.Width + 2);
  17067. end
  17068. else
  17069. begin
  17070. ImageInfo.XPos := R.Right - Images.Width;
  17071. Dec(R.Right, Images.Width + 2);
  17072. end;
  17073. ImageInfo.YPos := R.Top + VAlign - Images.Height div 2;
  17074. end;
  17075. //----------------------------------------------------------------------------------------------------------------------
  17076. procedure TBaseVirtualTree.AdjustPaintCellRect(var PaintInfo: TVTPaintInfo; var NextNonEmpty: TColumnIndex);
  17077. // Used in descendants to modify the paint rectangle of the current column while painting a certain node.
  17078. begin
  17079. // Since cells are always drawn from left to right the next column index is independent of the
  17080. // bidi mode, but not the column borders, which might change depending on the cell's content.
  17081. NextNonEmpty := FHeader.FColumns.GetNextVisibleColumn(PaintInfo.Column);
  17082. end;
  17083. //----------------------------------------------------------------------------------------------------------------------
  17084. procedure TBaseVirtualTree.AdjustPanningCursor(X, Y: Integer);
  17085. // Triggered by a mouse move when wheel panning/scrolling is active.
  17086. // Loads the proper cursor which indicates into which direction scrolling is done.
  17087. var
  17088. Name: string;
  17089. NewCursor: HCURSOR;
  17090. ScrollHorizontal,
  17091. ScrollVertical: Boolean;
  17092. begin
  17093. ScrollHorizontal := Integer(FRangeX) > ClientWidth;
  17094. ScrollVertical := Integer(FRangeY) > ClientHeight;
  17095. if (Abs(X - FLastClickPos.X) < 8) and (Abs(Y - FLastClickPos.Y) < 8) then
  17096. begin
  17097. // Mouse is in the neutral zone.
  17098. if ScrollHorizontal then
  17099. begin
  17100. if ScrollVertical then
  17101. Name := 'VT_MOVEALL'
  17102. else
  17103. Name := 'VT_MOVEEW';
  17104. end
  17105. else
  17106. Name := 'VT_MOVENS';
  17107. end
  17108. else
  17109. begin
  17110. // One of 8 directions applies: north, north-east, east, south-east, south, south-west, west and north-west.
  17111. // Check also if scrolling in the particular direction is possible.
  17112. if ScrollVertical and ScrollHorizontal then
  17113. begin
  17114. // All directions allowed.
  17115. if X - FLastClickPos.X < -8 then
  17116. begin
  17117. // Left hand side.
  17118. if Y - FLastClickPos.Y < -8 then
  17119. Name := 'VT_MOVENW'
  17120. else
  17121. if Y - FLastClickPos.Y > 8 then
  17122. Name := 'VT_MOVESW'
  17123. else
  17124. Name := 'VT_MOVEW';
  17125. end
  17126. else
  17127. if X - FLastClickPos.X > 8 then
  17128. begin
  17129. // Right hand side.
  17130. if Y - FLastClickPos.Y < -8 then
  17131. Name := 'VT_MOVENE'
  17132. else
  17133. if Y - FLastClickPos.Y > 8 then
  17134. Name := 'VT_MOVESE'
  17135. else
  17136. Name := 'VT_MOVEE';
  17137. end
  17138. else
  17139. begin
  17140. // Up or down.
  17141. if Y < FLastClickPos.Y then
  17142. Name := 'VT_MOVEN'
  17143. else
  17144. Name := 'VT_MOVES';
  17145. end;
  17146. end
  17147. else
  17148. if ScrollHorizontal then
  17149. begin
  17150. // Only horizontal movement allowed.
  17151. if X < FLastClickPos.X then
  17152. Name := 'VT_MOVEW'
  17153. else
  17154. Name := 'VT_MOVEE';
  17155. end
  17156. else
  17157. begin
  17158. // Only vertical movement allowed.
  17159. if Y < FLastClickPos.Y then
  17160. Name := 'VT_MOVEN'
  17161. else
  17162. Name := 'VT_MOVES';
  17163. end;
  17164. end;
  17165. // Now load the cursor and apply it.
  17166. NewCursor := LoadCursor(HInstance, PChar(Name));
  17167. if FPanningCursor <> NewCursor then
  17168. begin
  17169. DeleteObject(FPanningCursor);
  17170. FPanningCursor := NewCursor;
  17171. Windows.SetCursor(FPanningCursor);
  17172. end
  17173. else
  17174. DeleteObject(NewCursor);
  17175. end;
  17176. //----------------------------------------------------------------------------------------------------------------------
  17177. procedure TBaseVirtualTree.AdviseChangeEvent(StructureChange: Boolean; Node: PVirtualNode; Reason: TChangeReason);
  17178. // Used to register a delayed change event. If StructureChange is False then we have a selection change event (without
  17179. // a specific reason) otherwise it is a structure change.
  17180. begin
  17181. if StructureChange then
  17182. begin
  17183. if tsStructureChangePending in FStates then
  17184. StopTimer(StructureChangeTimer)
  17185. else
  17186. DoStateChange([tsStructureChangePending]);
  17187. FLastStructureChangeNode := Node;
  17188. if FLastStructureChangeReason = crIgnore then
  17189. FLastStructureChangeReason := Reason
  17190. else
  17191. if Reason <> crIgnore then
  17192. FLastStructureChangeReason := crAccumulated;
  17193. end
  17194. else
  17195. begin
  17196. if tsChangePending in FStates then
  17197. StopTimer(ChangeTimer)
  17198. else
  17199. DoStateChange([tsChangePending]);
  17200. FLastChangedNode := Node;
  17201. end;
  17202. end;
  17203. //----------------------------------------------------------------------------------------------------------------------
  17204. function TBaseVirtualTree.AllocateInternalDataArea(Size: Cardinal): Cardinal;
  17205. // Simple registration method to be called by each descendant to claim their internal data area.
  17206. // Result is the offset from the begin of the node to the internal data area of the calling tree class.
  17207. begin
  17208. Assert((FRoot = nil) or (FRoot.ChildCount = 0), 'Internal data allocation must be done before any node is created.');
  17209. Result := TreeNodeSize + FTotalInternalDataSize;
  17210. Inc(FTotalInternalDataSize, (Size + (SizeOf(Pointer) - 1)) and not (SizeOf(Pointer) - 1));
  17211. InitRootNode(Result);
  17212. end;
  17213. //----------------------------------------------------------------------------------------------------------------------
  17214. procedure TBaseVirtualTree.Animate(Steps, Duration: Cardinal; Callback: TVTAnimationCallback; Data: Pointer);
  17215. // This method does the calculation part of an animation as used for node toggling and hint animations.
  17216. // Steps is the maximum amount of animation steps to do and Duration determines the milliseconds the animation
  17217. // has to run. Callback is a task specific method which is called in the loop for every step and Data is simply
  17218. // something to pass on to the callback.
  17219. // The callback is called with the current step, the current step size and the Data parameter. Since the step amount
  17220. // as well as the step size are possibly adjusted during the animation, it is impossible to determine if the current
  17221. // step is the last step, even if the original step amount is known. To solve this problem the callback will be
  17222. // called after the loop has finished with a step size of 0 indicating so to execute any post processing.
  17223. var
  17224. StepSize,
  17225. RemainingTime,
  17226. RemainingSteps,
  17227. NextTimeStep,
  17228. CurrentStep,
  17229. StartTime,
  17230. CurrentTime: Cardinal;
  17231. begin
  17232. if not (tsInAnimation in FStates) and (Duration > 0) then
  17233. begin
  17234. DoStateChange([tsInAnimation]);
  17235. try
  17236. RemainingTime := Duration;
  17237. RemainingSteps := Steps;
  17238. // Determine the initial step size which is either 1 if the needed steps are less than the number of
  17239. // steps possible given by the duration or > 1 otherwise.
  17240. StepSize := Round(Max(1, RemainingSteps / Duration));
  17241. RemainingSteps := RemainingSteps div StepSize;
  17242. CurrentStep := 0;
  17243. while (RemainingSteps > 0) and (RemainingTime > 0) and not Application.Terminated do
  17244. begin
  17245. StartTime := timeGetTime;
  17246. NextTimeStep := StartTime + RemainingTime div RemainingSteps;
  17247. if not Callback(CurrentStep, StepSize, Data) then
  17248. Break;
  17249. // Keep duration for this step for rest calculation.
  17250. CurrentTime := timeGetTime;
  17251. // Wait until the calculated time has been reached.
  17252. while CurrentTime < NextTimeStep do
  17253. CurrentTime := timeGetTime;
  17254. // Subtract the time this step really needed.
  17255. if RemainingTime >= CurrentTime - StartTime then
  17256. begin
  17257. Dec(RemainingTime, CurrentTime - StartTime);
  17258. Dec(RemainingSteps);
  17259. end
  17260. else
  17261. begin
  17262. RemainingTime := 0;
  17263. RemainingSteps := 0;
  17264. end;
  17265. // If the remaining time per step is less than one time step then we have to decrease the
  17266. // step count and increase the step size.
  17267. if (RemainingSteps > 0) and ((RemainingTime div RemainingSteps) < 1) then
  17268. begin
  17269. repeat
  17270. Inc(StepSize);
  17271. RemainingSteps := RemainingTime div StepSize;
  17272. until (RemainingSteps <= 0) or ((RemainingTime div RemainingSteps) >= 1);
  17273. end;
  17274. CurrentStep := Cardinal(Steps) - RemainingSteps;
  17275. end;
  17276. if not Application.Terminated then
  17277. Callback(0, 0, Data);
  17278. finally
  17279. DoStateChange([], [tsCancelHintAnimation, tsInAnimation]);
  17280. end;
  17281. end;
  17282. end;
  17283. //----------------------------------------------------------------------------------------------------------------------
  17284. procedure TBaseVirtualTree.StartOperation(OperationKind: TVTOperationKind);
  17285. // Called to indicate that a long-running operation has been started.
  17286. begin
  17287. Inc(FOperationCount);
  17288. DoStartOperation(OperationKind);
  17289. if FOperationCount = 1 then
  17290. FOperationCanceled := False;
  17291. end;
  17292. //----------------------------------------------------------------------------------------------------------------------
  17293. function TBaseVirtualTree.CalculateSelectionRect(X, Y: Integer): Boolean;
  17294. // Recalculates old and new selection rectangle given that X, Y are new mouse coordinates.
  17295. // Returns True if there was a change since the last call.
  17296. var
  17297. MaxValue: Integer;
  17298. begin
  17299. if tsDrawSelecting in FStates then
  17300. FLastSelRect := FNewSelRect;
  17301. FNewSelRect.BottomRight := Point(X + FEffectiveOffsetX, Y - FOffsetY);
  17302. if FNewSelRect.Right < 0 then
  17303. FNewSelRect.Right := 0;
  17304. if FNewSelRect.Bottom < 0 then
  17305. FNewSelRect.Bottom := 0;
  17306. MaxValue := ClientWidth;
  17307. if FRangeX > Cardinal(MaxValue) then
  17308. MaxValue := FRangeX;
  17309. if FNewSelRect.Right > MaxValue then
  17310. FNewSelRect.Right := MaxValue;
  17311. MaxValue := ClientHeight;
  17312. if FRangeY > Cardinal(MaxValue) then
  17313. MaxValue := FRangeY;
  17314. if FNewSelRect.Bottom > MaxValue then
  17315. FNewSelRect.Bottom := MaxValue;
  17316. Result := not CompareMem(@FLastSelRect, @FNewSelRect, SizeOf(FNewSelRect));
  17317. end;
  17318. //----------------------------------------------------------------------------------------------------------------------
  17319. function TBaseVirtualTree.CanAutoScroll: Boolean;
  17320. // Determines if auto scrolling is currently allowed.
  17321. var
  17322. IsDropTarget: Boolean;
  17323. IsDrawSelecting: Boolean;
  17324. IsWheelPanning: Boolean;
  17325. begin
  17326. // Don't scroll the client area if the header is currently doing tracking or dragging.
  17327. // Do auto scroll only if there is a draw selection in progress or the tree is the current drop target or
  17328. // wheel panning/scrolling is active.
  17329. IsDropTarget := Assigned(FDragManager) and DragManager.IsDropTarget;
  17330. IsDrawSelecting := [tsDrawSelPending, tsDrawSelecting] * FStates <> [];
  17331. IsWheelPanning := [tsWheelPanning, tsWheelScrolling] * FStates <> [];
  17332. Result := ((toAutoScroll in FOptions.FAutoOptions) or IsWheelPanning) and
  17333. (FHeader.FStates = []) and (IsDrawSelecting or IsDropTarget or (tsVCLDragging in FStates) or IsWheelPanning);
  17334. end;
  17335. //----------------------------------------------------------------------------------------------------------------------
  17336. function TBaseVirtualTree.CanShowDragImage: Boolean;
  17337. // Determines whether a drag image should be shown.
  17338. begin
  17339. Result := FDragImageKind <> diNoImage;
  17340. end;
  17341. //----------------------------------------------------------------------------------------------------------------------
  17342. function TBaseVirtualTree.CanSplitterResizeNode(P: TPoint; Node: PVirtualNode; Column: TColumnIndex): Boolean;
  17343. begin
  17344. Result := (toNodeHeightResize in FOptions.FMiscOptions) and Assigned(Node) and (Node <> FRoot) and
  17345. (Column > NoColumn) and (coFixed in FHeader.FColumns[Column].FOptions);
  17346. DoCanSplitterResizeNode(P, Node, Column, Result);
  17347. end;
  17348. //----------------------------------------------------------------------------------------------------------------------
  17349. procedure TBaseVirtualTree.Change(Node: PVirtualNode);
  17350. begin
  17351. AdviseChangeEvent(False, Node, crIgnore);
  17352. if FUpdateCount = 0 then
  17353. begin
  17354. if (FChangeDelay > 0) and not (tsSynchMode in FStates) then
  17355. SetTimer(Handle, ChangeTimer, FChangeDelay, nil)
  17356. else
  17357. DoChange(Node);
  17358. end;
  17359. end;
  17360. //----------------------------------------------------------------------------------------------------------------------
  17361. procedure TBaseVirtualTree.ChangeScale(M, D: Integer);
  17362. begin
  17363. inherited;
  17364. if (M <> D) and (toAutoChangeScale in FOptions.FAutoOptions) then
  17365. begin
  17366. SetDefaultNodeHeight(MulDiv(FDefaultNodeHeight, M, D));
  17367. FHeader.ChangeScale(M, D);
  17368. end;
  17369. end;
  17370. //----------------------------------------------------------------------------------------------------------------------
  17371. procedure TBaseVirtualTree.ChangeTreeStatesAsync(EnterStates, LeaveStates: TChangeStates);
  17372. begin
  17373. if (Self.HandleAllocated) then
  17374. SendMessage(Self.Handle, WM_CHANGESTATE, Byte(EnterStates), Byte(LeaveStates));
  17375. end;
  17376. //----------------------------------------------------------------------------------------------------------------------
  17377. function TBaseVirtualTree.CheckParentCheckState(Node: PVirtualNode; NewCheckState: TCheckState): Boolean;
  17378. // Checks all siblings of node to determine which check state Node's parent must get.
  17379. var
  17380. CheckCount,
  17381. BoxCount: Cardinal;
  17382. PartialCheck: Boolean;
  17383. Run: PVirtualNode;
  17384. begin
  17385. CheckCount := 0;
  17386. BoxCount := 0;
  17387. PartialCheck := False;
  17388. Run := Node.Parent.FirstChild;
  17389. while Assigned(Run) do
  17390. begin
  17391. if Run = Node then
  17392. begin
  17393. // The given node cannot be checked because it does not yet have its new check state (as this depends
  17394. // on the outcome of this method). Instead NewCheckState is used as this contains the new state the node
  17395. // will get if this method returns True.
  17396. if Run.CheckType in [ctCheckBox, ctTriStateCheckBox] then
  17397. begin
  17398. Inc(BoxCount);
  17399. if NewCheckState in [csCheckedNormal, csCheckedPressed] then
  17400. Inc(CheckCount);
  17401. PartialCheck := PartialCheck or (NewCheckState = csMixedNormal);
  17402. end;
  17403. end
  17404. else
  17405. if Run.CheckType in [ctCheckBox, ctTriStateCheckBox] then
  17406. begin
  17407. Inc(BoxCount);
  17408. if Run.CheckState in [csCheckedNormal, csCheckedPressed] then
  17409. Inc(CheckCount);
  17410. PartialCheck := PartialCheck or (Run.CheckState = csMixedNormal);
  17411. end;
  17412. Run := Run.NextSibling;
  17413. end;
  17414. if (CheckCount = 0) and not PartialCheck then
  17415. NewCheckState := csUncheckedNormal
  17416. else
  17417. if CheckCount < BoxCount then
  17418. NewCheckState := csMixedNormal
  17419. else
  17420. NewCheckState := csCheckedNormal;
  17421. Node := Node.Parent;
  17422. Result := DoChecking(Node, NewCheckState);
  17423. if Result then
  17424. begin
  17425. DoCheckClick(Node, NewCheckState);
  17426. // Recursively adjust parent of parent.
  17427. // This is already done in the function DoCheckClick() called in the above line
  17428. // We revent unnecessary upward recursion by commenting this code.
  17429. // with Node^ do
  17430. // begin
  17431. // if not (vsInitialized in Parent.States) then
  17432. // InitNode(Parent);
  17433. // if ([vsChecking, vsDisabled] * Parent.States = []) and (Parent <> FRoot) and
  17434. // (Parent.CheckType = ctTriStateCheckBox) then
  17435. // Result := CheckParentCheckState(Node, NewCheckState);
  17436. // end;
  17437. end;
  17438. end;
  17439. //----------------------------------------------------------------------------------------------------------------------
  17440. procedure TBaseVirtualTree.ClearTempCache;
  17441. // make sure the temporary node cache is in a reliable state
  17442. begin
  17443. FTempNodeCache := nil;
  17444. FTempNodeCount := 0;
  17445. end;
  17446. //----------------------------------------------------------------------------------------------------------------------
  17447. function TBaseVirtualTree.ColumnIsEmpty(Node: PVirtualNode; Column: TColumnIndex): Boolean;
  17448. // Returns True if the given column is to be considered as being empty. This will usually be determined by
  17449. // descendants as the base tree implementation has not enough information to decide.
  17450. begin
  17451. Result := True;
  17452. if Assigned(FOnGetCellIsEmpty) then
  17453. FOnGetCellIsEmpty(Self, Node, Column, Result);
  17454. end;
  17455. //----------------------------------------------------------------------------------------------------------------------
  17456. function TBaseVirtualTree.ComputeRTLOffset(ExcludeScrollBar: Boolean): Integer;
  17457. // Computes the horizontal offset needed when all columns are automatically right aligned (in RTL bidi mode).
  17458. // ExcludeScrollBar determines if the left-hand vertical scrollbar is to be included (if visible) or not.
  17459. var
  17460. HeaderWidth: Integer;
  17461. ScrollBarVisible: Boolean;
  17462. begin
  17463. ScrollBarVisible := (Integer(FRangeY) > ClientHeight) and (ScrollBarOptions.ScrollBars in [ssVertical, ssBoth]);
  17464. if ScrollBarVisible then
  17465. Result := GetSystemMetrics(SM_CXVSCROLL)
  17466. else
  17467. Result := 0;
  17468. // Make everything right aligned.
  17469. HeaderWidth := FHeaderRect.Right - FHeaderRect.Left;
  17470. if Integer(FRangeX) + Result <= HeaderWidth then
  17471. Result := HeaderWidth - Integer(FRangeX);
  17472. // Otherwise take only left-hand vertical scrollbar into account.
  17473. if ScrollBarVisible and ExcludeScrollBar then
  17474. Dec(Result, GetSystemMetrics(SM_CXVSCROLL));
  17475. end;
  17476. //----------------------------------------------------------------------------------------------------------------------
  17477. function TBaseVirtualTree.CountLevelDifference(Node1, Node2: PVirtualNode): Integer;
  17478. // This method counts how many indentation levels the given nodes are apart. If both nodes have the same parent then the
  17479. // difference is 0 otherwise the result is basically GetNodeLevel(Node2) - GetNodeLevel(Node1), but with sign.
  17480. // If the result is negative then Node2 is less intended than Node1.
  17481. var
  17482. Level1, Level2: Integer;
  17483. begin
  17484. Assert(Assigned(Node1) and Assigned(Node2), 'Both nodes must be Assigned.');
  17485. Level1 := 0;
  17486. while Node1.Parent <> FRoot do
  17487. begin
  17488. Inc(Level1);
  17489. Node1 := Node1.Parent;
  17490. end;
  17491. Level2 := 0;
  17492. while Node2.Parent <> FRoot do
  17493. begin
  17494. Inc(Level2);
  17495. Node2 := Node2.Parent;
  17496. end;
  17497. Result := Level2 - Level1;
  17498. end;
  17499. //----------------------------------------------------------------------------------------------------------------------
  17500. function TBaseVirtualTree.CountVisibleChildren(Node: PVirtualNode): Cardinal;
  17501. // Returns the number of visible child nodes of the given node.
  17502. begin
  17503. Result := 0;
  17504. // The node's direct children...
  17505. if vsExpanded in Node.States then
  17506. begin
  17507. // ...and their children.
  17508. Node := Node.FirstChild;
  17509. while Assigned(Node) do
  17510. begin
  17511. if vsVisible in Node.States then
  17512. Inc(Result, CountVisibleChildren(Node) + Cardinal(IfThen(IsEffectivelyVisible[Node], 1)));
  17513. Node := Node.NextSibling;
  17514. end;
  17515. end;
  17516. end;
  17517. //----------------------------------------------------------------------------------------------------------------------
  17518. procedure TBaseVirtualTree.CreateParams(var Params: TCreateParams);
  17519. const
  17520. ScrollBar: array[TScrollStyle] of Cardinal = (0, WS_HSCROLL, WS_VSCROLL, WS_HSCROLL or WS_VSCROLL);
  17521. begin
  17522. inherited CreateParams(Params);
  17523. with Params do
  17524. begin
  17525. Style := Style or WS_CLIPCHILDREN or WS_CLIPSIBLINGS or ScrollBar[ScrollBarOptions.FScrollBars];
  17526. if toFullRepaintOnResize in FOptions.FMiscOptions then
  17527. WindowClass.style := WindowClass.style or CS_HREDRAW or CS_VREDRAW
  17528. else
  17529. WindowClass.style := WindowClass.style and not (CS_HREDRAW or CS_VREDRAW);
  17530. if FBorderStyle = bsSingle then
  17531. begin
  17532. if Ctl3D then
  17533. begin
  17534. ExStyle := ExStyle or WS_EX_CLIENTEDGE;
  17535. Style := Style and not WS_BORDER;
  17536. end
  17537. else
  17538. Style := Style or WS_BORDER;
  17539. end
  17540. else
  17541. Style := Style and not WS_BORDER;
  17542. AddBiDiModeExStyle(ExStyle);
  17543. end;
  17544. end;
  17545. //----------------------------------------------------------------------------------------------------------------------
  17546. procedure TBaseVirtualTree.CreateWnd;
  17547. // Initializes data which depends on a valid window handle.
  17548. begin
  17549. DoStateChange([tsWindowCreating]);
  17550. inherited;
  17551. DoStateChange([], [tsWindowCreating]);
  17552. if (StyleServices.Enabled and (toThemeAware in TreeOptions.PaintOptions)) or VclStyleEnabled then
  17553. begin
  17554. DoStateChange([tsUseThemes]);
  17555. if not VclStyleEnabled then
  17556. if (toUseExplorerTheme in FOptions.FPaintOptions) and IsWinVistaOrAbove then
  17557. begin
  17558. DoStateChange([tsUseExplorerTheme]);
  17559. SetWindowTheme('explorer');
  17560. end
  17561. else
  17562. DoStateChange([], [tsUseExplorerTheme]);
  17563. end
  17564. else
  17565. DoStateChange([], [tsUseThemes, tsUseExplorerTheme]);
  17566. AutoScale();
  17567. // Because of the special recursion and update stopper when creating the window (or resizing it)
  17568. // we have to manually trigger the auto size calculation here.
  17569. if hsNeedScaling in FHeader.FStates then
  17570. FHeader.RescaleHeader;
  17571. if hoAutoResize in FHeader.FOptions then
  17572. FHeader.FColumns.AdjustAutoSize(InvalidColumn);
  17573. PrepareBitmaps(True, True);
  17574. // Register tree as OLE drop target.
  17575. if not (csDesigning in ComponentState) and (toAcceptOLEDrop in FOptions.FMiscOptions) then
  17576. if not (csLoading in ComponentState) then // will be done in Loaded after all inherited settings are loaded from the DFMs
  17577. RegisterDragDrop(Handle, DragManager as IDropTarget);
  17578. UpdateScrollBars(True);
  17579. UpdateHeaderRect;
  17580. end;
  17581. //----------------------------------------------------------------------------------------------------------------------
  17582. procedure TBaseVirtualTree.DefineProperties(Filer: TFiler);
  17583. // There were heavy changes in some properties during development of VT. This method helps to make migration easier
  17584. // by reading old properties manually and put them into the new properties as appropriate.
  17585. // Note: these old properties are never written again and silently disappear.
  17586. // June 2002: Meanwhile another task is done here too: working around the problem that TCollection is not streamed
  17587. // correctly when using Visual Form Inheritance (VFI).
  17588. var
  17589. StoreIt: Boolean;
  17590. begin
  17591. inherited;
  17592. // The header can prevent writing columns altogether.
  17593. if FHeader.CanWriteColumns then
  17594. begin
  17595. // Check if we inherit from an ancestor form (Visual Form Inheritance).
  17596. StoreIt := Filer.Ancestor = nil;
  17597. // If there is an ancestor then save columns only if they are different to the base set.
  17598. if not StoreIt then
  17599. StoreIt := not FHeader.Columns.Equals(TBaseVirtualTree(Filer.Ancestor).FHeader.Columns);
  17600. end
  17601. else
  17602. StoreIt := False;
  17603. Filer.DefineProperty('Columns', FHeader.ReadColumns, FHeader.WriteColumns, StoreIt);
  17604. Filer.DefineProperty('Options', ReadOldOptions, nil, False);
  17605. end;
  17606. //----------------------------------------------------------------------------------------------------------------------
  17607. function TBaseVirtualTree.DetermineDropMode(const P: TPoint; var HitInfo: THitInfo; var NodeRect: TRect): TDropMode;
  17608. // Determine the DropMode.
  17609. var
  17610. ImageHit: Boolean;
  17611. LabelHit: Boolean;
  17612. ItemHit: Boolean;
  17613. begin
  17614. ImageHit := HitInfo.HitPositions * [hiOnNormalIcon, hiOnStateIcon] <> [];
  17615. LabelHit := hiOnItemLabel in HitInfo.HitPositions;
  17616. ItemHit := ((hiOnItem in HitInfo.HitPositions) and
  17617. ((toFullRowDrag in FOptions.FMiscOptions) or (toFullRowSelect in FOptions.FSelectionOptions)));
  17618. // In report mode only direct hits of the node captions/images in the main column are accepted as hits.
  17619. if (toReportMode in FOptions.FMiscOptions) and not (ItemHit or ((LabelHit or ImageHit) and
  17620. (HitInfo.HitColumn = FHeader.MainColumn))) then
  17621. HitInfo.HitNode := nil;
  17622. if Assigned(HitInfo.HitNode) then
  17623. begin
  17624. if LabelHit or ImageHit or not (toShowDropmark in FOptions.FPaintOptions) then
  17625. Result := dmOnNode
  17626. else
  17627. if ((NodeRect.Top + NodeRect.Bottom) div 2) > P.Y then
  17628. Result := dmAbove
  17629. else
  17630. Result := dmBelow;
  17631. end
  17632. else
  17633. Result := dmNowhere;
  17634. end;
  17635. //----------------------------------------------------------------------------------------------------------------------
  17636. procedure TBaseVirtualTree.DetermineHiddenChildrenFlag(Node: PVirtualNode);
  17637. // Update the hidden children flag of the given node.
  17638. var
  17639. Run: PVirtualNode;
  17640. begin
  17641. if Node.ChildCount = 0 then
  17642. begin
  17643. if vsHasChildren in Node.States then
  17644. Exclude(Node.States, vsAllChildrenHidden)
  17645. else
  17646. Include(Node.States, vsAllChildrenHidden);
  17647. end
  17648. else
  17649. begin
  17650. // Iterate through all siblings and stop when one visible is found.
  17651. Run := Node.FirstChild;
  17652. while Assigned(Run) and not IsEffectivelyVisible[Run] do
  17653. Run := Run.NextSibling;
  17654. if Assigned(Run) then
  17655. Exclude(Node.States, vsAllChildrenHidden)
  17656. else
  17657. Include(Node.States, vsAllChildrenHidden);
  17658. end;
  17659. end;
  17660. //----------------------------------------------------------------------------------------------------------------------
  17661. procedure TBaseVirtualTree.DetermineHiddenChildrenFlagAllNodes;
  17662. var
  17663. Run: PVirtualNode;
  17664. begin
  17665. Run := GetFirstNoInit(False);
  17666. while Assigned(Run) do
  17667. begin
  17668. DetermineHiddenChildrenFlag(Run);
  17669. Run := GetNextNoInit(Run);
  17670. end;
  17671. end;
  17672. //----------------------------------------------------------------------------------------------------------------------
  17673. procedure TBaseVirtualTree.DetermineHitPositionLTR(var HitInfo: THitInfo; Offset, Right: Integer;
  17674. Alignment: TAlignment);
  17675. // This method determines the hit position within a node with left-to-right orientation.
  17676. var
  17677. MainColumnHit: Boolean;
  17678. Run: PVirtualNode;
  17679. Indent,
  17680. TextWidth,
  17681. ImageOffset: Integer;
  17682. begin
  17683. MainColumnHit := HitInfo.HitColumn = FHeader.MainColumn;
  17684. Indent := 0;
  17685. // If columns are not used or the main column is hit then the tree indentation must be considered too.
  17686. if MainColumnHit then
  17687. begin
  17688. if toFixedIndent in FOptions.FPaintOptions then
  17689. Indent := FIndent
  17690. else
  17691. begin
  17692. Run := HitInfo.HitNode;
  17693. while (Run.Parent <> FRoot) do
  17694. begin
  17695. Inc(Indent, FIndent);
  17696. Run := Run.Parent;
  17697. end;
  17698. if toShowRoot in FOptions.FPaintOptions then
  17699. Inc(Indent, FIndent);
  17700. end;
  17701. end;
  17702. if (MainColumnHit and (Offset < (Indent + Margin{See issue #259}))) then
  17703. begin
  17704. // Position is to the left of calculated indentation which can only happen for the main column.
  17705. // Check whether it corresponds to a button/checkbox.
  17706. if (toShowButtons in FOptions.FPaintOptions) and (vsHasChildren in HitInfo.HitNode.States) then
  17707. begin
  17708. // Position of button is interpreted very generously to avoid forcing the user
  17709. // to click exactly into the 9x9 pixels area. The entire node height and one full
  17710. // indentation level is accepted as button hit.
  17711. if Offset >= Indent - Integer(FIndent) then
  17712. Include(HitInfo.HitPositions, hiOnItemButton);
  17713. if Offset >= Indent - FPlusBM.Width then
  17714. Include(HitInfo.HitPositions, hiOnItemButtonExact);
  17715. end;
  17716. // no button hit so position is on indent
  17717. if HitInfo.HitPositions = [] then
  17718. Include(HitInfo.HitPositions, hiOnItemIndent);
  17719. end
  17720. else
  17721. begin
  17722. // The next hit positions can be:
  17723. // - on the check box
  17724. // - on the state image
  17725. // - on the normal image
  17726. // - to the left of the text area
  17727. // - on the label or
  17728. // - to the right of the text area
  17729. // (in this order).
  17730. // In report mode no hit other than in the main column is possible.
  17731. if MainColumnHit or not (toReportMode in FOptions.FMiscOptions) then
  17732. begin
  17733. ImageOffset := Indent + FMargin;
  17734. // Check support is only available for the main column.
  17735. if MainColumnHit and (toCheckSupport in FOptions.FMiscOptions) and Assigned(FCheckImages) and
  17736. (HitInfo.HitNode.CheckType <> ctNone) then
  17737. Inc(ImageOffset, FCheckImages.Width + 2);
  17738. if MainColumnHit and (Offset < ImageOffset) then
  17739. begin
  17740. HitInfo.HitPositions := [hiOnItem];
  17741. if (HitInfo.HitNode.CheckType <> ctNone) then
  17742. Include(HitInfo.HitPositions, hiOnItemCheckBox);
  17743. end
  17744. else
  17745. begin
  17746. if Assigned(FStateImages) and HasImage(HitInfo.HitNode, ikState, HitInfo.HitColumn) then
  17747. Inc(ImageOffset, FStateImages.Width + 2);
  17748. if Offset < ImageOffset then
  17749. Include(HitInfo.HitPositions, hiOnStateIcon)
  17750. else
  17751. begin
  17752. if Assigned(FImages) and HasImage(HitInfo.HitNode, ikNormal, HitInfo.HitColumn) then
  17753. Inc(ImageOffset, GetNodeImageSize(HitInfo.HitNode).cx + 2);
  17754. if Offset < ImageOffset then
  17755. Include(HitInfo.HitPositions, hiOnNormalIcon)
  17756. else
  17757. begin
  17758. // ImageOffset contains now the left border of the node label area. This is used to calculate the
  17759. // correct alignment in the column.
  17760. TextWidth := DoGetNodeWidth(HitInfo.HitNode, HitInfo.HitColumn);
  17761. // Check if the text can be aligned at all. This is only possible if there is enough room
  17762. // in the remaining text rectangle.
  17763. if TextWidth > Right - ImageOffset then
  17764. Include(HitInfo.HitPositions, hiOnItemLabel)
  17765. else
  17766. begin
  17767. case Alignment of
  17768. taCenter:
  17769. begin
  17770. Indent := (ImageOffset + Right - TextWidth) div 2;
  17771. if Offset < Indent then
  17772. Include(HitInfo.HitPositions, hiOnItemLeft)
  17773. else
  17774. if Offset < Indent + TextWidth then
  17775. Include(HitInfo.HitPositions, hiOnItemLabel)
  17776. else
  17777. Include(HitInfo.HitPositions, hiOnItemRight);
  17778. end;
  17779. taRightJustify:
  17780. begin
  17781. Indent := Right - TextWidth;
  17782. if Offset < Indent then
  17783. Include(HitInfo.HitPositions, hiOnItemLeft)
  17784. else
  17785. Include(HitInfo.HitPositions, hiOnItemLabel);
  17786. end;
  17787. else // taLeftJustify
  17788. if Offset < ImageOffset + TextWidth then
  17789. Include(HitInfo.HitPositions, hiOnItemLabel)
  17790. else
  17791. Include(HitInfo.HitPositions, hiOnItemRight);
  17792. end;
  17793. end;
  17794. end;
  17795. end;
  17796. end;
  17797. end;
  17798. end;
  17799. end;
  17800. //----------------------------------------------------------------------------------------------------------------------
  17801. procedure TBaseVirtualTree.DetermineHitPositionRTL(var HitInfo: THitInfo; Offset, Right: Integer; Alignment: TAlignment);
  17802. // This method determines the hit position within a node with right-to-left orientation.
  17803. var
  17804. MainColumnHit: Boolean;
  17805. Run: PVirtualNode;
  17806. Indent,
  17807. TextWidth,
  17808. ImageOffset: Integer;
  17809. begin
  17810. MainColumnHit := HitInfo.HitColumn = FHeader.MainColumn;
  17811. // If columns are not used or the main column is hit then the tree indentation must be considered too.
  17812. if MainColumnHit then
  17813. begin
  17814. if toFixedIndent in FOptions.FPaintOptions then
  17815. Dec(Right, FIndent)
  17816. else
  17817. begin
  17818. Run := HitInfo.HitNode;
  17819. while (Run.Parent <> FRoot) do
  17820. begin
  17821. Dec(Right, FIndent);
  17822. Run := Run.Parent;
  17823. end;
  17824. if toShowRoot in FOptions.FPaintOptions then
  17825. Dec(Right, FIndent);
  17826. end;
  17827. end;
  17828. if Offset >= Right then
  17829. begin
  17830. // Position is to the right of calculated indentation which can only happen for the main column.
  17831. // Check whether it corresponds to a button/checkbox.
  17832. if (toShowButtons in FOptions.FPaintOptions) and (vsHasChildren in HitInfo.HitNode.States) then
  17833. begin
  17834. // Position of button is interpreted very generously to avoid forcing the user
  17835. // to click exactly into the 9x9 pixels area. The entire node height and one full
  17836. // indentation level is accepted as button hit.
  17837. if Offset <= Right + Integer(FIndent) then
  17838. Include(HitInfo.HitPositions, hiOnItemButton);
  17839. if Offset <= Right + FPlusBM.Width then
  17840. Include(HitInfo.HitPositions, hiOnItemButtonExact);
  17841. end;
  17842. // no button hit so position is on indent
  17843. if HitInfo.HitPositions = [] then
  17844. Include(HitInfo.HitPositions, hiOnItemIndent);
  17845. end
  17846. else
  17847. begin
  17848. // The next hit positions can be:
  17849. // - on the check box
  17850. // - on the state image
  17851. // - on the normal image
  17852. // - to the left of the text area
  17853. // - on the label or
  17854. // - to the right of the text area
  17855. // (in this order).
  17856. // In report mode no hit other than in the main column is possible.
  17857. if MainColumnHit or not (toReportMode in FOptions.FMiscOptions) then
  17858. begin
  17859. ImageOffset := Right - FMargin;
  17860. // Check support is only available for the main column.
  17861. if MainColumnHit and (toCheckSupport in FOptions.FMiscOptions) and Assigned(FCheckImages) and
  17862. (HitInfo.HitNode.CheckType <> ctNone) then
  17863. Dec(ImageOffset, FCheckImages.Width + 2);
  17864. if MainColumnHit and (Offset > ImageOffset) then
  17865. begin
  17866. HitInfo.HitPositions := [hiOnItem];
  17867. if (HitInfo.HitNode.CheckType <> ctNone) then
  17868. Include(HitInfo.HitPositions, hiOnItemCheckBox);
  17869. end
  17870. else
  17871. begin
  17872. if Assigned(FStateImages) and HasImage(HitInfo.HitNode, ikState, HitInfo.HitColumn) then
  17873. Dec(ImageOffset, FStateImages.Width + 2);
  17874. if Offset > ImageOffset then
  17875. Include(HitInfo.HitPositions, hiOnStateIcon)
  17876. else
  17877. begin
  17878. if Assigned(FImages) and HasImage(HitInfo.HitNode, ikNormal, HitInfo.HitColumn) then
  17879. Dec(ImageOffset, GetNodeImageSize(HitInfo.HitNode).cx + 2);
  17880. if Offset > ImageOffset then
  17881. Include(HitInfo.HitPositions, hiOnNormalIcon)
  17882. else
  17883. begin
  17884. // ImageOffset contains now the right border of the node label area. This is used to calculate the
  17885. // correct alignment in the column.
  17886. TextWidth := DoGetNodeWidth(HitInfo.HitNode, HitInfo.HitColumn);
  17887. // Check if the text can be aligned at all. This is only possible if there is enough room
  17888. // in the remaining text rectangle.
  17889. if TextWidth > ImageOffset then
  17890. Include(HitInfo.HitPositions, hiOnItemLabel)
  17891. else
  17892. begin
  17893. // Consider bidi mode here. In RTL context does left alignment actually mean right alignment
  17894. // and vice versa.
  17895. ChangeBiDiModeAlignment(Alignment);
  17896. case Alignment of
  17897. taCenter:
  17898. begin
  17899. Indent := (ImageOffset - TextWidth) div 2;
  17900. if Offset < Indent then
  17901. Include(HitInfo.HitPositions, hiOnItemLeft)
  17902. else
  17903. if Offset < Indent + TextWidth then
  17904. Include(HitInfo.HitPositions, hiOnItemLabel)
  17905. else
  17906. Include(HitInfo.HitPositions, hiOnItemRight);
  17907. end;
  17908. taRightJustify:
  17909. begin
  17910. Indent := ImageOffset - TextWidth;
  17911. if Offset < Indent then
  17912. Include(HitInfo.HitPositions, hiOnItemLeft)
  17913. else
  17914. Include(HitInfo.HitPositions, hiOnItemLabel);
  17915. end;
  17916. else // taLeftJustify
  17917. if Offset > TextWidth then
  17918. Include(HitInfo.HitPositions, hiOnItemRight)
  17919. else
  17920. Include(HitInfo.HitPositions, hiOnItemLabel);
  17921. end;
  17922. end;
  17923. end;
  17924. end;
  17925. end;
  17926. end;
  17927. end;
  17928. end;
  17929. //----------------------------------------------------------------------------------------------------------------------
  17930. function TBaseVirtualTree.DetermineLineImageAndSelectLevel(Node: PVirtualNode; var LineImage: TLineImage): Integer;
  17931. // This method is used during paint cycles and initializes an array of line type IDs. These IDs are used to paint
  17932. // the tree lines in front of the given node.
  17933. // Additionally an initial count of selected parents is determined and returned which is used for specific painting.
  17934. var
  17935. X: Integer;
  17936. Indent: Integer;
  17937. Run: PVirtualNode;
  17938. begin
  17939. Result := 0;
  17940. if toShowRoot in FOptions.FPaintOptions then
  17941. X := 1
  17942. else
  17943. X := 0;
  17944. Run := Node;
  17945. // Determine indentation level of top node.
  17946. while Run.Parent <> FRoot do
  17947. begin
  17948. Inc(X);
  17949. Run := Run.Parent;
  17950. // Count selected nodes (FRoot is never selected).
  17951. if vsSelected in Run.States then
  17952. Inc(Result);
  17953. end;
  17954. // Set initial size of line index array, this will automatically initialized all entries to ltNone.
  17955. SetLength(LineImage, X);
  17956. Indent := X - 1;
  17957. // Only use lines if requested.
  17958. if (toShowTreeLines in FOptions.FPaintOptions) and
  17959. (not (toHideTreeLinesIfThemed in FOptions.FPaintOptions) or not (tsUseThemes in FStates)) then
  17960. begin
  17961. if toChildrenAbove in FOptions.FPaintOptions then
  17962. begin
  17963. Dec(X);
  17964. if not HasVisiblePreviousSibling(Node) then
  17965. begin
  17966. if (Node.Parent <> FRoot) or HasVisibleNextSibling(Node) then
  17967. LineImage[X] := ltBottomRight
  17968. else
  17969. LineImage[X] := ltRight;
  17970. end
  17971. else
  17972. if (Node.Parent = FRoot) and (not HasVisibleNextSibling(Node)) then
  17973. LineImage[X] := ltTopRight
  17974. else
  17975. LineImage[X] := ltTopDownRight;
  17976. // Now go up to the root to determine the rest.
  17977. Run := Node.Parent;
  17978. while Run <> FRoot do
  17979. begin
  17980. Dec(X);
  17981. if HasVisiblePreviousSibling(Run) then
  17982. LineImage[X] := ltTopDown
  17983. else
  17984. LineImage[X] := ltNone;
  17985. Run := Run.Parent;
  17986. end;
  17987. end
  17988. else
  17989. begin
  17990. // Start over parent traversal if necessary.
  17991. Run := Node;
  17992. if Run.Parent <> FRoot then
  17993. begin
  17994. // The very last image (the one immediately before the item label) is different.
  17995. if HasVisibleNextSibling(Run) then
  17996. LineImage[X - 1] := ltTopDownRight
  17997. else
  17998. LineImage[X - 1] := ltTopRight;
  17999. Run := Run.Parent;
  18000. // Now go up all parents.
  18001. repeat
  18002. if Run.Parent = FRoot then
  18003. Break;
  18004. Dec(X);
  18005. if HasVisibleNextSibling(Run) then
  18006. LineImage[X - 1] := ltTopDown
  18007. else
  18008. LineImage[X - 1] := ltNone;
  18009. Run := Run.Parent;
  18010. until False;
  18011. end;
  18012. // Prepare root level. Run points at this stage to a top level node.
  18013. if (toShowRoot in FOptions.FPaintOptions) and ((toShowTreeLines in FOptions.FPaintOptions) and
  18014. (not (toHideTreeLinesIfThemed in FOptions.FPaintOptions) or not (tsUseThemes in FStates))) then
  18015. begin
  18016. // Is the top node a root node?
  18017. if Run = Node then
  18018. begin
  18019. // First child gets the bottom-right bitmap if it isn't also the only child.
  18020. if IsFirstVisibleChild(FRoot, Run) then
  18021. // Is it the only child?
  18022. if IsLastVisibleChild(FRoot, Run) then
  18023. LineImage[0] := ltRight
  18024. else
  18025. LineImage[0] := ltBottomRight
  18026. else
  18027. // real last child
  18028. if IsLastVisibleChild(FRoot, Run) then
  18029. LineImage[0] := ltTopRight
  18030. else
  18031. LineImage[0] := ltTopDownRight;
  18032. end
  18033. else
  18034. begin
  18035. // No, top node is not a top level node. So we need different painting.
  18036. if HasVisibleNextSibling(Run) then
  18037. LineImage[0] := ltTopDown
  18038. else
  18039. LineImage[0] := ltNone;
  18040. end;
  18041. end;
  18042. end;
  18043. end;
  18044. if (tsUseExplorerTheme in FStates) and HasChildren[Node] and (Indent >= 0) then
  18045. LineImage[Indent] := ltNone;
  18046. end;
  18047. //----------------------------------------------------------------------------------------------------------------------
  18048. function TBaseVirtualTree.DetermineNextCheckState(CheckType: TCheckType; CheckState: TCheckState): TCheckState;
  18049. // Determines the next check state in case the user click the check image or pressed the space key.
  18050. begin
  18051. case CheckType of
  18052. ctTriStateCheckBox,
  18053. ctCheckBox:
  18054. if CheckState = csCheckedNormal then
  18055. Result := csUncheckedNormal
  18056. else
  18057. Result := csCheckedNormal;
  18058. ctRadioButton:
  18059. Result := csCheckedNormal;
  18060. ctButton:
  18061. Result := csUncheckedNormal;
  18062. else
  18063. Result := csMixedNormal;
  18064. end;
  18065. end;
  18066. //----------------------------------------------------------------------------------------------------------------------
  18067. function TBaseVirtualTree.DetermineScrollDirections(X, Y: Integer): TScrollDirections;
  18068. // Determines which direction the client area must be scrolled depending on the given position.
  18069. begin
  18070. Result:= [];
  18071. if CanAutoScroll then
  18072. begin
  18073. // Calculation for wheel panning/scrolling is a bit different to normal auto scroll.
  18074. if [tsWheelPanning, tsWheelScrolling] * FStates <> [] then
  18075. begin
  18076. if (X - FLastClickPos.X) < -8 then
  18077. Include(Result, sdLeft);
  18078. if (X - FLastClickPos.X) > 8 then
  18079. Include(Result, sdRight);
  18080. if (Y - FLastClickPos.Y) < -8 then
  18081. Include(Result, sdUp);
  18082. if (Y - FLastClickPos.Y) > 8 then
  18083. Include(Result, sdDown);
  18084. end
  18085. else
  18086. begin
  18087. if (X < Integer(FDefaultNodeHeight)) and (FEffectiveOffsetX <> 0) then
  18088. Include(Result, sdLeft);
  18089. if (ClientWidth + FEffectiveOffsetX < Integer(FRangeX)) and (X > ClientWidth - Integer(FDefaultNodeHeight)) then
  18090. Include(Result, sdRight);
  18091. if (Y < Integer(FDefaultNodeHeight)) and (FOffsetY <> 0) then
  18092. Include(Result, sdUp);
  18093. if (ClientHeight - FOffsetY < Integer(FRangeY)) and (Y > ClientHeight - Integer(FDefaultNodeHeight)) then
  18094. Include(Result, sdDown);
  18095. // Since scrolling during dragging is not handled via the timer we do a check here whether the auto
  18096. // scroll timeout already has elapsed or not.
  18097. if (Result <> []) and
  18098. ((Assigned(FDragManager) and DragManager.IsDropTarget) or
  18099. (FindDragTarget(Point(X, Y), False) = Self)) then
  18100. begin
  18101. if FDragScrollStart = 0 then
  18102. FDragScrollStart := timeGetTime;
  18103. // Reset any scroll direction to avoid scroll in the case the user is dragging and the auto scroll time has not
  18104. // yet elapsed.
  18105. if ((timeGetTime - FDragScrollStart) < FAutoScrollDelay) then
  18106. Result := [];
  18107. end;
  18108. end;
  18109. end;
  18110. end;
  18111. //----------------------------------------------------------------------------------------------------------------------
  18112. procedure TBaseVirtualTree.DoAdvancedHeaderDraw(var PaintInfo: THeaderPaintInfo; const Elements: THeaderPaintElements);
  18113. begin
  18114. if Assigned(FOnAdvancedHeaderDraw) then
  18115. FOnAdvancedHeaderDraw(FHeader, PaintInfo, Elements);
  18116. end;
  18117. //----------------------------------------------------------------------------------------------------------------------
  18118. procedure TBaseVirtualTree.DoAfterCellPaint(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; CellRect: TRect);
  18119. begin
  18120. if Assigned(FOnAfterCellPaint) then
  18121. FOnAfterCellPaint(Self, Canvas, Node, Column, CellRect);
  18122. end;
  18123. //----------------------------------------------------------------------------------------------------------------------
  18124. procedure TBaseVirtualTree.DoAfterItemErase(Canvas: TCanvas; Node: PVirtualNode; ItemRect: TRect);
  18125. begin
  18126. if Assigned(FOnAfterItemErase) then
  18127. FOnAfterItemErase(Self, Canvas, Node, ItemRect);
  18128. end;
  18129. //----------------------------------------------------------------------------------------------------------------------
  18130. procedure TBaseVirtualTree.DoAfterItemPaint(Canvas: TCanvas; Node: PVirtualNode; ItemRect: TRect);
  18131. begin
  18132. if Assigned(FOnAfterItemPaint) then
  18133. FOnAfterItemPaint(Self, Canvas, Node, ItemRect);
  18134. end;
  18135. //----------------------------------------------------------------------------------------------------------------------
  18136. procedure TBaseVirtualTree.DoAfterPaint(Canvas: TCanvas);
  18137. begin
  18138. if Assigned(FOnAfterPaint) then
  18139. FOnAfterPaint(Self, Canvas);
  18140. end;
  18141. //----------------------------------------------------------------------------------------------------------------------
  18142. procedure TBaseVirtualTree.DoAutoScroll(X, Y: Integer);
  18143. begin
  18144. FScrollDirections := DetermineScrollDirections(X, Y);
  18145. if FStates * [tsWheelPanning, tsWheelScrolling] = [] then
  18146. begin
  18147. if FScrollDirections = [] then
  18148. begin
  18149. if ((FStates * [tsScrollPending, tsScrolling]) <> []) then
  18150. begin
  18151. StopTimer(ScrollTimer);
  18152. DoStateChange([], [tsScrollPending, tsScrolling]);
  18153. end;
  18154. end
  18155. else
  18156. begin
  18157. // start auto scroll if not yet done
  18158. if (FStates * [tsScrollPending, tsScrolling]) = [] then
  18159. begin
  18160. DoStateChange([tsScrollPending]);
  18161. SetTimer(Handle, ScrollTimer, FAutoScrollDelay, nil);
  18162. end;
  18163. end;
  18164. end;
  18165. end;
  18166. //----------------------------------------------------------------------------------------------------------------------
  18167. function TBaseVirtualTree.DoBeforeDrag(Node: PVirtualNode; Column: TColumnIndex): Boolean;
  18168. begin
  18169. Result := False;
  18170. if Assigned(FOnDragAllowed) then
  18171. FOnDragAllowed(Self, Node, Column, Result);
  18172. end;
  18173. //----------------------------------------------------------------------------------------------------------------------
  18174. procedure TBaseVirtualTree.DoBeforeCellPaint(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
  18175. CellPaintMode: TVTCellPaintMode; CellRect: TRect; var ContentRect: TRect);
  18176. var
  18177. UpdateRect: TRect;
  18178. begin
  18179. if Assigned(FOnBeforeCellPaint) then
  18180. begin
  18181. if CellPaintMode = cpmGetContentMargin then
  18182. begin
  18183. // Prevent drawing if we are only about to get the margin. As this also clears the update rect we need to save it.
  18184. GetUpdateRect(Handle, UpdateRect, False);
  18185. SetUpdateState(True);
  18186. end;
  18187. Canvas.Font := Self.Font; // Fixes issue #298
  18188. FOnBeforeCellPaint(Self, Canvas, Node, Column, CellPaintMode, CellRect, ContentRect);
  18189. if CellPaintMode = cpmGetContentMargin then
  18190. begin
  18191. SetUpdateState(False);
  18192. InvalidateRect(Handle, @UpdateRect, False);
  18193. end;
  18194. end;
  18195. end;
  18196. //----------------------------------------------------------------------------------------------------------------------
  18197. procedure TBaseVirtualTree.DoBeforeItemErase(Canvas: TCanvas; Node: PVirtualNode; ItemRect: TRect; var Color: TColor;
  18198. var EraseAction: TItemEraseAction);
  18199. begin
  18200. if Assigned(FOnBeforeItemErase) then
  18201. FOnBeforeItemErase(Self, Canvas, Node, ItemRect, Color, EraseAction);
  18202. end;
  18203. //----------------------------------------------------------------------------------------------------------------------
  18204. function TBaseVirtualTree.DoBeforeItemPaint(Canvas: TCanvas; Node: PVirtualNode; ItemRect: TRect): Boolean;
  18205. begin
  18206. // By default custom draw will not be used, so the tree handles drawing the node.
  18207. Result := False;
  18208. if Assigned(FOnBeforeItemPaint) then
  18209. FOnBeforeItemPaint(Self, Canvas, Node, ItemRect, Result);
  18210. end;
  18211. //----------------------------------------------------------------------------------------------------------------------
  18212. procedure TBaseVirtualTree.DoBeforePaint(Canvas: TCanvas);
  18213. begin
  18214. if Assigned(FOnBeforePaint) then
  18215. FOnBeforePaint(Self, Canvas);
  18216. end;
  18217. //----------------------------------------------------------------------------------------------------------------------
  18218. function TBaseVirtualTree.DoCancelEdit: Boolean;
  18219. // Called when the current edit action or a pending edit must be cancelled.
  18220. begin
  18221. StopTimer(EditTimer);
  18222. DoStateChange([], [tsEditPending]);
  18223. Result := (tsEditing in FStates) and FEditLink.CancelEdit;
  18224. if Result then
  18225. begin
  18226. DoStateChange([], [tsEditing]);
  18227. if Assigned(FOnEditCancelled) then
  18228. FOnEditCancelled(Self, FEditColumn);
  18229. if not (csDestroying in ComponentState) then
  18230. FEditLink := nil;
  18231. end;
  18232. end;
  18233. //----------------------------------------------------------------------------------------------------------------------
  18234. procedure TBaseVirtualTree.DoCanEdit(Node: PVirtualNode; Column: TColumnIndex; var Allowed: Boolean);
  18235. begin
  18236. if Assigned(FOnEditing) then
  18237. FOnEditing(Self, Node, Column, Allowed);
  18238. end;
  18239. //----------------------------------------------------------------------------------------------------------------------
  18240. procedure TBaseVirtualTree.DoCanSplitterResizeNode(P: TPoint; Node: PVirtualNode; Column: TColumnIndex;
  18241. var Allowed: Boolean);
  18242. begin
  18243. if Assigned(FOnCanSplitterResizeNode) then
  18244. FOnCanSplitterResizeNode(Self, P, Node, Column, Allowed);
  18245. end;
  18246. //----------------------------------------------------------------------------------------------------------------------
  18247. procedure TBaseVirtualTree.DoChange(Node: PVirtualNode);
  18248. begin
  18249. StopTimer(ChangeTimer);
  18250. if Assigned(FOnChange) then
  18251. FOnChange(Self, Node);
  18252. // This is a good place to reset the cached node. This is the same as the node passed in here.
  18253. // This is necessary to allow descendants to override this method and get the node then.
  18254. DoStateChange([], [tsChangePending]);
  18255. FLastChangedNode := nil;
  18256. end;
  18257. //----------------------------------------------------------------------------------------------------------------------
  18258. procedure TBaseVirtualTree.DoCheckClick(Node: PVirtualNode; NewCheckState: TCheckState);
  18259. begin
  18260. if ChangeCheckState(Node, NewCheckState) then
  18261. DoChecked(Node);
  18262. end;
  18263. //----------------------------------------------------------------------------------------------------------------------
  18264. procedure TBaseVirtualTree.DoChecked(Node: PVirtualNode);
  18265. begin
  18266. if Assigned(FOnChecked) then
  18267. FOnChecked(Self, Node);
  18268. if Assigned(FAccessibleItem) then
  18269. NotifyWinEvent(EVENT_OBJECT_STATECHANGE, Handle, OBJID_CLIENT, CHILDID_SELF);
  18270. end;
  18271. //----------------------------------------------------------------------------------------------------------------------
  18272. function TBaseVirtualTree.DoChecking(Node: PVirtualNode; var NewCheckState: TCheckState): Boolean;
  18273. // Determines if a node is allowed to change its check state to NewCheckState.
  18274. begin
  18275. if toReadOnly in FOptions.FMiscOptions then
  18276. Result := False
  18277. else
  18278. begin
  18279. Result := True;
  18280. if Assigned(FOnChecking) then
  18281. FOnChecking(Self, Node, NewCheckState, Result);
  18282. end;
  18283. end;
  18284. //----------------------------------------------------------------------------------------------------------------------
  18285. procedure TBaseVirtualTree.DoCollapsed(Node: PVirtualNode);
  18286. var
  18287. lFirstSelected: PVirtualNode;
  18288. lParent: PVirtualNode;
  18289. begin
  18290. if Assigned(FOnCollapsed) then
  18291. FOnCollapsed(Self, Node);
  18292. if Assigned(FAccessibleItem) then
  18293. NotifyWinEvent(EVENT_OBJECT_STATECHANGE, Handle, OBJID_CLIENT, CHILDID_SELF);
  18294. if (toAlwaysSelectNode in TreeOptions.SelectionOptions) then
  18295. begin
  18296. // Select the next visible parent if the currently selected node gets invisible due to a collapse
  18297. // This makes the VT behave more like the Win32 custom TreeView control
  18298. // This makes only sense no no multi selection is allowed and if there is a selected node at all
  18299. lFirstSelected := GetFirstSelected();
  18300. if Assigned(lFirstSelected) and not FullyVisible[lFirstSelected] then
  18301. begin
  18302. lParent := GetVisibleParent(lFirstSelected);
  18303. Selected[lParent] := True;
  18304. Selected[lFirstSelected] := False;
  18305. end;//if
  18306. //if there is (still) no selected node, then use FNextNodeToSelect to select one
  18307. if SelectedCount = 0 then
  18308. EnsureNodeSelected();
  18309. end;//if
  18310. end;
  18311. //----------------------------------------------------------------------------------------------------------------------
  18312. function TBaseVirtualTree.DoCollapsing(Node: PVirtualNode): Boolean;
  18313. begin
  18314. Result := True;
  18315. if Assigned(FOnCollapsing) then
  18316. FOnCollapsing(Self, Node, Result);
  18317. end;
  18318. //----------------------------------------------------------------------------------------------------------------------
  18319. procedure TBaseVirtualTree.DoColumnClick(Column: TColumnIndex; Shift: TShiftState);
  18320. begin
  18321. if Assigned(FOnColumnClick) then
  18322. FOnColumnClick(Self, Column, Shift);
  18323. end;
  18324. //----------------------------------------------------------------------------------------------------------------------
  18325. procedure TBaseVirtualTree.DoColumnDblClick(Column: TColumnIndex; Shift: TShiftState);
  18326. begin
  18327. if Assigned(FOnColumnDblClick) then
  18328. FOnColumnDblClick(Self, Column, Shift);
  18329. end;
  18330. //----------------------------------------------------------------------------------------------------------------------
  18331. procedure TBaseVirtualTree.DoColumnResize(Column: TColumnIndex);
  18332. var
  18333. R: TRect;
  18334. Run: PVirtualNode;
  18335. begin
  18336. if not (csLoading in ComponentState) and HandleAllocated then
  18337. begin
  18338. // Reset all vsHeightMeasured flags if we are in multiline mode.
  18339. Run := GetFirstInitialized;
  18340. while Assigned(Run) do
  18341. begin
  18342. if vsMultiline in Run.States then
  18343. Exclude(Run.States, vsHeightMeasured);
  18344. Run := GetNextInitialized(Run);
  18345. end;
  18346. UpdateHorizontalScrollBar(True);
  18347. if Column > NoColumn then
  18348. begin
  18349. // Invalidate client area from the current column all to the right (or left in RTL mode).
  18350. R := ClientRect;
  18351. if not (toAutoSpanColumns in FOptions.FAutoOptions) then
  18352. if UseRightToLeftAlignment then
  18353. R.Right := FHeader.Columns[Column].Left + FHeader.Columns[Column].Width + ComputeRTLOffset
  18354. else
  18355. R.Left := FHeader.Columns[Column].Left;
  18356. InvalidateRect(Handle, @R, False);
  18357. FHeader.Invalidate(FHeader.Columns[Column], True);
  18358. end;
  18359. if [hsColumnWidthTracking, hsResizing] * FHeader.States = [hsColumnWidthTracking] then
  18360. UpdateWindow(Handle);
  18361. if not (tsUpdating in FStates) then
  18362. UpdateDesigner; // design time only
  18363. if Assigned(FOnColumnResize) and not (hsResizing in FHeader.States) then
  18364. FOnColumnResize(FHeader, Column);
  18365. // If the tree is currently in edit state then notify edit link.
  18366. if tsEditing in FStates then
  18367. UpdateEditBounds;
  18368. end;
  18369. end;
  18370. //----------------------------------------------------------------------------------------------------------------------
  18371. function TBaseVirtualTree.DoCompare(Node1, Node2: PVirtualNode; Column: TColumnIndex): Integer;
  18372. begin
  18373. Result := 0;
  18374. if Assigned(FOnCompareNodes) then
  18375. FOnCompareNodes(Self, Node1, Node2, Column, Result);
  18376. end;
  18377. //----------------------------------------------------------------------------------------------------------------------
  18378. function TBaseVirtualTree.DoCreateDataObject: IDataObject;
  18379. begin
  18380. Result := nil;
  18381. if Assigned(FOnCreateDataObject) then
  18382. FOnCreateDataObject(Self, Result);
  18383. end;
  18384. //----------------------------------------------------------------------------------------------------------------------
  18385. function TBaseVirtualTree.DoCreateDragManager: IVTDragManager;
  18386. begin
  18387. Result := nil;
  18388. if Assigned(FOnCreateDragManager) then
  18389. FOnCreateDragManager(Self, Result);
  18390. end;
  18391. //----------------------------------------------------------------------------------------------------------------------
  18392. function TBaseVirtualTree.DoCreateEditor(Node: PVirtualNode; Column: TColumnIndex): IVTEditLink;
  18393. begin
  18394. Result := nil;
  18395. if Assigned(FOnCreateEditor) then
  18396. FOnCreateEditor(Self, Node, Column, Result);
  18397. end;
  18398. //----------------------------------------------------------------------------------------------------------------------
  18399. procedure TBaseVirtualTree.DoDragging(P: TPoint);
  18400. // Initiates finally the drag'n drop operation and returns after DD is finished.
  18401. //--------------- local function --------------------------------------------
  18402. function GetDragOperations: Integer;
  18403. begin
  18404. if FDragOperations = [] then
  18405. Result := DROPEFFECT_COPY or DROPEFFECT_MOVE or DROPEFFECT_LINK
  18406. else
  18407. begin
  18408. Result := 0;
  18409. if doCopy in FDragOperations then
  18410. Result := Result or DROPEFFECT_COPY;
  18411. if doLink in FDragOperations then
  18412. Result := Result or DROPEFFECT_LINK;
  18413. if doMove in FDragOperations then
  18414. Result := Result or DROPEFFECT_MOVE;
  18415. end;
  18416. end;
  18417. //--------------- end local function ----------------------------------------
  18418. var
  18419. AllowedEffects: LongInt;
  18420. DragObject: TDragObject;
  18421. DataObject: IDataObject;
  18422. begin
  18423. DataObject := nil;
  18424. // Dragging is dragging, nothing else.
  18425. DoCancelEdit;
  18426. if Assigned(FCurrentHotNode) then
  18427. begin
  18428. InvalidateNode(FCurrentHotNode);
  18429. FCurrentHotNode := nil;
  18430. end;
  18431. // Select the focused node if not already done.
  18432. if Assigned(FFocusedNode) and not (vsSelected in FFocusedNode.States) then
  18433. begin
  18434. InternalAddToSelection(FFocusedNode, False);
  18435. InvalidateNode(FFocusedNode);
  18436. end;
  18437. UpdateWindow(Handle);
  18438. // Keep a list of all currently selected nodes as this list might change,
  18439. // but we have probably to delete currently selected nodes.
  18440. FDragSelection := GetSortedSelection(True);
  18441. try
  18442. DoStateChange([tsOLEDragging], [tsOLEDragPending, tsClearPending]);
  18443. // An application might create a drag object like used during VCL dd. This is not required for OLE dd but
  18444. // required as parameter.
  18445. DragObject := nil;
  18446. DoStartDrag(DragObject);
  18447. DragObject.Free;
  18448. DataObject := DragManager.DataObject;
  18449. PrepareDragImage(P, DataObject);
  18450. FLastDropMode := dmOnNode;
  18451. // Don't forget to initialize the result. It might never be touched.
  18452. FLastDragEffect := DROPEFFECT_NONE;
  18453. AllowedEffects := GetDragOperations;
  18454. try
  18455. DragAndDrop(AllowedEffects, DataObject, FLastDragEffect);
  18456. DragManager.ForceDragLeave;
  18457. finally
  18458. GetCursorPos(P);
  18459. P := ScreenToClient(P);
  18460. DoEndDrag(Self, P.X, P.Y);
  18461. FDragImage.EndDrag;
  18462. // Finish the operation.
  18463. if (FLastDragEffect = DROPEFFECT_MOVE) and (toAutoDeleteMovedNodes in TreeOptions.AutoOptions) then
  18464. begin
  18465. // The operation was a move so delete the previously selected nodes.
  18466. DeleteSelectedNodes;
  18467. end;
  18468. DoStateChange([], [tsOLEDragging]);
  18469. end;
  18470. finally
  18471. FDragSelection := nil;
  18472. end;
  18473. end;
  18474. //----------------------------------------------------------------------------------------------------------------------
  18475. procedure TBaseVirtualTree.DoDragExpand;
  18476. var
  18477. SourceTree: TBaseVirtualTree;
  18478. begin
  18479. StopTimer(ExpandTimer);
  18480. if Assigned(FDropTargetNode) and (vsHasChildren in FDropTargetNode.States) and
  18481. not (vsExpanded in FDropTargetNode.States) then
  18482. begin
  18483. if Assigned(FDragManager) then
  18484. SourceTree := DragManager.DragSource
  18485. else
  18486. SourceTree := nil;
  18487. if not DragManager.DropTargetHelperSupported and Assigned(SourceTree) then
  18488. SourceTree.FDragImage.HideDragImage;
  18489. ToggleNode(FDropTargetNode);
  18490. UpdateWindow(Handle);
  18491. if not DragManager.DropTargetHelperSupported and Assigned(SourceTree) then
  18492. SourceTree.FDragImage.ShowDragImage;
  18493. end;
  18494. end;
  18495. //----------------------------------------------------------------------------------------------------------------------
  18496. function TBaseVirtualTree.DoDragOver(Source: TObject; Shift: TShiftState; State: TDragState; Pt: TPoint; Mode: TDropMode;
  18497. var Effect: Integer): Boolean;
  18498. begin
  18499. Result := False;
  18500. if Assigned(FOnDragOver) then
  18501. FOnDragOver(Self, Source, Shift, State, Pt, Mode, Effect, Result);
  18502. end;
  18503. //----------------------------------------------------------------------------------------------------------------------
  18504. procedure TBaseVirtualTree.DoDragDrop(Source: TObject; DataObject: IDataObject; Formats: TFormatArray;
  18505. Shift: TShiftState; Pt: TPoint; var Effect: Integer; Mode: TDropMode);
  18506. begin
  18507. if Assigned(FOnDragDrop) then
  18508. FOnDragDrop(Self, Source, DataObject, Formats, Shift, Pt, Effect, Mode);
  18509. end;
  18510. //----------------------------------------------------------------------------------------------------------------------
  18511. procedure TBaseVirtualTree.DoBeforeDrawLineImage(Node: PVirtualNode; Level: Integer; var XPos: Integer);
  18512. begin
  18513. if Assigned(FOnBeforeDrawLineImage) then
  18514. FOnBeforeDrawLineImage(Self, Node, Level, XPos);
  18515. end;
  18516. //----------------------------------------------------------------------------------------------------------------------
  18517. procedure TBaseVirtualTree.DoEdit;
  18518. begin
  18519. Application.CancelHint;
  18520. StopTimer(ScrollTimer);
  18521. StopTimer(EditTimer);
  18522. DoStateChange([], [tsEditPending]);
  18523. if Assigned(FFocusedNode) and not (vsDisabled in FFocusedNode.States) and
  18524. not (toReadOnly in FOptions.FMiscOptions) and (FEditLink = nil) then
  18525. begin
  18526. FEditLink := DoCreateEditor(FFocusedNode, FEditColumn);
  18527. if Assigned(FEditLink) then
  18528. begin
  18529. DoStateChange([tsEditing], [tsDrawSelecting, tsDrawSelPending, tsToggleFocusedSelection, tsOLEDragPending,
  18530. tsOLEDragging, tsClearPending, tsDrawSelPending, tsScrollPending, tsScrolling, tsMouseCheckPending]);
  18531. ScrollIntoView(FFocusedNode, toCenterScrollIntoView in FOptions.SelectionOptions,
  18532. not (toDisableAutoscrollOnEdit in FOptions.AutoOptions));
  18533. if FEditLink.PrepareEdit(Self, FFocusedNode, FEditColumn) then
  18534. begin
  18535. UpdateEditBounds;
  18536. // Node needs repaint because the selection rectangle and static text must disappear.
  18537. InvalidateNode(FFocusedNode);
  18538. if not FEditLink.BeginEdit then
  18539. DoStateChange([], [tsEditing]);
  18540. end
  18541. else
  18542. DoStateChange([], [tsEditing]);
  18543. if not (tsEditing in FStates) then
  18544. FEditLink := nil;
  18545. end;
  18546. end;
  18547. end;
  18548. //----------------------------------------------------------------------------------------------------------------------
  18549. procedure TBaseVirtualTree.DoEndDrag(Target: TObject; X, Y: Integer);
  18550. // Does some housekeeping for VCL drag'n drop;
  18551. begin
  18552. inherited;
  18553. DragFinished;
  18554. end;
  18555. //----------------------------------------------------------------------------------------------------------------------
  18556. function TBaseVirtualTree.DoEndEdit: Boolean;
  18557. begin
  18558. StopTimer(EditTimer);
  18559. Result := (tsEditing in FStates) and FEditLink.EndEdit;
  18560. if Result then
  18561. begin
  18562. DoStateChange([], [tsEditing]);
  18563. FEditLink := nil;
  18564. if Assigned(FOnEdited) then
  18565. FOnEdited(Self, FFocusedNode, FEditColumn);
  18566. end;
  18567. DoStateChange([], [tsEditPending]);
  18568. end;
  18569. //----------------------------------------------------------------------------------------------------------------------
  18570. procedure TBaseVirtualTree.DoEndOperation(OperationKind: TVTOperationKind);
  18571. begin
  18572. if Assigned(FOnEndOperation) then
  18573. FOnEndOperation(Self, OperationKind);
  18574. end;
  18575. //----------------------------------------------------------------------------------------------------------------------
  18576. procedure TBaseVirtualTree.DoEnter();
  18577. begin
  18578. inherited;
  18579. EnsureNodeSelected();
  18580. end;
  18581. //----------------------------------------------------------------------------------------------------------------------
  18582. procedure TBaseVirtualTree.DoExpanded(Node: PVirtualNode);
  18583. begin
  18584. if Assigned(FOnExpanded) then
  18585. FOnExpanded(Self, Node);
  18586. if Assigned(FAccessibleItem) then
  18587. NotifyWinEvent(EVENT_OBJECT_STATECHANGE, Handle, OBJID_CLIENT, CHILDID_SELF);
  18588. end;
  18589. //----------------------------------------------------------------------------------------------------------------------
  18590. function TBaseVirtualTree.DoExpanding(Node: PVirtualNode): Boolean;
  18591. begin
  18592. Result := True;
  18593. if Assigned(FOnExpanding) then
  18594. FOnExpanding(Self, Node, Result);
  18595. end;
  18596. //----------------------------------------------------------------------------------------------------------------------
  18597. procedure TBaseVirtualTree.DoFocusChange(Node: PVirtualNode; Column: TColumnIndex);
  18598. begin
  18599. if Assigned(FOnFocusChanged) then
  18600. FOnFocusChanged(Self, Node, Column);
  18601. if Assigned(FAccessibleItem) then
  18602. begin
  18603. NotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, Handle, OBJID_CLIENT, CHILDID_SELF);
  18604. NotifyWinEvent(EVENT_OBJECT_NAMECHANGE, Handle, OBJID_CLIENT, CHILDID_SELF);
  18605. NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, Handle, OBJID_CLIENT, CHILDID_SELF);
  18606. NotifyWinEvent(EVENT_OBJECT_STATECHANGE, Handle, OBJID_CLIENT, CHILDID_SELF);
  18607. NotifyWinEvent(EVENT_OBJECT_SELECTION, Handle, OBJID_CLIENT, CHILDID_SELF);
  18608. NotifyWinEvent(EVENT_OBJECT_FOCUS, Handle, OBJID_CLIENT, CHILDID_SELF);
  18609. end;
  18610. end;
  18611. //----------------------------------------------------------------------------------------------------------------------
  18612. function TBaseVirtualTree.DoFocusChanging(OldNode, NewNode: PVirtualNode; OldColumn, NewColumn: TColumnIndex): Boolean;
  18613. begin
  18614. Result := (OldColumn = NewColumn) or FHeader.AllowFocus(NewColumn);
  18615. if Assigned(FOnFocusChanging) then
  18616. FOnFocusChanging(Self, OldNode, NewNode, OldColumn, NewColumn, Result);
  18617. end;
  18618. //----------------------------------------------------------------------------------------------------------------------
  18619. procedure TBaseVirtualTree.DoFocusNode(Node: PVirtualNode; Ask: Boolean);
  18620. begin
  18621. if not (tsEditing in FStates) or EndEditNode then
  18622. begin
  18623. if Node = FRoot then
  18624. Node := nil;
  18625. if (FFocusedNode <> Node) and (not Ask or DoFocusChanging(FFocusedNode, Node, FFocusedColumn, FFocusedColumn)) then
  18626. begin
  18627. if Assigned(FFocusedNode) then
  18628. begin
  18629. // Do automatic collapsing of last focused node if enabled. This is however only done if
  18630. // old and new focused node have a common parent node.
  18631. if (toAutoExpand in FOptions.FAutoOptions) and Assigned(Node) and (Node.Parent = FFocusedNode.Parent) and
  18632. (vsExpanded in FFocusedNode.States) then
  18633. ToggleNode(FFocusedNode)
  18634. else
  18635. InvalidateNode(FFocusedNode);
  18636. end;
  18637. FFocusedNode := Node;
  18638. end;
  18639. // Have to scroll the node into view, even it is the same node as before.
  18640. if Assigned(FFocusedNode) then
  18641. begin
  18642. // Make sure a valid column is set if columns are used and no column has currently the focus.
  18643. if FHeader.UseColumns and (not FHeader.FColumns.IsValidColumn(FFocusedColumn)) then
  18644. FFocusedColumn := FHeader.MainColumn;
  18645. // Do automatic expansion of the newly focused node if enabled.
  18646. if (toAutoExpand in FOptions.FAutoOptions) and not (vsExpanded in FFocusedNode.States) then
  18647. ToggleNode(FFocusedNode);
  18648. InvalidateNode(FFocusedNode);
  18649. if (FUpdateCount = 0) and not (toDisableAutoscrollOnFocus in FOptions.FAutoOptions) then
  18650. ScrollIntoView(FFocusedNode, (toCenterScrollIntoView in FOptions.SelectionOptions) and
  18651. (MouseButtonDown * FStates = []), not (toFullRowSelect in FOptions.SelectionOptions) );
  18652. end;
  18653. // Reset range anchor if necessary.
  18654. if FSelectionCount = 0 then
  18655. ResetRangeAnchor;
  18656. end;
  18657. end;
  18658. //----------------------------------------------------------------------------------------------------------------------
  18659. procedure TBaseVirtualTree.DoFreeNode(Node: PVirtualNode);
  18660. begin
  18661. // Prevent invalid references
  18662. if Node = FLastChangedNode then
  18663. FLastChangedNode := nil;
  18664. if Node = FCurrentHotNode then
  18665. FCurrentHotNode := nil;
  18666. if Node = FDropTargetNode then
  18667. FDropTargetNode := nil;
  18668. if Node = FLastStructureChangeNode then
  18669. FLastStructureChangeNode := nil;
  18670. if Node = FNextNodeToSelect then
  18671. FNextNodeToSelect := nil;
  18672. if Self.UpdateCount = 0 then
  18673. begin
  18674. // Omit this stuff if the control is in a BeginUpdate/EndUpdate bracket to increase performance
  18675. // We now try
  18676. // Make sure that CurrentNode does not point to an invalid node
  18677. if (toAlwaysSelectNode in TreeOptions.SelectionOptions) and (Node = GetFirstSelected()) then
  18678. begin
  18679. if Assigned(FNextNodeToSelect) then
  18680. // Select a new node if the currently selected node gets freed
  18681. Selected[FNextNodeToSelect] := True
  18682. else
  18683. begin
  18684. FNextNodeToSelect := Self.NodeParent[GetFirstSelected()];
  18685. if Assigned(FNextNodeToSelect) then
  18686. Selected[FNextNodeToSelect] := True;
  18687. end;//else
  18688. end;//if
  18689. end;
  18690. // fire event
  18691. if Assigned(FOnFreeNode) and ([vsInitialized, vsOnFreeNodeCallRequired] * Node.States <> []) then
  18692. FOnFreeNode(Self, Node);
  18693. FreeMem(Node);
  18694. if Self.UpdateCount = 0 then
  18695. EnsureNodeSelected();
  18696. end;
  18697. //----------------------------------------------------------------------------------------------------------------------
  18698. // These constants are defined in the platform SDK for W2K/XP, but not yet in Delphi.
  18699. const
  18700. SPI_GETTOOLTIPANIMATION = $1016;
  18701. SPI_GETTOOLTIPFADE = $1018;
  18702. function TBaseVirtualTree.DoGetAnimationType: THintAnimationType;
  18703. // Determines (depending on the properties settings and the system) which hint
  18704. // animation type is to be used.
  18705. var
  18706. Animation: BOOL;
  18707. begin
  18708. Result := FAnimationType;
  18709. if Result = hatSystemDefault then
  18710. begin
  18711. SystemParametersInfo(SPI_GETTOOLTIPANIMATION, 0, @Animation, 0);
  18712. if not Animation then
  18713. Result := hatNone
  18714. else
  18715. begin
  18716. SystemParametersInfo(SPI_GETTOOLTIPFADE, 0, @Animation, 0);
  18717. if Animation then
  18718. Result := hatFade
  18719. else
  18720. Result := hatSlide;
  18721. end;
  18722. end;
  18723. // Check availability of MMX if fading is requested.
  18724. if not MMXAvailable and (Result = hatFade) then
  18725. Result := hatSlide;
  18726. end;
  18727. //----------------------------------------------------------------------------------------------------------------------
  18728. function TBaseVirtualTree.DoGetCellContentMargin(Node: PVirtualNode; Column: TColumnIndex;
  18729. CellContentMarginType: TVTCellContentMarginType = ccmtAllSides; Canvas: TCanvas = nil): TPoint;
  18730. // Determines the margins of the content rectangle caused by DoBeforeCellPaint.
  18731. // Note that shrinking the content rectangle results in positive margins whereas enlarging the content rectangle results
  18732. // in negative margins.
  18733. var
  18734. CellRect,
  18735. ContentRect: TRect;
  18736. begin
  18737. Result := Point(0, 0);
  18738. if Assigned(FOnBeforeCellPaint) then // Otherwise DoBeforeCellPaint has no effect.
  18739. begin
  18740. if Canvas = nil then
  18741. Canvas := Self.Canvas;
  18742. // Determine then node's cell rectangle and content rectangle before calling DoBeforeCellPaint.
  18743. CellRect := GetDisplayRect(Node, Column, True);
  18744. ContentRect := CellRect;
  18745. DoBeforeCellPaint(Canvas, Node, Column, cpmGetContentMargin, CellRect, ContentRect);
  18746. // Calculate the changes caused by DoBeforeCellPaint.
  18747. case CellContentMarginType of
  18748. ccmtAllSides:
  18749. // Calculate the width difference and high difference.
  18750. Result := Point((CellRect.Right - CellRect.Left) - (ContentRect.Right - ContentRect.Left),
  18751. (CellRect.Bottom - CellRect.Top) - (ContentRect.Bottom - ContentRect.Top));
  18752. ccmtTopLeftOnly:
  18753. // Calculate the left margin and top margin only.
  18754. Result := Point(ContentRect.Left - CellRect.Left, ContentRect.Top - CellRect.Top);
  18755. ccmtBottomRightOnly:
  18756. // Calculate the right margin and bottom margin only.
  18757. Result := Point(CellRect.Right - ContentRect.Right, CellRect.Bottom - ContentRect.Bottom);
  18758. end;
  18759. end;
  18760. end;
  18761. //----------------------------------------------------------------------------------------------------------------------
  18762. procedure TBaseVirtualTree.DoGetCursor(var Cursor: TCursor);
  18763. begin
  18764. if Assigned(FOnGetCursor) then
  18765. FOnGetCursor(Self, Cursor);
  18766. end;
  18767. //----------------------------------------------------------------------------------------------------------------------
  18768. procedure TBaseVirtualTree.DoGetHeaderCursor(var Cursor: HCURSOR);
  18769. begin
  18770. if Assigned(FOnGetHeaderCursor) then
  18771. FOnGetHeaderCursor(FHeader, Cursor);
  18772. end;
  18773. //----------------------------------------------------------------------------------------------------------------------
  18774. function TBaseVirtualTree.DoGetImageIndex(Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
  18775. var Ghosted: Boolean; var Index: Integer): TCustomImageList;
  18776. // Queries the application/descendant about certain image properties for a node.
  18777. // Returns a custom image list if given by the callee, otherwise nil.
  18778. begin
  18779. Result := nil;
  18780. // First try the enhanced event to allow for custom image lists.
  18781. if Assigned(FOnGetImageEx) then
  18782. FOnGetImageEx(Self, Node, Kind, Column, Ghosted, Index, Result)
  18783. else
  18784. if Assigned(FOnGetImage) then
  18785. FOnGetImage(Self, Node, Kind, Column, Ghosted, Index);
  18786. end;
  18787. //----------------------------------------------------------------------------------------------------------------------
  18788. procedure TBaseVirtualTree.DoGetImageText(Node: PVirtualNode; Kind: TVTImageKind;
  18789. Column: TColumnIndex; var ImageText: UnicodeString);
  18790. // Queries the application/descendant about alternative image text for a node.
  18791. begin
  18792. if Assigned(FOnGetImageText) then
  18793. FOnGetImageText(Self, Node, Kind, Column, ImageText);
  18794. end;
  18795. //----------------------------------------------------------------------------------------------------------------------
  18796. procedure TBaseVirtualTree.DoGetLineStyle(var Bits: Pointer);
  18797. begin
  18798. if Assigned(FOnGetLineStyle) then
  18799. FOnGetLineStyle(Self, Bits);
  18800. end;
  18801. //----------------------------------------------------------------------------------------------------------------------
  18802. function TBaseVirtualTree.DoGetNodeHint(Node: PVirtualNode; Column: TColumnIndex;
  18803. var LineBreakStyle: TVTTooltipLineBreakStyle): UnicodeString;
  18804. begin
  18805. Result := Hint;
  18806. LineBreakStyle := hlbDefault;
  18807. end;
  18808. //----------------------------------------------------------------------------------------------------------------------
  18809. function TBaseVirtualTree.DoGetNodeTooltip(Node: PVirtualNode; Column: TColumnIndex;
  18810. var LineBreakStyle: TVTTooltipLineBreakStyle): UnicodeString;
  18811. begin
  18812. Result := Hint;
  18813. LineBreakStyle := hlbDefault;
  18814. end;
  18815. //----------------------------------------------------------------------------------------------------------------------
  18816. function TBaseVirtualTree.DoGetNodeExtraWidth(Node: PVirtualNode; Column: TColumnIndex; Canvas: TCanvas = nil): Integer;
  18817. // Returns the pixel width of extra space occupied by node contents (for example, static text).
  18818. begin
  18819. Result := 0;
  18820. end;
  18821. //----------------------------------------------------------------------------------------------------------------------
  18822. function TBaseVirtualTree.DoGetNodeWidth(Node: PVirtualNode; Column: TColumnIndex; Canvas: TCanvas = nil): Integer;
  18823. // Returns the pixel width of a node.
  18824. begin
  18825. Result := 0;
  18826. end;
  18827. //----------------------------------------------------------------------------------------------------------------------
  18828. function TBaseVirtualTree.DoGetPopupMenu(Node: PVirtualNode; Column: TColumnIndex; Position: TPoint): TPopupMenu;
  18829. // Queries the application whether there is a node specific popup menu.
  18830. var
  18831. Run: PVirtualNode;
  18832. AskParent: Boolean;
  18833. begin
  18834. Result := nil;
  18835. if Assigned(FOnGetPopupMenu) then
  18836. begin
  18837. Run := Node;
  18838. if Assigned(Run) then
  18839. begin
  18840. AskParent := True;
  18841. repeat
  18842. FOnGetPopupMenu(Self, Run, Column, Position, AskParent, Result);
  18843. Run := Run.Parent;
  18844. until (Run = FRoot) or Assigned(Result) or not AskParent;
  18845. end
  18846. else
  18847. FOnGetPopupMenu(Self, nil, -1, Position, AskParent, Result);
  18848. end;
  18849. end;
  18850. //----------------------------------------------------------------------------------------------------------------------
  18851. procedure TBaseVirtualTree.DoGetUserClipboardFormats(var Formats: TFormatEtcArray);
  18852. begin
  18853. if Assigned(FOnGetUserClipboardFormats) then
  18854. FOnGetUserClipboardFormats(Self, Formats);
  18855. end;
  18856. //----------------------------------------------------------------------------------------------------------------------
  18857. procedure TBaseVirtualTree.DoHeaderClick(HitInfo: TVTHeaderHitInfo);
  18858. begin
  18859. if Assigned(FOnHeaderClick) then
  18860. FOnHeaderClick(FHeader, HitInfo);
  18861. end;
  18862. //----------------------------------------------------------------------------------------------------------------------
  18863. procedure TBaseVirtualTree.DoHeaderDblClick(HitInfo: TVTHeaderHitInfo);
  18864. begin
  18865. if Assigned(FOnHeaderDblClick) then
  18866. FOnHeaderDblClick(FHeader, HitInfo);
  18867. end;
  18868. //----------------------------------------------------------------------------------------------------------------------
  18869. procedure TBaseVirtualTree.DoHeaderDragged(Column: TColumnIndex; OldPosition: TColumnPosition);
  18870. begin
  18871. if Assigned(FOnHeaderDragged) then
  18872. FOnHeaderDragged(FHeader, Column, OldPosition);
  18873. end;
  18874. //----------------------------------------------------------------------------------------------------------------------
  18875. procedure TBaseVirtualTree.DoHeaderDraggedOut(Column: TColumnIndex; DropPosition: TPoint);
  18876. begin
  18877. if Assigned(FOnHeaderDraggedOut) then
  18878. FOnHeaderDraggedOut(FHeader, Column, DropPosition);
  18879. end;
  18880. //----------------------------------------------------------------------------------------------------------------------
  18881. function TBaseVirtualTree.DoHeaderDragging(Column: TColumnIndex): Boolean;
  18882. begin
  18883. Result := True;
  18884. if Assigned(FOnHeaderDragging) then
  18885. FOnHeaderDragging(FHeader, Column, Result);
  18886. end;
  18887. //----------------------------------------------------------------------------------------------------------------------
  18888. procedure TBaseVirtualTree.DoHeaderDraw(Canvas: TCanvas; Column: TVirtualTreeColumn; R: TRect; Hover, Pressed: Boolean;
  18889. DropMark: TVTDropMarkMode);
  18890. begin
  18891. if Assigned(FOnHeaderDraw) then
  18892. FOnHeaderDraw(FHeader, Canvas, Column, R, Hover, Pressed, DropMark);
  18893. end;
  18894. //----------------------------------------------------------------------------------------------------------------------
  18895. procedure TBaseVirtualTree.DoHeaderDrawQueryElements(var PaintInfo: THeaderPaintInfo; var Elements: THeaderPaintElements);
  18896. begin
  18897. if Assigned(FOnHeaderDrawQueryElements) then
  18898. FOnHeaderDrawQueryElements(FHeader, PaintInfo, Elements);
  18899. end;
  18900. //----------------------------------------------------------------------------------------------------------------------
  18901. procedure TBaseVirtualTree.DoHeaderMouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  18902. begin
  18903. if Assigned(FOnHeaderMouseDown) then
  18904. FOnHeaderMouseDown(FHeader, Button, Shift, X, Y);
  18905. end;
  18906. //----------------------------------------------------------------------------------------------------------------------
  18907. procedure TBaseVirtualTree.DoHeaderMouseMove(Shift: TShiftState; X, Y: Integer);
  18908. begin
  18909. if Assigned(FOnHeaderMouseMove) then
  18910. FOnHeaderMouseMove(FHeader, Shift, X, Y);
  18911. end;
  18912. //----------------------------------------------------------------------------------------------------------------------
  18913. procedure TBaseVirtualTree.DoHeaderMouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  18914. begin
  18915. if Assigned(FOnHeaderMouseUp) then
  18916. FOnHeaderMouseUp(FHeader, Button, Shift, X, Y);
  18917. end;
  18918. //----------------------------------------------------------------------------------------------------------------------
  18919. procedure TBaseVirtualTree.DoHotChange(Old, New: PVirtualNode);
  18920. begin
  18921. if Assigned(FOnHotChange) then
  18922. FOnHotChange(Self, Old, New);
  18923. end;
  18924. //----------------------------------------------------------------------------------------------------------------------
  18925. function TBaseVirtualTree.DoIncrementalSearch(Node: PVirtualNode; const Text: UnicodeString): Integer;
  18926. begin
  18927. Result := 0;
  18928. if Assigned(FOnIncrementalSearch) then
  18929. FOnIncrementalSearch(Self, Node, Text, Result);
  18930. end;
  18931. //----------------------------------------------------------------------------------------------------------------------
  18932. function TBaseVirtualTree.DoInitChildren(Node: PVirtualNode; var ChildCount: Cardinal): Boolean;
  18933. /// The function calls the OnInitChildren and returns True if the event was called; it returns False if the caller can expect that no changes have been made to ChildCount
  18934. begin
  18935. if Assigned(FOnInitChildren) then
  18936. begin
  18937. FOnInitChildren(Self, Node, ChildCount);
  18938. Result := True;
  18939. end
  18940. else
  18941. Result := False;
  18942. end;
  18943. //----------------------------------------------------------------------------------------------------------------------
  18944. procedure TBaseVirtualTree.DoInitNode(Parent, Node: PVirtualNode; var InitStates: TVirtualNodeInitStates);
  18945. begin
  18946. if Assigned(FOnInitNode) then
  18947. FOnInitNode(Self, Parent, Node, InitStates);
  18948. end;
  18949. //----------------------------------------------------------------------------------------------------------------------
  18950. function TBaseVirtualTree.DoKeyAction(var CharCode: Word; var Shift: TShiftState): Boolean;
  18951. begin
  18952. Result := True;
  18953. if Assigned(FOnKeyAction) then
  18954. FOnKeyAction(Self, CharCode, Shift, Result);
  18955. end;
  18956. //----------------------------------------------------------------------------------------------------------------------
  18957. procedure TBaseVirtualTree.DoLoadUserData(Node: PVirtualNode; Stream: TStream);
  18958. begin
  18959. if Assigned(FOnLoadNode) then
  18960. if Node = FRoot then
  18961. FOnLoadNode(Self, nil, Stream)
  18962. else
  18963. FOnLoadNode(Self, Node, Stream);
  18964. end;
  18965. //----------------------------------------------------------------------------------------------------------------------
  18966. procedure TBaseVirtualTree.DoMeasureItem(TargetCanvas: TCanvas; Node: PVirtualNode; var NodeHeight: Integer);
  18967. begin
  18968. if Assigned(FOnMeasureItem) then
  18969. FOnMeasureItem(Self, TargetCanvas, Node, NodeHeight);
  18970. end;
  18971. //----------------------------------------------------------------------------------------------------------------------
  18972. procedure TBaseVirtualTree.DoMouseEnter();
  18973. begin
  18974. if Assigned(FOnMouseEnter) then
  18975. FOnMouseEnter(Self);
  18976. end;
  18977. //----------------------------------------------------------------------------------------------------------------------
  18978. procedure TBaseVirtualTree.DoMouseLeave;
  18979. begin
  18980. if Assigned(FOnMouseLeave) then
  18981. FOnMouseLeave(Self);
  18982. end;
  18983. //----------------------------------------------------------------------------------------------------------------------
  18984. procedure TBaseVirtualTree.DoNodeCopied(Node: PVirtualNode);
  18985. begin
  18986. if Assigned(FOnNodeCopied) then
  18987. FOnNodeCopied(Self, Node);
  18988. end;
  18989. //----------------------------------------------------------------------------------------------------------------------
  18990. function TBaseVirtualTree.DoNodeCopying(Node, NewParent: PVirtualNode): Boolean;
  18991. begin
  18992. Result := True;
  18993. if Assigned(FOnNodeCopying) then
  18994. FOnNodeCopying(Self, Node, NewParent, Result);
  18995. end;
  18996. //----------------------------------------------------------------------------------------------------------------------
  18997. procedure TBaseVirtualTree.DoNodeClick(const HitInfo: THitInfo);
  18998. begin
  18999. if Assigned(FOnNodeClick) then
  19000. FOnNodeClick(Self, HitInfo);
  19001. end;
  19002. //----------------------------------------------------------------------------------------------------------------------
  19003. procedure TBaseVirtualTree.DoNodeDblClick(const HitInfo: THitInfo);
  19004. begin
  19005. if Assigned(FOnNodeDblClick) then
  19006. FOnNodeDblClick(Self, HitInfo);
  19007. end;
  19008. //----------------------------------------------------------------------------------------------------------------------
  19009. function TBaseVirtualTree.DoNodeHeightDblClickResize(Node: PVirtualNode; Column: TColumnIndex; Shift: TShiftState;
  19010. P: TPoint): Boolean;
  19011. begin
  19012. Result := True;
  19013. if Assigned(FOnNodeHeightDblClickResize) then
  19014. FOnNodeHeightDblClickResize(Self, Node, Column, Shift, P, Result);
  19015. end;
  19016. //----------------------------------------------------------------------------------------------------------------------
  19017. function TBaseVirtualTree.DoNodeHeightTracking(Node: PVirtualNode; Column: TColumnIndex; Shift: TShiftState;
  19018. var TrackPoint: TPoint; P: TPoint): Boolean;
  19019. begin
  19020. Result := True;
  19021. if Assigned(FOnNodeHeightTracking) then
  19022. FOnNodeHeightTracking(Self, Node, Column, Shift, TrackPoint, P, Result);
  19023. end;
  19024. //----------------------------------------------------------------------------------------------------------------------
  19025. procedure TBaseVirtualTree.DoNodeMoved(Node: PVirtualNode);
  19026. begin
  19027. if Assigned(FOnNodeMoved) then
  19028. FOnNodeMoved(Self, Node);
  19029. end;
  19030. //----------------------------------------------------------------------------------------------------------------------
  19031. function TBaseVirtualTree.DoNodeMoving(Node, NewParent: PVirtualNode): Boolean;
  19032. begin
  19033. Result := True;
  19034. if Assigned(FOnNodeMoving) then
  19035. FOnNodeMoving(Self, Node, NewParent, Result);
  19036. end;
  19037. //----------------------------------------------------------------------------------------------------------------------
  19038. function TBaseVirtualTree.DoPaintBackground(Canvas: TCanvas; R: TRect): Boolean;
  19039. begin
  19040. Result := False;
  19041. if Assigned(FOnPaintBackground) then
  19042. FOnPaintBackground(Self, Canvas, R, Result);
  19043. end;
  19044. //----------------------------------------------------------------------------------------------------------------------
  19045. procedure TBaseVirtualTree.DoPaintDropMark(Canvas: TCanvas; Node: PVirtualNode; R: TRect);
  19046. // draws the drop mark into the given rectangle
  19047. // Note: Changed properties of the given canvas should be reset to their previous values.
  19048. var
  19049. SaveBrushColor: TColor;
  19050. SavePenStyle: TPenStyle;
  19051. begin
  19052. if FLastDropMode in [dmAbove, dmBelow] then
  19053. with Canvas do
  19054. begin
  19055. SavePenStyle := Pen.Style;
  19056. Pen.Style := psClear;
  19057. SaveBrushColor := Brush.Color;
  19058. Brush.Color := FColors.DropMarkColor;
  19059. if FLastDropMode = dmAbove then
  19060. begin
  19061. Polygon([Point(R.Left + 2, R.Top),
  19062. Point(R.Right - 2, R.Top),
  19063. Point(R.Right - 2, R.Top + 6),
  19064. Point(R.Right - 6, R.Top + 2),
  19065. Point(R.Left + 6 , R.Top + 2),
  19066. Point(R.Left + 2, R.Top + 6)
  19067. ]);
  19068. end
  19069. else
  19070. Polygon([Point(R.Left + 2, R.Bottom - 1),
  19071. Point(R.Right - 2, R.Bottom - 1),
  19072. Point(R.Right - 2, R.Bottom - 8),
  19073. Point(R.Right - 7, R.Bottom - 3),
  19074. Point(R.Left + 7 , R.Bottom - 3),
  19075. Point(R.Left + 2, R.Bottom - 8)
  19076. ]);
  19077. Brush.Color := SaveBrushColor;
  19078. Pen.Style := SavePenStyle;
  19079. end;
  19080. end;
  19081. //----------------------------------------------------------------------------------------------------------------------
  19082. procedure TBaseVirtualTree.DoPaintNode(var PaintInfo: TVTPaintInfo);
  19083. begin
  19084. end;
  19085. //----------------------------------------------------------------------------------------------------------------------
  19086. procedure TBaseVirtualTree.DoPopupMenu(Node: PVirtualNode; Column: TColumnIndex; Position: TPoint);
  19087. // Support for node dependent popup menus.
  19088. var
  19089. Menu: TPopupMenu;
  19090. begin
  19091. Menu := DoGetPopupMenu(Node, Column, Position);
  19092. if Assigned(Menu) then
  19093. begin
  19094. DoStateChange([tsPopupMenuShown]);
  19095. StopTimer(EditTimer);
  19096. Menu.PopupComponent := Self;
  19097. with ClientToScreen(Position) do
  19098. Menu.Popup(X, Y);
  19099. end;
  19100. end;
  19101. //----------------------------------------------------------------------------------------------------------------------
  19102. procedure TBaseVirtualTree.DoRemoveFromSelection(Node: PVirtualNode);
  19103. begin
  19104. if Assigned(FOnRemoveFromSelection) then
  19105. FOnRemoveFromSelection(Self, Node);
  19106. end;
  19107. //----------------------------------------------------------------------------------------------------------------------
  19108. function TBaseVirtualTree.DoRenderOLEData(const FormatEtcIn: TFormatEtc; out Medium: TStgMedium;
  19109. ForClipboard: Boolean): HRESULT;
  19110. begin
  19111. Result := E_FAIL;
  19112. if Assigned(FOnRenderOLEData) then
  19113. FOnRenderOLEData(Self, FormatEtcIn, Medium, ForClipboard, Result);
  19114. end;
  19115. //----------------------------------------------------------------------------------------------------------------------
  19116. procedure TBaseVirtualTree.DoReset(Node: PVirtualNode);
  19117. begin
  19118. if Assigned(FOnResetNode) then
  19119. FOnResetNode(Self, Node);
  19120. end;
  19121. //----------------------------------------------------------------------------------------------------------------------
  19122. procedure TBaseVirtualTree.DoSaveUserData(Node: PVirtualNode; Stream: TStream);
  19123. begin
  19124. if Assigned(FOnSaveNode) then
  19125. if Node = FRoot then
  19126. FOnSaveNode(Self, nil, Stream)
  19127. else
  19128. FOnSaveNode(Self, Node, Stream);
  19129. end;
  19130. //----------------------------------------------------------------------------------------------------------------------
  19131. procedure TBaseVirtualTree.DoScroll(DeltaX, DeltaY: Integer);
  19132. begin
  19133. if Assigned(FOnScroll) then
  19134. FOnScroll(Self, DeltaX, DeltaY);
  19135. end;
  19136. //----------------------------------------------------------------------------------------------------------------------
  19137. function TBaseVirtualTree.DoSetOffsetXY(Value: TPoint; Options: TScrollUpdateOptions; ClipRect: PRect = nil): Boolean;
  19138. // Actual offset setter used to scroll the client area, update scroll bars and invalidating the header (all optional).
  19139. // Returns True if the offset really changed otherwise False is returned.
  19140. var
  19141. DeltaX: Integer;
  19142. DeltaY: Integer;
  19143. DWPStructure: HDWP;
  19144. I: Integer;
  19145. P: TPoint;
  19146. R: TRect;
  19147. begin
  19148. // Range check, order is important here.
  19149. if Value.X < (ClientWidth - Integer(FRangeX)) then
  19150. Value.X := ClientWidth - Integer(FRangeX);
  19151. if Value.X > 0 then
  19152. Value.X := 0;
  19153. DeltaX := Value.X - FOffsetX;
  19154. if UseRightToLeftAlignment then
  19155. DeltaX := -DeltaX;
  19156. if Value.Y < (ClientHeight - Integer(FRangeY)) then
  19157. Value.Y := ClientHeight - Integer(FRangeY);
  19158. if Value.Y > 0 then
  19159. Value.Y := 0;
  19160. DeltaY := Value.Y - FOffsetY;
  19161. Result := (DeltaX <> 0) or (DeltaY <> 0);
  19162. if Result then
  19163. begin
  19164. FOffsetX := Value.X;
  19165. FOffsetY := Value.Y;
  19166. Result := True;
  19167. if tsHint in Self.FStates then
  19168. Application.CancelHint;
  19169. if FUpdateCount = 0 then
  19170. begin
  19171. // The drag image from VCL controls need special consideration.
  19172. if tsVCLDragging in FStates then
  19173. ImageList_DragShowNolock(False);
  19174. if (suoScrollClientArea in Options) and not (tsToggling in FStates) then
  19175. begin
  19176. // Have to invalidate the entire window if there's a background.
  19177. if (toShowBackground in FOptions.FPaintOptions) and (FBackground.Graphic is TBitmap) then
  19178. begin
  19179. // Since we don't use ScrollWindow here we have to move all client windows ourselves.
  19180. DWPStructure := BeginDeferWindowPos(ControlCount);
  19181. for I := 0 to ControlCount - 1 do
  19182. if Controls[I] is TWinControl then
  19183. begin
  19184. with Controls[I] as TWinControl do
  19185. DWPStructure := DeferWindowPos(DWPStructure, Handle, 0, Left + DeltaX, Top + DeltaY, 0, 0,
  19186. SWP_NOZORDER or SWP_NOACTIVATE or SWP_NOSIZE);
  19187. if DWPStructure = 0 then
  19188. Break;
  19189. end;
  19190. if DWPStructure <> 0 then
  19191. EndDeferWindowPos(DWPStructure);
  19192. InvalidateRect(Handle, nil, False);
  19193. end
  19194. else
  19195. begin
  19196. if (DeltaX <> 0) and (Header.Columns.GetVisibleFixedWidth > 0) then
  19197. begin
  19198. // When fixed columns exists we have to scroll separately horizontally and vertically.
  19199. // Horizontally is scroll only the client area not occupied by fixed columns and
  19200. // vertically entire client area (or clipping area if one exists).
  19201. R := ClientRect;
  19202. R.Left := Header.Columns.GetVisibleFixedWidth;
  19203. ScrollWindow(Handle, DeltaX, 0, @R, @R);
  19204. if DeltaY <> 0 then
  19205. ScrollWindow(Handle, 0, DeltaY, ClipRect, ClipRect);
  19206. end
  19207. else
  19208. ScrollWindow(Handle, DeltaX, DeltaY, ClipRect, ClipRect);
  19209. end;
  19210. end;
  19211. if suoUpdateNCArea in Options then
  19212. begin
  19213. if DeltaX <> 0 then
  19214. begin
  19215. if (suoRepaintHeader in Options) and (hoVisible in FHeader.FOptions) then
  19216. FHeader.Invalidate(nil);
  19217. if not (tsSizing in FStates) and (FScrollBarOptions.ScrollBars in [{$if CompilerVersion >= 24}System.UITypes.TScrollStyle.{$ifend}ssHorizontal, {$if CompilerVersion >= 24}System.UITypes.TScrollStyle.{$ifend}ssBoth]) then
  19218. UpdateHorizontalScrollBar(suoRepaintScrollBars in Options);
  19219. end;
  19220. if (DeltaY <> 0) and ([tsThumbTracking, tsSizing] * FStates = []) then
  19221. begin
  19222. UpdateVerticalScrollBar(suoRepaintScrollBars in Options);
  19223. if not (FHeader.UseColumns or IsMouseSelecting) and
  19224. (FScrollBarOptions.ScrollBars in [{$if CompilerVersion >= 24}System.UITypes.TScrollStyle.{$ifend}ssHorizontal, {$if CompilerVersion >= 24}System.UITypes.TScrollStyle.{$ifend}ssBoth]) then
  19225. UpdateHorizontalScrollBar(suoRepaintScrollBars in Options);
  19226. end;
  19227. end;
  19228. if tsVCLDragging in FStates then
  19229. ImageList_DragShowNolock(True);
  19230. end;
  19231. // Finally update "hot" node if hot tracking is activated
  19232. GetCursorPos(P);
  19233. P := ScreenToClient(P);
  19234. if PtInRect(ClientRect, P) then
  19235. HandleHotTrack(P.X, P.Y);
  19236. DoScroll(DeltaX, DeltaY);
  19237. Perform(CM_UPDATE_VCLSTYLE_SCROLLBARS,0,0);
  19238. end;
  19239. end;
  19240. //----------------------------------------------------------------------------------------------------------------------
  19241. procedure TBaseVirtualTree.DoShowScrollBar(Bar: Integer; Show: Boolean);
  19242. begin
  19243. ShowScrollBar(Handle, Bar, Show);
  19244. if Assigned(FOnShowScrollBar) then
  19245. FOnShowScrollBar(Self, Bar, Show);
  19246. end;
  19247. //----------------------------------------------------------------------------------------------------------------------
  19248. procedure TBaseVirtualTree.DoStartDrag(var DragObject: TDragObject);
  19249. begin
  19250. inherited;
  19251. // Check if the application created an own drag object. This is needed to pass the correct source in
  19252. // OnDragOver and OnDragDrop.
  19253. if Assigned(DragObject) then
  19254. DoStateChange([tsUserDragObject]);
  19255. end;
  19256. //----------------------------------------------------------------------------------------------------------------------
  19257. procedure TBaseVirtualTree.DoStartOperation(OperationKind: TVTOperationKind);
  19258. begin
  19259. if Assigned(FOnStartOperation) then
  19260. FOnStartOperation(Self, OperationKind);
  19261. end;
  19262. //----------------------------------------------------------------------------------------------------------------------
  19263. procedure TBaseVirtualTree.DoStateChange(Enter: TVirtualTreeStates; Leave: TVirtualTreeStates = []);
  19264. var
  19265. ActualEnter,
  19266. ActualLeave: TVirtualTreeStates;
  19267. begin
  19268. if Assigned(FOnStateChange) then
  19269. begin
  19270. ActualEnter := Enter - FStates;
  19271. ActualLeave := FStates * Leave;
  19272. if (ActualEnter + ActualLeave) <> [] then
  19273. FOnStateChange(Self, Enter, Leave);
  19274. end;
  19275. FStates := FStates + Enter - Leave;
  19276. end;
  19277. //----------------------------------------------------------------------------------------------------------------------
  19278. procedure TBaseVirtualTree.DoStructureChange(Node: PVirtualNode; Reason: TChangeReason);
  19279. begin
  19280. StopTimer(StructureChangeTimer);
  19281. if Assigned(FOnStructureChange) then
  19282. FOnStructureChange(Self, Node, Reason);
  19283. // This is a good place to reset the cached node and reason. These are the same as the values passed in here.
  19284. // This is necessary to allow descendants to override this method and get them.
  19285. DoStateChange([], [tsStructureChangePending]);
  19286. FLastStructureChangeNode := nil;
  19287. FLastStructureChangeReason := crIgnore;
  19288. end;
  19289. //----------------------------------------------------------------------------------------------------------------------
  19290. procedure TBaseVirtualTree.DoTimerScroll;
  19291. var
  19292. P,
  19293. ClientP: TPoint;
  19294. InRect,
  19295. Panning: Boolean;
  19296. R,
  19297. ClipRect: TRect;
  19298. DeltaX,
  19299. DeltaY: Integer;
  19300. begin
  19301. GetCursorPos(P);
  19302. R := ClientRect;
  19303. ClipRect := R;
  19304. MapWindowPoints(Handle, 0, R, 2);
  19305. InRect := PtInRect(R, P);
  19306. ClientP := ScreenToClient(P);
  19307. Panning := [tsWheelPanning, tsWheelScrolling] * FStates <> [];
  19308. if IsMouseSelecting or InRect or Panning then
  19309. begin
  19310. DeltaX := 0;
  19311. DeltaY := 0;
  19312. if sdUp in FScrollDirections then
  19313. begin
  19314. if Panning then
  19315. DeltaY := FLastClickPos.Y - ClientP.Y - 8
  19316. else
  19317. if InRect then
  19318. DeltaY := Min(FScrollBarOptions.FIncrementY, ClientHeight)
  19319. else
  19320. DeltaY := Min(FScrollBarOptions.FIncrementY, ClientHeight) * Abs(R.Top - P.Y);
  19321. if FOffsetY = 0 then
  19322. Exclude(FScrollDirections, sdUp);
  19323. end;
  19324. if sdDown in FScrollDirections then
  19325. begin
  19326. if Panning then
  19327. DeltaY := FLastClickPos.Y - ClientP.Y + 8
  19328. else
  19329. if InRect then
  19330. DeltaY := -Min(FScrollBarOptions.FIncrementY, ClientHeight)
  19331. else
  19332. DeltaY := -Min(FScrollBarOptions.FIncrementY, ClientHeight) * Abs(P.Y - R.Bottom);
  19333. if (ClientHeight - FOffsetY) = Integer(FRangeY) then
  19334. Exclude(FScrollDirections, sdDown);
  19335. end;
  19336. if sdLeft in FScrollDirections then
  19337. begin
  19338. if Panning then
  19339. DeltaX := FLastClickPos.X - ClientP.X - 8
  19340. else
  19341. if InRect then
  19342. DeltaX := FScrollBarOptions.FIncrementX
  19343. else
  19344. DeltaX := FScrollBarOptions.FIncrementX * Abs(R.Left - P.X);
  19345. if FEffectiveOffsetX = 0 then
  19346. Exclude(FScrollDirections, sdleft);
  19347. end;
  19348. if sdRight in FScrollDirections then
  19349. begin
  19350. if Panning then
  19351. DeltaX := FLastClickPos.X - ClientP.X + 8
  19352. else
  19353. if InRect then
  19354. DeltaX := -FScrollBarOptions.FIncrementX
  19355. else
  19356. DeltaX := -FScrollBarOptions.FIncrementX * Abs(P.X - R.Right);
  19357. if (ClientWidth + FEffectiveOffsetX) = Integer(FRangeX) then
  19358. Exclude(FScrollDirections, sdRight);
  19359. end;
  19360. if UseRightToLeftAlignment then
  19361. DeltaX := - DeltaX;
  19362. if IsMouseSelecting then
  19363. begin
  19364. // In order to avoid scrolling the area which needs a repaint due to the changed selection rectangle
  19365. // we limit the scroll area explicitely.
  19366. OffsetRect(ClipRect, DeltaX, DeltaY);
  19367. DoSetOffsetXY(Point(FOffsetX + DeltaX, FOffsetY + DeltaY), DefaultScrollUpdateFlags, @ClipRect);
  19368. // When selecting with the mouse then either update only the parts of the window which have been uncovered
  19369. // by the scroll operation if no change in the selection happend or invalidate and redraw the entire
  19370. // client area otherwise (to avoid the time consuming task of determining the display rectangles of every
  19371. // changed node).
  19372. if CalculateSelectionRect(ClientP.X, ClientP.Y) and HandleDrawSelection(ClientP.X, ClientP.Y) then
  19373. InvalidateRect(Handle, nil, False)
  19374. else
  19375. begin
  19376. // The selection did not change so invalidate only the part of the window which really needs an update.
  19377. // 1) Invalidate the parts uncovered by the scroll operation. Add another offset range, we have to
  19378. // scroll only one stripe but have to update two.
  19379. OffsetRect(ClipRect, DeltaX, DeltaY);
  19380. SubtractRect(ClipRect, ClientRect, ClipRect);
  19381. InvalidateRect(Handle, @ClipRect, False);
  19382. // 2) Invalidate the selection rectangles.
  19383. UnionRect(ClipRect, OrderRect(FNewSelRect), OrderRect(FLastSelRect));
  19384. OffsetRect(ClipRect, FOffsetX, FOffsetY);
  19385. InvalidateRect(Handle, @ClipRect, False);
  19386. end;
  19387. end
  19388. else
  19389. begin
  19390. // Scroll only if there is no drag'n drop in progress. Drag'n drop scrolling is handled in DragOver.
  19391. if ((FDragManager = nil) or not DragManager.IsDropTarget) and ((DeltaX <> 0) or (DeltaY <> 0)) then
  19392. DoSetOffsetXY(Point(FOffsetX + DeltaX, FOffsetY + DeltaY), DefaultScrollUpdateFlags, nil);
  19393. end;
  19394. UpdateWindow(Handle);
  19395. if (FScrollDirections = []) and ([tsWheelPanning, tsWheelScrolling] * FStates = []) then
  19396. begin
  19397. StopTimer(ScrollTimer);
  19398. DoStateChange([], [tsScrollPending, tsScrolling]);
  19399. end;
  19400. end;
  19401. end;
  19402. //----------------------------------------------------------------------------------------------------------------------
  19403. procedure TBaseVirtualTree.DoUpdating(State: TVTUpdateState);
  19404. begin
  19405. if Assigned(FOnUpdating) then
  19406. FOnUpdating(Self, State);
  19407. end;
  19408. //----------------------------------------------------------------------------------------------------------------------
  19409. function TBaseVirtualTree.DoValidateCache: Boolean;
  19410. // This method fills the cache, which is used to speed up searching for nodes.
  19411. // The strategy is simple: Take the current number of visible nodes and distribute evenly a number of marks
  19412. // (which are stored in FPositionCache) so that iterating through the tree doesn't cost too much time.
  19413. // If there are less than 'CacheThreshold' nodes in the tree then the cache remains empty.
  19414. // Result is True if the cache was filled without interruption, otherwise False.
  19415. // Note: You can adjust the maximum number of nodes between two cache entries by changing CacheThreshold.
  19416. var
  19417. EntryCount,
  19418. CurrentTop,
  19419. Index: Cardinal;
  19420. CurrentNode,
  19421. Temp: PVirtualNode;
  19422. begin
  19423. EntryCount := 0;
  19424. if not (tsStopValidation in FStates) then
  19425. begin
  19426. if FStartIndex = 0 then
  19427. FPositionCache := nil;
  19428. EntryCount := CalculateCacheEntryCount;
  19429. SetLength(FPositionCache, EntryCount);
  19430. if FStartIndex > EntryCount then
  19431. FStartIndex := EntryCount;
  19432. // Optimize validation by starting with FStartIndex if set.
  19433. if (FStartIndex > 0) and Assigned(FPositionCache[FStartIndex - 1].Node) then
  19434. begin
  19435. // Index is the current entry in FPositionCache.
  19436. Index := FStartIndex - 1;
  19437. // Running term for absolute top value.
  19438. CurrentTop := FPositionCache[Index].AbsoluteTop;
  19439. // Running node pointer.
  19440. CurrentNode := FPositionCache[Index].Node;
  19441. end
  19442. else
  19443. begin
  19444. // Index is the current entry in FPositionCache.
  19445. Index := 0;
  19446. // Running term for absolute top value.
  19447. CurrentTop := 0;
  19448. // Running node pointer.
  19449. CurrentNode := GetFirstVisibleNoInit(nil, True);
  19450. end;
  19451. // EntryCount serves as counter for processed nodes here. This value can always start at 0 as
  19452. // the validation either starts also at index 0 or an index which is always a multiple of CacheThreshold
  19453. // and EntryCount is only used with modulo CacheThreshold.
  19454. EntryCount := 0;
  19455. if Assigned(CurrentNode) then
  19456. begin
  19457. while not (tsStopValidation in FStates) do
  19458. begin
  19459. // If the cache is full then stop the loop.
  19460. if (Integer(Index) > Length(FPositionCache)) then // ADDED: 17.09.2013 - Veit Zimmermann
  19461. Break; // ADDED: 17.09.2013 - Veit Zimmermann
  19462. if (EntryCount mod CacheThreshold) = 0 then
  19463. begin
  19464. // New cache entry to set up.
  19465. with FPositionCache[Index] do
  19466. begin
  19467. Node := CurrentNode;
  19468. AbsoluteTop := CurrentTop;
  19469. end;
  19470. Inc(Index);
  19471. end;
  19472. Inc(CurrentTop, NodeHeight[CurrentNode]);
  19473. // Advance to next visible node.
  19474. Temp := GetNextVisibleNoInit(CurrentNode, True);
  19475. // If there is no further node then stop the loop.
  19476. if (Temp = nil) then // CHANGED: 17.09.2013 - Veit Zimmermann
  19477. Break; // CHANGED: 17.09.2013 - Veit Zimmermann
  19478. CurrentNode := Temp;
  19479. Inc(EntryCount);
  19480. end;
  19481. end;
  19482. // Finalize the position cache so no nil entry remains there.
  19483. if not (tsStopValidation in FStates) and (Integer(Index) <= High(FPositionCache)) then
  19484. begin
  19485. SetLength(FPositionCache, Index + 1);
  19486. with FPositionCache[Index] do
  19487. begin
  19488. Node := CurrentNode;
  19489. AbsoluteTop := CurrentTop;
  19490. end;
  19491. end;
  19492. end;
  19493. Result := (EntryCount > 0) and not (tsStopValidation in FStates);
  19494. // In variable node height mode it might have happend that some or all of the nodes have been adjusted in their
  19495. // height. During validation updates of the scrollbars is disabled so let's do this here.
  19496. if Result and (toVariableNodeHeight in FOptions.FMiscOptions) then
  19497. begin
  19498. UpdateScrollBars(True);
  19499. end;
  19500. end;
  19501. //----------------------------------------------------------------------------------------------------------------------
  19502. procedure TBaseVirtualTree.DragAndDrop(AllowedEffects: Dword; DataObject: IDataObject; var DragEffect: LongInt);
  19503. {$IF CompilerVersion >= 22}
  19504. var
  19505. lDragEffect: DWord; // required for type compatibility with SHDoDragDrop
  19506. {$ifend}
  19507. begin
  19508. {$IF CompilerVersion >= 22}
  19509. if IsWinVistaOrAbove then
  19510. begin
  19511. lDragEffect := DWord(DragEffect);
  19512. SHDoDragDrop(Self.Handle, DataObject, nil, AllowedEffects, lDragEffect); // supports drag hints on Windows Vista and later
  19513. DragEffect := LongInt(lDragEffect);
  19514. end
  19515. else
  19516. {$ifend}
  19517. ActiveX.DoDragDrop(DataObject, DragManager as IDropSource, AllowedEffects, DragEffect);
  19518. end;
  19519. //----------------------------------------------------------------------------------------------------------------------
  19520. procedure TBaseVirtualTree.DragCanceled;
  19521. // Does some housekeeping for VCL drag'n drop;
  19522. begin
  19523. inherited;
  19524. DragFinished;
  19525. end;
  19526. //----------------------------------------------------------------------------------------------------------------------
  19527. function TBaseVirtualTree.DragDrop(const DataObject: IDataObject; KeyState: Integer; Pt: TPoint;
  19528. var Effect: Integer): HResult;
  19529. var
  19530. Shift: TShiftState;
  19531. EnumFormat: IEnumFormatEtc;
  19532. Fetched: Integer;
  19533. OLEFormat: TFormatEtc;
  19534. Formats: TFormatArray;
  19535. begin
  19536. StopTimer(ExpandTimer);
  19537. StopTimer(ScrollTimer);
  19538. DoStateChange([], [tsScrollPending, tsScrolling]);
  19539. Formats := nil;
  19540. // Ask explicitly again whether the action is allowed. Otherwise we may accept a drop which is intentionally not
  19541. // allowed but cannot be prevented by the application because when the tree was scrolling while dropping
  19542. // no DragOver event is created by the OLE subsystem.
  19543. Result := DragOver(DragManager.DragSource, KeyState, dsDragMove, Pt, Effect);
  19544. try
  19545. if (Result <> NOERROR) or ((Effect and not DROPEFFECT_SCROLL) = DROPEFFECT_NONE) then
  19546. Result := E_FAIL
  19547. else
  19548. begin
  19549. try
  19550. Shift := KeysToShiftState(KeyState);
  19551. if tsLeftButtonDown in FStates then
  19552. Include(Shift, ssLeft);
  19553. if tsMiddleButtonDown in FStates then
  19554. Include(Shift, ssMiddle);
  19555. if tsRightButtonDown in FStates then
  19556. Include(Shift, ssRight);
  19557. Pt := ScreenToClient(Pt);
  19558. // Determine which formats we can get and pass them along with the data object to the drop handler.
  19559. Result := DataObject.EnumFormatEtc(DATADIR_GET, EnumFormat);
  19560. if Failed(Result) then
  19561. Abort;
  19562. Result := EnumFormat.Reset;
  19563. if Failed(Result) then
  19564. Abort;
  19565. // create a list of available formats
  19566. while EnumFormat.Next(1, OLEFormat, @Fetched) = S_OK do
  19567. begin
  19568. SetLength(Formats, Length(Formats) + 1);
  19569. Formats[High(Formats)] := OLEFormat.cfFormat;
  19570. end;
  19571. DoDragDrop(DragManager.DragSource, DataObject, Formats, Shift, Pt, Effect, FLastDropMode);
  19572. except
  19573. // An unhandled exception here leaks memory.
  19574. Application.HandleException(Self);
  19575. Result := E_UNEXPECTED;
  19576. end;
  19577. end;
  19578. finally
  19579. if Assigned(FDropTargetNode) then
  19580. begin
  19581. InvalidateNode(FDropTargetNode);
  19582. FDropTargetNode := nil;
  19583. end;
  19584. end;
  19585. end;
  19586. //----------------------------------------------------------------------------------------------------------------------
  19587. function TBaseVirtualTree.DragEnter(KeyState: Integer; Pt: TPoint; var Effect: Integer): HResult;
  19588. // callback routine for the drop target interface
  19589. var
  19590. Shift: TShiftState;
  19591. Accept: Boolean;
  19592. R: TRect;
  19593. HitInfo: THitInfo;
  19594. begin
  19595. try
  19596. // Determine acceptance of drag operation and reset scroll start time.
  19597. FDragScrollStart := 0;
  19598. Shift := KeysToShiftState(KeyState);
  19599. if tsLeftButtonDown in FStates then
  19600. Include(Shift, ssLeft);
  19601. if tsMiddleButtonDown in FStates then
  19602. Include(Shift, ssMiddle);
  19603. if tsRightButtonDown in FStates then
  19604. Include(Shift, ssRight);
  19605. Pt := ScreenToClient(Pt);
  19606. Effect := SuggestDropEffect(DragManager.DragSource, Shift, Pt, Effect);
  19607. Accept := DoDragOver(DragManager.DragSource, Shift, dsDragEnter, Pt, FLastDropMode, Effect);
  19608. if not Accept then
  19609. Effect := DROPEFFECT_NONE
  19610. else
  19611. begin
  19612. // Set initial drop target node and drop mode.
  19613. GetHitTestInfoAt(Pt.X, Pt.Y, True, HitInfo);
  19614. if Assigned(HitInfo.HitNode) then
  19615. begin
  19616. FDropTargetNode := HitInfo.HitNode;
  19617. R := GetDisplayRect(HitInfo.HitNode, FHeader.MainColumn, False);
  19618. if (hiOnItemLabel in HitInfo.HitPositions) or ((hiOnItem in HitInfo.HitPositions) and
  19619. ((toFullRowDrag in FOptions.FMiscOptions) or (toFullRowSelect in FOptions.FSelectionOptions)))then
  19620. FLastDropMode := dmOnNode
  19621. else
  19622. if ((R.Top + R.Bottom) div 2) > Pt.Y then
  19623. FLastDropMode := dmAbove
  19624. else
  19625. FLastDropMode := dmBelow;
  19626. end
  19627. else
  19628. FLastDropMode := dmNowhere;
  19629. end;
  19630. // If the drag source is a virtual tree then we know how to control the drag image
  19631. // and can show it even if the source is not the target tree.
  19632. // This is only necessary if we cannot use the drag image helper interfaces.
  19633. if not DragManager.DropTargetHelperSupported and Assigned(DragManager.DragSource) then
  19634. DragManager.DragSource.FDragImage.ShowDragImage;
  19635. Result := NOERROR;
  19636. except
  19637. Result := E_UNEXPECTED;
  19638. end;
  19639. end;
  19640. //----------------------------------------------------------------------------------------------------------------------
  19641. procedure TBaseVirtualTree.DragFinished;
  19642. // Called by DragCancelled or EndDrag to make up for the still missing mouse button up messages.
  19643. // These are important for such important things like popup menus.
  19644. var
  19645. P: TPoint;
  19646. begin
  19647. if [tsOLEDragging, tsVCLDragPending, tsVCLDragging, tsVCLDragFinished] * FStates = [] then
  19648. Exit;
  19649. DoStateChange([], [tsVCLDragPending, tsVCLDragging, tsUserDragObject, tsVCLDragFinished]);
  19650. GetCursorPos(P);
  19651. P := ScreenToClient(P);
  19652. if tsRightButtonDown in FStates then
  19653. Perform(WM_RBUTTONUP, 0, LPARAM(Longint(PointToSmallPoint(P))))
  19654. else
  19655. if tsMiddleButtonDown in FStates then
  19656. Perform(WM_MBUTTONUP, 0, LPARAM(Longint(PointToSmallPoint(P))))
  19657. else
  19658. Perform(WM_LBUTTONUP, 0, LPARAM(Longint(PointToSmallPoint(P))));
  19659. end;
  19660. //----------------------------------------------------------------------------------------------------------------------
  19661. procedure TBaseVirtualTree.DragLeave;
  19662. var
  19663. Effect: Integer;
  19664. begin
  19665. StopTimer(ExpandTimer);
  19666. if not DragManager.DropTargetHelperSupported and Assigned(DragManager.DragSource) then
  19667. DragManager.DragSource.FDragImage.HideDragImage;
  19668. if Assigned(FDropTargetNode) then
  19669. begin
  19670. InvalidateNode(FDropTargetNode);
  19671. FDropTargetNode := nil;
  19672. end;
  19673. UpdateWindow(Handle);
  19674. Effect := 0;
  19675. DoDragOver(nil, [], dsDragLeave, Point(0, 0), FLastDropMode, Effect);
  19676. end;
  19677. //----------------------------------------------------------------------------------------------------------------------
  19678. function TBaseVirtualTree.DragOver(Source: TObject; KeyState: Integer; DragState: TDragState; Pt: TPoint;
  19679. var Effect: LongInt): HResult;
  19680. // callback routine for the drop target interface
  19681. var
  19682. Shift: TShiftState;
  19683. Accept,
  19684. DragImageWillMove,
  19685. WindowScrolled: Boolean;
  19686. OldR, R: TRect;
  19687. NewDropMode: TDropMode;
  19688. HitInfo: THitInfo;
  19689. DragPos: TPoint;
  19690. Tree: TBaseVirtualTree;
  19691. LastNode: PVirtualNode;
  19692. DeltaX,
  19693. DeltaY: Integer;
  19694. ScrollOptions: TScrollUpdateOptions;
  19695. begin
  19696. if not DragManager.DropTargetHelperSupported and (Source is TBaseVirtualTree) then
  19697. begin
  19698. Tree := Source as TBaseVirtualTree;
  19699. ScrollOptions := [suoUpdateNCArea];
  19700. end
  19701. else
  19702. begin
  19703. Tree := nil;
  19704. ScrollOptions := DefaultScrollUpdateFlags;
  19705. end;
  19706. try
  19707. DragPos := Pt;
  19708. Pt := ScreenToClient(Pt);
  19709. // Check if we have to scroll the client area.
  19710. FScrollDirections := DetermineScrollDirections(Pt.X, Pt.Y);
  19711. DeltaX := 0;
  19712. DeltaY := 0;
  19713. if FScrollDirections <> [] then
  19714. begin
  19715. // Determine amount to scroll.
  19716. if sdUp in FScrollDirections then
  19717. begin
  19718. DeltaY := Min(FScrollBarOptions.FIncrementY, ClientHeight);
  19719. if FOffsetY = 0 then
  19720. Exclude(FScrollDirections, sdUp);
  19721. end;
  19722. if sdDown in FScrollDirections then
  19723. begin
  19724. DeltaY := -Min(FScrollBarOptions.FIncrementY, ClientHeight);
  19725. if (ClientHeight - FOffsetY) = Integer(FRangeY) then
  19726. Exclude(FScrollDirections, sdDown);
  19727. end;
  19728. if sdLeft in FScrollDirections then
  19729. begin
  19730. DeltaX := FScrollBarOptions.FIncrementX;
  19731. if FEffectiveOffsetX = 0 then
  19732. Exclude(FScrollDirections, sdleft);
  19733. end;
  19734. if sdRight in FScrollDirections then
  19735. begin
  19736. DeltaX := -FScrollBarOptions.FIncrementX;
  19737. if (ClientWidth + FEffectiveOffsetX) = Integer(FRangeX) then
  19738. Exclude(FScrollDirections, sdRight);
  19739. end;
  19740. WindowScrolled := DoSetOffsetXY(Point(FOffsetX + DeltaX, FOffsetY + DeltaY), ScrollOptions, nil);
  19741. end
  19742. else
  19743. WindowScrolled := False;
  19744. // Determine acceptance of drag operation as well as drag target.
  19745. Shift := KeysToShiftState(KeyState);
  19746. if tsLeftButtonDown in FStates then
  19747. Include(Shift, ssLeft);
  19748. if tsMiddleButtonDown in FStates then
  19749. Include(Shift, ssMiddle);
  19750. if tsRightButtonDown in FStates then
  19751. Include(Shift, ssRight);
  19752. GetHitTestInfoAt(Pt.X, Pt.Y, True, HitInfo);
  19753. if Assigned(HitInfo.HitNode) then
  19754. R := GetDisplayRect(HitInfo.HitNode, NoColumn, False)
  19755. else
  19756. R := Rect(0, 0, 0, 0);
  19757. NewDropMode := DetermineDropMode(Pt, HitInfo, R);
  19758. if Assigned(Tree) then
  19759. DragImageWillMove := Tree.FDragImage.WillMove(DragPos)
  19760. else
  19761. DragImageWillMove := False;
  19762. if (HitInfo.HitNode <> FDropTargetNode) or (FLastDropMode <> NewDropMode) then
  19763. begin
  19764. // Something in the tree will change. This requires to update the screen and/or the drag image.
  19765. FLastDropMode := NewDropMode;
  19766. if HitInfo.HitNode <> FDropTargetNode then
  19767. begin
  19768. StopTimer(ExpandTimer);
  19769. // The last target node is needed for the rectangle determination but must already be set for
  19770. // the recapture call, hence it must be stored somewhere.
  19771. LastNode := FDropTargetNode;
  19772. FDropTargetNode := HitInfo.HitNode;
  19773. // In order to show a selection rectangle a column must be focused.
  19774. if FFocusedColumn <= NoColumn then
  19775. FFocusedColumn := FHeader.MainColumn;
  19776. if Assigned(LastNode) and Assigned(FDropTargetNode) then
  19777. begin
  19778. // Optimize the case that the selection moved between two nodes.
  19779. OldR := GetDisplayRect(LastNode, NoColumn, False);
  19780. UnionRect(R, R, OldR);
  19781. if Assigned(Tree) then
  19782. begin
  19783. if WindowScrolled then
  19784. UpdateWindowAndDragImage(Tree, ClientRect, True, not DragImageWillMove)
  19785. else
  19786. UpdateWindowAndDragImage(Tree, R, False, not DragImageWillMove);
  19787. end
  19788. else
  19789. InvalidateRect(Handle, @R, False);
  19790. end
  19791. else
  19792. begin
  19793. if Assigned(LastNode) then
  19794. begin
  19795. // Repaint last target node.
  19796. OldR := GetDisplayRect(LastNode, NoColumn, False);
  19797. if Assigned(Tree) then
  19798. begin
  19799. if WindowScrolled then
  19800. UpdateWindowAndDragImage(Tree, ClientRect, WindowScrolled, not DragImageWillMove)
  19801. else
  19802. UpdateWindowAndDragImage(Tree, OldR, False, not DragImageWillMove);
  19803. end
  19804. else
  19805. InvalidateRect(Handle, @OldR, False);
  19806. end
  19807. else
  19808. begin
  19809. if Assigned(Tree) then
  19810. begin
  19811. if WindowScrolled then
  19812. UpdateWindowAndDragImage(Tree, ClientRect, WindowScrolled, not DragImageWillMove)
  19813. else
  19814. UpdateWindowAndDragImage(Tree, R, False, not DragImageWillMove);
  19815. end
  19816. else
  19817. InvalidateRect(Handle, @R, False);
  19818. end;
  19819. end;
  19820. // Start auto expand timer if necessary.
  19821. if (toAutoDropExpand in FOptions.FAutoOptions) and Assigned(FDropTargetNode) and
  19822. (vsHasChildren in FDropTargetNode.States) then
  19823. SetTimer(Handle, ExpandTimer, FAutoExpandDelay, nil);
  19824. end
  19825. else
  19826. begin
  19827. // Only the drop mark position changed so invalidate the current drop target node.
  19828. if Assigned(Tree) then
  19829. begin
  19830. if WindowScrolled then
  19831. UpdateWindowAndDragImage(Tree, ClientRect, WindowScrolled, not DragImageWillMove)
  19832. else
  19833. UpdateWindowAndDragImage(Tree, R, False, not DragImageWillMove);
  19834. end
  19835. else
  19836. InvalidateRect(Handle, @R, False);
  19837. end;
  19838. end
  19839. else
  19840. begin
  19841. // No change in the current drop target or drop mode. This might still mean horizontal or vertical scrolling.
  19842. if Assigned(Tree) and ((DeltaX <> 0) or (DeltaY <> 0)) then
  19843. UpdateWindowAndDragImage(Tree, ClientRect, WindowScrolled, not DragImageWillMove);
  19844. end;
  19845. Update;
  19846. if Assigned(Tree) and DragImageWillMove then
  19847. Tree.FDragImage.DragTo(DragPos, False);
  19848. Effect := SuggestDropEffect(Source, Shift, Pt, Effect);
  19849. Accept := DoDragOver(Source, Shift, DragState, Pt, FLastDropMode, Effect);
  19850. if not Accept then
  19851. Effect := DROPEFFECT_NONE;
  19852. if WindowScrolled then
  19853. Effect := Effect or Integer(DROPEFFECT_SCROLL);
  19854. Result := NOERROR;
  19855. except
  19856. Result := E_UNEXPECTED;
  19857. end;
  19858. end;
  19859. //----------------------------------------------------------------------------------------------------------------------
  19860. procedure TBaseVirtualTree.DrawDottedHLine(const PaintInfo: TVTPaintInfo; Left, Right, Top: Integer);
  19861. // Draws a horizontal line with alternating pixels (this style is not supported for pens under Win9x).
  19862. var
  19863. R: TRect;
  19864. begin
  19865. with PaintInfo, Canvas do
  19866. begin
  19867. Brush.Color := FColors.BackGroundColor;
  19868. R := Rect(Min(Left, Right), Top, Max(Left, Right) + 1, Top + 1);
  19869. Windows.FillRect(Handle, R, FDottedBrush);
  19870. end;
  19871. end;
  19872. //----------------------------------------------------------------------------------------------------------------------
  19873. procedure TBaseVirtualTree.DrawDottedVLine(const PaintInfo: TVTPaintInfo; Top, Bottom, Left: Integer; UseSelectedBkColor: Boolean = False);
  19874. // Draws a horizontal line with alternating pixels (this style is not supported for pens under Win9x).
  19875. var
  19876. R: TRect;
  19877. begin
  19878. with PaintInfo, Canvas do
  19879. begin
  19880. if UseSelectedBkColor then
  19881. begin
  19882. if Focused or (toPopupMode in FOptions.FPaintOptions) then
  19883. Brush.Color := FColors.FocusedSelectionColor
  19884. else
  19885. Brush.Color := FColors.UnfocusedSelectionColor;
  19886. end
  19887. else
  19888. Brush.Color := FColors.BackGroundColor;
  19889. R := Rect(Left, Min(Top, Bottom), Left + 1, Max(Top, Bottom) + 1);
  19890. Windows.FillRect(Handle, R, FDottedBrush);
  19891. end;
  19892. end;
  19893. //----------------------------------------------------------------------------------------------------------------------
  19894. procedure TBaseVirtualTree.EndOperation(OperationKind: TVTOperationKind);
  19895. // Called to indicate that a long-running operation has finished.
  19896. begin
  19897. Assert(FOperationCount > 0, 'EndOperation must not be called when no operation in progress.');
  19898. Dec(FOperationCount);
  19899. DoEndOperation(OperationKind);
  19900. end;
  19901. //----------------------------------------------------------------------------------------------------------------------
  19902. procedure TBaseVirtualTree.EnsureNodeFocused();
  19903. begin
  19904. if FocusedNode = nil then
  19905. FocusedNode := Self.GetFirstVisible();
  19906. end;
  19907. //----------------------------------------------------------------------------------------------------------------------
  19908. procedure TBaseVirtualTree.EnsureNodeSelected;
  19909. begin
  19910. if (toAlwaysSelectNode in TreeOptions.SelectionOptions) and (GetFirstSelected() = nil) and not SelectionLocked then
  19911. begin
  19912. if Assigned(FNextNodeToSelect) then
  19913. Selected[FNextNodeToSelect] := True
  19914. else if Self.Focused then
  19915. Selected[GetFirstVisible] := True;
  19916. end;//if
  19917. end;
  19918. //----------------------------------------------------------------------------------------------------------------------
  19919. function TBaseVirtualTree.FindNodeInSelection(P: PVirtualNode; var Index: Integer; LowBound,
  19920. HighBound: Integer): Boolean;
  19921. // Search routine to find a specific node in the selection array.
  19922. // LowBound and HighBound determine the range in which to search the node.
  19923. // Either value can be -1 to denote the maximum range otherwise LowBound must be less or equal HighBound.
  19924. var
  19925. L, H,
  19926. I: Integer;
  19927. begin
  19928. Result := False;
  19929. L := 0;
  19930. if LowBound >= 0 then
  19931. L := LowBound;
  19932. H := FSelectionCount - 1;
  19933. if HighBound >= 0 then
  19934. H := HighBound;
  19935. while L <= H do
  19936. begin
  19937. I := (L + H) shr 1;
  19938. if PAnsiChar(FSelection[I]) < PAnsiChar(P) then
  19939. L := I + 1
  19940. else
  19941. begin
  19942. H := I - 1;
  19943. if FSelection[I] = P then
  19944. begin
  19945. Result := True;
  19946. L := I;
  19947. end;
  19948. end;
  19949. end;
  19950. Index := L;
  19951. end;
  19952. //----------------------------------------------------------------------------------------------------------------------
  19953. procedure TBaseVirtualTree.FinishChunkHeader(Stream: TStream; StartPos, EndPos: Integer);
  19954. // used while streaming out a node to finally write out the size of the chunk
  19955. var
  19956. Size: Integer;
  19957. begin
  19958. // seek back to the second entry in the chunk header
  19959. Stream.Position := StartPos + SizeOf(Size);
  19960. // determine size of chunk without the chunk header
  19961. Size := EndPos - StartPos - SizeOf(TChunkHeader);
  19962. // write the size...
  19963. Stream.Write(Size, SizeOf(Size));
  19964. // ... and seek to the last endposition
  19965. Stream.Position := EndPos;
  19966. end;
  19967. //----------------------------------------------------------------------------------------------------------------------
  19968. procedure TBaseVirtualTree.FontChanged(AFont: TObject);
  19969. // Little helper function for font changes (as they are not tracked in TBitmap/TCanvas.OnChange).
  19970. begin
  19971. FFontChanged := True;
  19972. if Assigned(FOldFontChange) then
  19973. FOldFontChange(AFont);
  19974. //if not (tsPainting in TreeStates) then AutoScale();
  19975. end;
  19976. //----------------------------------------------------------------------------------------------------------------------
  19977. function TBaseVirtualTree.GetBorderDimensions: TSize;
  19978. // Returns the overall width of the current window border, depending on border styles.
  19979. // Note: these numbers represent the system's standards not special properties, which can be set for TWinControl
  19980. // (e.g. bevels, border width).
  19981. var
  19982. Styles: Integer;
  19983. begin
  19984. Result.cx := 0;
  19985. Result.cy := 0;
  19986. Styles := GetWindowLong(Handle, GWL_STYLE);
  19987. if (Styles and WS_BORDER) <> 0 then
  19988. begin
  19989. Dec(Result.cx);
  19990. Dec(Result.cy);
  19991. end;
  19992. if (Styles and WS_THICKFRAME) <> 0 then
  19993. begin
  19994. Dec(Result.cx, GetSystemMetrics(SM_CXFIXEDFRAME));
  19995. Dec(Result.cy, GetSystemMetrics(SM_CYFIXEDFRAME));
  19996. end;
  19997. Styles := GetWindowLong(Handle, GWL_EXSTYLE);
  19998. if (Styles and WS_EX_CLIENTEDGE) <> 0 then
  19999. begin
  20000. Dec(Result.cx, GetSystemMetrics(SM_CXEDGE));
  20001. Dec(Result.cy, GetSystemMetrics(SM_CYEDGE));
  20002. end;
  20003. end;
  20004. //----------------------------------------------------------------------------------------------------------------------
  20005. function TBaseVirtualTree.GetCheckImage(Node: PVirtualNode; ImgCheckType: TCheckType = ctNone; ImgCheckState:
  20006. TCheckState = csUncheckedNormal; ImgEnabled: Boolean = True): Integer;
  20007. // Determines the index into the check image list for the given node depending on the check type
  20008. // and enabled state.
  20009. const
  20010. // Four dimensional array consisting of image indices for the check type, the check state, the enabled state and the
  20011. // hot state.
  20012. CheckStateToCheckImage: array[ctCheckBox..ctButton, csUncheckedNormal..csMixedPressed, Boolean, Boolean] of Integer = (
  20013. // ctCheckBox, ctTriStateCheckBox
  20014. (
  20015. // csUncheckedNormal (disabled [not hot, hot], enabled [not hot, hot])
  20016. ((ckCheckUncheckedDisabled, ckCheckUncheckedDisabled), (ckCheckUncheckedNormal, ckCheckUncheckedHot)),
  20017. // csUncheckedPressed (disabled [not hot, hot], enabled [not hot, hot])
  20018. ((ckCheckUncheckedDisabled, ckCheckUncheckedDisabled), (ckCheckUncheckedPressed, ckCheckUncheckedPressed)),
  20019. // csCheckedNormal
  20020. ((ckCheckCheckedDisabled, ckCheckCheckedDisabled), (ckCheckCheckedNormal, ckCheckCheckedHot)),
  20021. // csCheckedPressed
  20022. ((ckCheckCheckedDisabled, ckCheckCheckedDisabled), (ckCheckCheckedPressed, ckCheckCheckedPressed)),
  20023. // csMixedNormal
  20024. ((ckCheckMixedDisabled, ckCheckMixedDisabled), (ckCheckMixedNormal, ckCheckMixedHot)),
  20025. // csMixedPressed
  20026. ((ckCheckMixedDisabled, ckCheckMixedDisabled), (ckCheckMixedPressed, ckCheckMixedPressed))
  20027. ),
  20028. // ctRadioButton
  20029. (
  20030. // csUncheckedNormal (disabled [not hot, hot], enabled [not hot, hot])
  20031. ((ckRadioUncheckedDisabled, ckRadioUncheckedDisabled), (ckRadioUncheckedNormal, ckRadioUncheckedHot)),
  20032. // csUncheckedPressed (disabled [not hot, hot], enabled [not hot, hot])
  20033. ((ckRadioUncheckedDisabled, ckRadioUncheckedDisabled), (ckRadioUncheckedPressed, ckRadioUncheckedPressed)),
  20034. // csCheckedNormal
  20035. ((ckRadioCheckedDisabled, ckRadioCheckedDisabled), (ckRadioCheckedNormal, ckRadioCheckedHot)),
  20036. // csCheckedPressed
  20037. ((ckRadioCheckedDisabled, ckRadioCheckedDisabled), (ckRadioCheckedPressed, ckRadioCheckedPressed)),
  20038. // csMixedNormal (should never appear with ctRadioButton)
  20039. ((ckCheckMixedDisabled, ckCheckMixedDisabled), (ckCheckMixedNormal, ckCheckMixedHot)),
  20040. // csMixedPressed (should never appear with ctRadioButton)
  20041. ((ckCheckMixedDisabled, ckCheckMixedDisabled), (ckCheckMixedPressed, ckCheckMixedPressed))
  20042. ),
  20043. // ctButton
  20044. (
  20045. // csUncheckedNormal (disabled [not hot, hot], enabled [not hot, hot])
  20046. ((ckButtonDisabled, ckButtonDisabled), (ckButtonNormal, ckButtonHot)),
  20047. // csUncheckedPressed (disabled [not hot, hot], enabled [not hot, hot])
  20048. ((ckButtonDisabled, ckButtonDisabled), (ckButtonPressed, ckButtonPressed)),
  20049. // csCheckedNormal
  20050. ((ckButtonDisabled, ckButtonDisabled), (ckButtonNormal, ckButtonHot)),
  20051. // csCheckedPressed
  20052. ((ckButtonDisabled, ckButtonDisabled), (ckButtonPressed, ckButtonPressed)),
  20053. // csMixedNormal (should never appear with ctButton)
  20054. ((ckCheckMixedDisabled, ckCheckMixedDisabled), (ckCheckMixedNormal, ckCheckMixedHot)),
  20055. // csMixedPressed (should never appear with ctButton)
  20056. ((ckCheckMixedDisabled, ckCheckMixedDisabled), (ckCheckMixedPressed, ckCheckMixedPressed))
  20057. )
  20058. );
  20059. var
  20060. IsHot: Boolean;
  20061. begin
  20062. if Assigned(Node) then
  20063. begin
  20064. ImgCheckType := Node.CheckType;
  20065. ImgCheckState := Node.CheckState;
  20066. ImgEnabled := not (vsDisabled in Node.States) and Enabled;
  20067. IsHot := Node = FCurrentHotNode;
  20068. end
  20069. else
  20070. IsHot := False;
  20071. if ImgCheckType = ctTriStateCheckBox then
  20072. ImgCheckType := ctCheckBox;
  20073. if ImgCheckType = ctNone then
  20074. Result := -1
  20075. else
  20076. Result := CheckStateToCheckImage[ImgCheckType, ImgCheckState, ImgEnabled, IsHot];
  20077. end;
  20078. //----------------------------------------------------------------------------------------------------------------------
  20079. class function TBaseVirtualTree.GetCheckImageListFor(Kind: TCheckImageKind): TCustomImageList;
  20080. begin
  20081. case Kind of
  20082. ckDarkCheck:
  20083. Result := DarkCheckImages;
  20084. ckLightTick:
  20085. Result := LightTickImages;
  20086. ckDarkTick:
  20087. Result := DarkTickImages;
  20088. ckLightCheck:
  20089. Result := LightCheckImages;
  20090. ckFlat:
  20091. Result := FlatImages;
  20092. ckXP:
  20093. Result := XPImages;
  20094. ckSystemDefault:
  20095. Result := SystemCheckImages;
  20096. ckSystemFlat:
  20097. Result := SystemFlatCheckImages;
  20098. else
  20099. Result := nil;
  20100. end;
  20101. end;
  20102. //----------------------------------------------------------------------------------------------------------------------
  20103. function TBaseVirtualTree.GetColumnClass: TVirtualTreeColumnClass;
  20104. begin
  20105. Result := TVirtualTreeColumn;
  20106. end;
  20107. //----------------------------------------------------------------------------------------------------------------------
  20108. function TBaseVirtualTree.GetHeaderClass: TVTHeaderClass;
  20109. begin
  20110. Result := TVTHeader;
  20111. end;
  20112. //----------------------------------------------------------------------------------------------------------------------
  20113. function TBaseVirtualTree.GetHintWindowClass: THintWindowClass;
  20114. // Returns the default hint window class used for the tree. Descendants can override it to use their own classes.
  20115. begin
  20116. Result := TVirtualTreeHintWindow;
  20117. end;
  20118. //----------------------------------------------------------------------------------------------------------------------
  20119. procedure TBaseVirtualTree.GetImageIndex(var Info: TVTPaintInfo; Kind: TVTImageKind; InfoIndex: TVTImageInfoIndex;
  20120. DefaultImages: TCustomImageList);
  20121. // Retrieves the image index and an eventual customized image list for drawing.
  20122. var
  20123. CustomImages: TCustomImageList;
  20124. begin
  20125. with Info do
  20126. begin
  20127. ImageInfo[InfoIndex].Index := -1;
  20128. ImageInfo[InfoIndex].Ghosted := False;
  20129. CustomImages := DoGetImageIndex(Node, Kind, Column, ImageInfo[InfoIndex].Ghosted, ImageInfo[InfoIndex].Index);
  20130. if Assigned(CustomImages) then
  20131. ImageInfo[InfoIndex].Images := CustomImages
  20132. else
  20133. ImageInfo[InfoIndex].Images := DefaultImages;
  20134. end;
  20135. end;
  20136. //----------------------------------------------------------------------------------------------------------------------
  20137. function TBaseVirtualTree.IsEmpty: Boolean;
  20138. begin
  20139. Result := (Self.ChildCount[nil] = 0);
  20140. end;
  20141. //----------------------------------------------------------------------------------------------------------------------
  20142. function TBaseVirtualTree.GetNodeImageSize(Node: PVirtualNode): TSize;
  20143. // Returns the size of an image
  20144. // Override if you need different sized images for certain nodes.
  20145. begin
  20146. if Assigned(FImages) then
  20147. begin
  20148. Result.cx := FImages.Width;
  20149. Result.cy := FImages.Height;
  20150. end
  20151. else
  20152. begin
  20153. Result.cx := 0;
  20154. Result.cy := 0;
  20155. end;
  20156. end;
  20157. //----------------------------------------------------------------------------------------------------------------------
  20158. function TBaseVirtualTree.GetMaxRightExtend: Cardinal;
  20159. // Determines the maximum with of the currently visible part of the tree, depending on the length
  20160. // of the node texts. This method is used for determining the horizontal scroll range if no columns are used.
  20161. var
  20162. Node,
  20163. NextNode: PVirtualNode;
  20164. TopPosition: Integer;
  20165. NodeLeft,
  20166. CurrentWidth: Integer;
  20167. WithCheck: Boolean;
  20168. CheckOffset: Integer;
  20169. begin
  20170. Node := GetNodeAt(0, 0, True, TopPosition);
  20171. Result := 0;
  20172. if toShowRoot in FOptions.FPaintOptions then
  20173. NodeLeft := (GetNodeLevel(Node) + 1) * FIndent
  20174. else
  20175. NodeLeft := GetNodeLevel(Node) * FIndent;
  20176. if Assigned(FStateImages) then
  20177. Inc(NodeLeft, FStateImages.Width + 2);
  20178. if Assigned(FImages) then
  20179. Inc(NodeLeft, FImages.Width + 2);
  20180. WithCheck := (toCheckSupport in FOptions.FMiscOptions) and Assigned(FCheckImages);
  20181. if WithCheck then
  20182. CheckOffset := FCheckImages.Width + 2
  20183. else
  20184. CheckOffset := 0;
  20185. while Assigned(Node) do
  20186. begin
  20187. if not (vsInitialized in Node.States) then
  20188. InitNode(Node);
  20189. if WithCheck and (Node.CheckType <> ctNone) then
  20190. Inc(NodeLeft, CheckOffset);
  20191. CurrentWidth := DoGetNodeWidth(Node, NoColumn);
  20192. Inc(CurrentWidth, DoGetNodeExtraWidth(Node, NoColumn));
  20193. if Integer(Result) < (NodeLeft + CurrentWidth) then
  20194. Result := NodeLeft + CurrentWidth;
  20195. Inc(TopPosition, NodeHeight[Node]);
  20196. if TopPosition > Height then
  20197. Break;
  20198. if WithCheck and (Node.CheckType <> ctNone) then
  20199. Dec(NodeLeft, CheckOffset);
  20200. // Get next visible node and update left node position.
  20201. NextNode := GetNextVisible(Node, True);
  20202. if NextNode = nil then
  20203. Break;
  20204. Inc(NodeLeft, CountLevelDifference(Node, NextNode) * Integer(FIndent));
  20205. Node := NextNode;
  20206. end;
  20207. Inc(Result, FMargin);
  20208. end;
  20209. //----------------------------------------------------------------------------------------------------------------------
  20210. procedure TBaseVirtualTree.GetNativeClipboardFormats(var Formats: TFormatEtcArray);
  20211. // Returns the supported clipboard formats of the tree.
  20212. begin
  20213. InternalClipboardFormats.EnumerateFormats(TVirtualTreeClass(ClassType), Formats, FClipboardFormats);
  20214. // Ask application/descendants for self defined formats.
  20215. DoGetUserClipboardFormats(Formats);
  20216. end;
  20217. //----------------------------------------------------------------------------------------------------------------------
  20218. function TBaseVirtualTree.GetOperationCanceled;
  20219. begin
  20220. Result := FOperationCanceled and (FOperationCount > 0);
  20221. end;
  20222. //----------------------------------------------------------------------------------------------------------------------
  20223. function TBaseVirtualTree.GetOptionsClass: TTreeOptionsClass;
  20224. begin
  20225. Result := TCustomVirtualTreeOptions;
  20226. end;
  20227. //----------------------------------------------------------------------------------------------------------------------
  20228. function TBaseVirtualTree.GetTreeFromDataObject(const DataObject: IDataObject): TBaseVirtualTree;
  20229. // Returns the owner/sender of the given data object by means of a special clipboard format
  20230. // or nil if the sender is in another process or no virtual tree at all.
  20231. var
  20232. Medium: TStgMedium;
  20233. Data: PVTReference;
  20234. begin
  20235. Result := nil;
  20236. if Assigned(DataObject) then
  20237. begin
  20238. StandardOLEFormat.cfFormat := CF_VTREFERENCE;
  20239. if DataObject.GetData(StandardOLEFormat, Medium) = S_OK then
  20240. begin
  20241. Data := GlobalLock(Medium.hGlobal);
  20242. if Assigned(Data) then
  20243. begin
  20244. if Data.Process = GetCurrentProcessID then
  20245. Result := Data.Tree;
  20246. GlobalUnlock(Medium.hGlobal);
  20247. end;
  20248. ReleaseStgMedium(Medium);
  20249. end;
  20250. end;
  20251. end;
  20252. //----------------------------------------------------------------------------------------------------------------------
  20253. procedure TBaseVirtualTree.HandleHotTrack(X, Y: Integer);
  20254. // Updates the current "hot" node.
  20255. var
  20256. HitInfo: THitInfo;
  20257. CheckPositions: THitPositions;
  20258. ButtonIsHit,
  20259. DoInvalidate: Boolean;
  20260. begin
  20261. DoInvalidate := False;
  20262. // Get information about the hit.
  20263. GetHitTestInfoAt(X, Y, True, HitInfo);
  20264. // Only make the new node being "hot" if its label is hit or full row selection is enabled.
  20265. CheckPositions := [hiOnItemLabel, hiOnItemCheckbox];
  20266. // If running under Windows Vista using the explorer theme hitting the buttons makes the node hot, too.
  20267. if tsUseExplorerTheme in FStates then
  20268. Include(CheckPositions, hiOnItemButtonExact);
  20269. if (CheckPositions * HitInfo.HitPositions = []) and
  20270. (not (toFullRowSelect in FOptions.FSelectionOptions) or (hiNowhere in HitInfo.HitPositions)) then
  20271. HitInfo.HitNode := nil;
  20272. if (HitInfo.HitNode <> FCurrentHotNode) or (HitInfo.HitColumn <> FCurrentHotColumn) then
  20273. begin
  20274. DoInvalidate := (toHotTrack in FOptions.PaintOptions) or (toCheckSupport in FOptions.FMiscOptions);
  20275. DoHotChange(FCurrentHotNode, HitInfo.HitNode);
  20276. if Assigned(FCurrentHotNode) and DoInvalidate then
  20277. InvalidateNode(FCurrentHotNode);
  20278. FCurrentHotNode := HitInfo.HitNode;
  20279. FCurrentHotColumn := HitInfo.HitColumn;
  20280. end;
  20281. ButtonIsHit := (hiOnItemButtonExact in HitInfo.HitPositions) and (toHotTrack in FOptions.FPaintOptions);
  20282. if Assigned(FCurrentHotNode) and ((FHotNodeButtonHit <> ButtonIsHit) or DoInvalidate) then
  20283. begin
  20284. FHotNodeButtonHit := ButtonIsHit and (toHotTrack in FOptions.FPaintOptions);
  20285. InvalidateNode(FCurrentHotNode);
  20286. end
  20287. else
  20288. if not Assigned(FCurrentHotNode) then
  20289. FHotNodeButtonHit := False;
  20290. end;
  20291. //----------------------------------------------------------------------------------------------------------------------
  20292. procedure TBaseVirtualTree.HandleIncrementalSearch(CharCode: Word);
  20293. var
  20294. Run, Stop: PVirtualNode;
  20295. GetNextNode: TGetNextNodeProc;
  20296. NewSearchText: UnicodeString;
  20297. SingleLetter,
  20298. PreviousSearch: Boolean; // True if VK_BACK was sent.
  20299. SearchDirection: TVTSearchDirection;
  20300. //--------------- local functions -------------------------------------------
  20301. procedure SetupNavigation;
  20302. // If the search buffer is empty then we start searching with the next node after the last one, otherwise
  20303. // we continue with the last one. Node navigation function is set up too here, to avoid frequent checks.
  20304. var
  20305. FindNextNode: Boolean;
  20306. begin
  20307. FindNextNode := (Length(FSearchBuffer) = 0) or (Run = nil) or SingleLetter or PreviousSearch;
  20308. case FIncrementalSearch of
  20309. isVisibleOnly:
  20310. if SearchDirection = sdForward then
  20311. begin
  20312. GetNextNode := GetNextVisible;
  20313. if FindNextNode then
  20314. begin
  20315. if Run = nil then
  20316. Run := GetFirstVisible(nil, True)
  20317. else
  20318. begin
  20319. Run := GetNextVisible(Run, True);
  20320. // Do wrap around.
  20321. if Run = nil then
  20322. Run := GetFirstVisible(nil, True);
  20323. end;
  20324. end;
  20325. end
  20326. else
  20327. begin
  20328. GetNextNode := GetPreviousVisible;
  20329. if FindNextNode then
  20330. begin
  20331. if Run = nil then
  20332. Run := GetLastVisible(nil, True)
  20333. else
  20334. begin
  20335. Run := GetPreviousVisible(Run, True);
  20336. // Do wrap around.
  20337. if Run = nil then
  20338. Run := GetLastVisible(nil, True);
  20339. end;
  20340. end;
  20341. end;
  20342. isInitializedOnly:
  20343. if SearchDirection = sdForward then
  20344. begin
  20345. GetNextNode := GetNextNoInit;
  20346. if FindNextNode then
  20347. begin
  20348. if Run = nil then
  20349. Run := GetFirstNoInit
  20350. else
  20351. begin
  20352. Run := GetNextNoInit(Run);
  20353. // Do wrap around.
  20354. if Run = nil then
  20355. Run := GetFirstNoInit;
  20356. end;
  20357. end;
  20358. end
  20359. else
  20360. begin
  20361. GetNextNode := GetPreviousNoInit;
  20362. if FindNextNode then
  20363. begin
  20364. if Run = nil then
  20365. Run := GetLastNoInit
  20366. else
  20367. begin
  20368. Run := GetPreviousNoInit(Run);
  20369. // Do wrap around.
  20370. if Run = nil then
  20371. Run := GetLastNoInit;
  20372. end;
  20373. end;
  20374. end;
  20375. else
  20376. // isAll
  20377. if SearchDirection = sdForward then
  20378. begin
  20379. GetNextNode := GetNext;
  20380. if FindNextNode then
  20381. begin
  20382. if Run = nil then
  20383. Run := GetFirst
  20384. else
  20385. begin
  20386. Run := GetNext(Run);
  20387. // Do wrap around.
  20388. if Run = nil then
  20389. Run := GetFirst;
  20390. end;
  20391. end;
  20392. end
  20393. else
  20394. begin
  20395. GetNextNode := GetPrevious;
  20396. if FindNextNode then
  20397. begin
  20398. if Run = nil then
  20399. Run := GetLast
  20400. else
  20401. begin
  20402. Run := GetPrevious(Run);
  20403. // Do wrap around.
  20404. if Run = nil then
  20405. Run := GetLast;
  20406. end;
  20407. end;
  20408. end;
  20409. end;
  20410. end;
  20411. //---------------------------------------------------------------------------
  20412. function CodePageFromLocale(Language: LCID): Integer;
  20413. // Determines the code page for a given locale.
  20414. // Unfortunately there is no easier way than this, currently.
  20415. var
  20416. Buf: array[0..6] of Char;
  20417. begin
  20418. GetLocaleInfo(Language, LOCALE_IDEFAULTANSICODEPAGE, Buf, 6);
  20419. Result := StrToIntDef(Buf, GetACP);
  20420. end;
  20421. //---------------------------------------------------------------------------
  20422. function KeyUnicode(C: Char): WideChar;
  20423. // Converts the given character into its corresponding Unicode character
  20424. // depending on the active keyboard layout.
  20425. begin
  20426. {$ifdef UNICODE}
  20427. Result := C; //!!!!!!
  20428. {$ELSE}
  20429. MultiByteToWideChar(CodePageFromLocale(GetKeyboardLayout(0) and $FFFF),
  20430. MB_USEGLYPHCHARS, @C, 1, @Result, 1);
  20431. {$endif}
  20432. end;
  20433. //--------------- end local functions ---------------------------------------
  20434. var
  20435. FoundMatch: Boolean;
  20436. NewChar: WideChar;
  20437. begin
  20438. StopTimer(SearchTimer);
  20439. if FIncrementalSearch <> isNone then
  20440. begin
  20441. if CharCode <> 0 then
  20442. begin
  20443. DoStateChange([tsIncrementalSearching]);
  20444. // Convert the given virtual key code into a Unicode character based on the current locale.
  20445. NewChar := KeyUnicode(Char(CharCode));
  20446. PreviousSearch := NewChar = WideChar(VK_BACK);
  20447. // We cannot do a search with an empty search buffer.
  20448. if not PreviousSearch or (FSearchBuffer <> '') then
  20449. begin
  20450. // Determine which method to use to advance nodes and the start node to search from.
  20451. case FSearchStart of
  20452. ssAlwaysStartOver:
  20453. Run := nil;
  20454. ssFocusedNode:
  20455. Run := FFocusedNode;
  20456. else // ssLastHit
  20457. Run := FLastSearchNode;
  20458. end;
  20459. // Make sure the start node corresponds to the search criterion.
  20460. if Assigned(Run) then
  20461. begin
  20462. case FIncrementalSearch of
  20463. isInitializedOnly:
  20464. if not (vsInitialized in Run.States) then
  20465. Run := nil;
  20466. isVisibleOnly:
  20467. if not FullyVisible[Run] or IsEffectivelyFiltered[Run] then
  20468. Run := nil;
  20469. end;
  20470. end;
  20471. Stop := Run;
  20472. // VK_BACK temporarily changes search direction to opposite mode.
  20473. if PreviousSearch then
  20474. begin
  20475. if SearchDirection = sdBackward then
  20476. SearchDirection := sdForward
  20477. else
  20478. SearchDirection := sdBackward;
  20479. end
  20480. else
  20481. SearchDirection := FSearchDirection;
  20482. // The "single letter mode" is used to advance quickly from node to node when pressing the same key several times.
  20483. SingleLetter := (Length(FSearchBuffer) = 1) and not PreviousSearch and (FSearchBuffer[1] = NewChar);
  20484. // However if the current hit (if there is one) would fit also with a repeated character then
  20485. // don't use single letter mode.
  20486. if SingleLetter and (DoIncrementalSearch(Run, FSearchBuffer + NewChar) = 0) then
  20487. SingleLetter := False;
  20488. SetupNavigation;
  20489. FoundMatch := False;
  20490. if Assigned(Run) then
  20491. begin
  20492. if SingleLetter then
  20493. NewSearchText := FSearchBuffer
  20494. else
  20495. if PreviousSearch then
  20496. begin
  20497. SetLength(FSearchBuffer, Length(FSearchBuffer) - 1);
  20498. NewSearchText := FSearchBuffer;
  20499. end
  20500. else
  20501. NewSearchText := FSearchBuffer + NewChar;
  20502. repeat
  20503. if DoIncrementalSearch(Run, NewSearchText) = 0 then
  20504. begin
  20505. FoundMatch := True;
  20506. Break;
  20507. end;
  20508. // Advance to next node if we have not found a match.
  20509. Run := GetNextNode(Run);
  20510. // Do wrap around start or end of tree.
  20511. if (Run <> Stop) and (Run = nil) then
  20512. SetupNavigation;
  20513. until Run = Stop;
  20514. end;
  20515. if FoundMatch then
  20516. begin
  20517. ClearSelection;
  20518. FSearchBuffer := NewSearchText;
  20519. FLastSearchNode := Run;
  20520. FocusedNode := Run;
  20521. Selected[Run] := True;
  20522. FLastSearchNode := Run;
  20523. end
  20524. else
  20525. // Play an acoustic signal if nothing could be found but don't beep if only the currently
  20526. // focused node matches.
  20527. if Assigned(Run) and (DoIncrementalSearch(Run, NewSearchText) <> 0) then
  20528. Beep;
  20529. end;
  20530. end;
  20531. // Restart search timeout interval.
  20532. SetTimer(Handle, SearchTimer, FSearchTimeout, nil);
  20533. end;
  20534. end;
  20535. //----------------------------------------------------------------------------------------------------------------------
  20536. procedure TBaseVirtualTree.HandleMouseDblClick(var Message: TWMMouse; const HitInfo: THitInfo);
  20537. var
  20538. NewCheckState: TCheckState;
  20539. Node: PVirtualNode;
  20540. MayEdit: Boolean;
  20541. begin
  20542. MayEdit := not (tsEditing in FStates) and (toEditOnDblClick in FOptions.FMiscOptions);
  20543. if tsEditPending in FStates then
  20544. begin
  20545. StopTimer(EditTimer);
  20546. DoStateChange([], [tsEditPending]);
  20547. end;
  20548. if not (tsEditing in FStates) or DoEndEdit then
  20549. begin
  20550. if HitInfo.HitColumn = FHeader.FColumns.FClickIndex then
  20551. DoColumnDblClick(HitInfo.HitColumn, KeysToShiftState(Message.Keys));
  20552. if HitInfo.HitNode <> nil then
  20553. DoNodeDblClick(HitInfo);
  20554. Node := nil;
  20555. if (hiOnItem in HitInfo.HitPositions) and (HitInfo.HitColumn > NoColumn) and
  20556. (coFixed in FHeader.FColumns[HitInfo.HitColumn].FOptions) then
  20557. begin
  20558. if hiUpperSplitter in HitInfo.HitPositions then
  20559. Node := GetPreviousVisible(HitInfo.HitNode, True)
  20560. else
  20561. if hiLowerSplitter in HitInfo.HitPositions then
  20562. Node := HitInfo.HitNode;
  20563. end;
  20564. if Assigned(Node) and (Node <> FRoot) and (toNodeHeightDblClickResize in FOptions.FMiscOptions) then
  20565. begin
  20566. if DoNodeHeightDblClickResize(Node, HitInfo.HitColumn, KeysToShiftState(Message.Keys), Point(Message.XPos, Message.YPos)) then
  20567. begin
  20568. SetNodeHeight(Node, FDefaultNodeHeight);
  20569. UpdateWindow(Handle);
  20570. MayEdit := False;
  20571. end;
  20572. end
  20573. else
  20574. if hiOnItemCheckBox in HitInfo.HitPositions then
  20575. begin
  20576. if (FStates * [tsMouseCheckPending, tsKeyCheckPending] = []) and not (vsDisabled in HitInfo.HitNode.States) then
  20577. begin
  20578. with HitInfo.HitNode^ do
  20579. NewCheckState := DetermineNextCheckState(CheckType, CheckState);
  20580. if (ssLeft in KeysToShiftState(Message.Keys)) and DoChecking(HitInfo.HitNode, NewCheckState) then
  20581. begin
  20582. DoStateChange([tsMouseCheckPending]);
  20583. FCheckNode := HitInfo.HitNode;
  20584. FPendingCheckState := NewCheckState;
  20585. FCheckNode.CheckState := PressedState[FCheckNode.CheckState];
  20586. InvalidateNode(HitInfo.HitNode);
  20587. MayEdit := False;
  20588. end;
  20589. end;
  20590. end
  20591. else
  20592. begin
  20593. if hiOnItemButton in HitInfo.HitPositions then
  20594. begin
  20595. ToggleNode(HitInfo.HitNode);
  20596. MayEdit := False;
  20597. end
  20598. else
  20599. begin
  20600. if toToggleOnDblClick in FOptions.FMiscOptions then
  20601. begin
  20602. if ((([hiOnItemButton, hiOnItemLabel, hiOnNormalIcon, hiOnStateIcon] * HitInfo.HitPositions) <> []) or
  20603. ((toFullRowSelect in FOptions.FSelectionOptions) and Assigned(HitInfo.HitNode))) then
  20604. begin
  20605. ToggleNode(HitInfo.HitNode);
  20606. MayEdit := False;
  20607. end;
  20608. end;
  20609. end;
  20610. end;
  20611. end;
  20612. if MayEdit and Assigned(FFocusedNode) and (FFocusedNode = HitInfo.HitNode) and
  20613. (FFocusedColumn = HitInfo.HitColumn) and CanEdit(FFocusedNode, HitInfo.HitColumn) then
  20614. begin
  20615. DoStateChange([tsEditPending]);
  20616. FEditColumn := FFocusedColumn;
  20617. SetTimer(Handle, EditTimer, FEditDelay, nil);
  20618. end;
  20619. end;
  20620. //----------------------------------------------------------------------------------------------------------------------
  20621. procedure TBaseVirtualTree.HandleMouseDown(var Message: TWMMouse; var HitInfo: THitInfo);
  20622. // centralized mouse button down handling
  20623. var
  20624. LastFocused: PVirtualNode;
  20625. Column: TColumnIndex;
  20626. ShiftState: TShiftState;
  20627. // helper variables to shorten boolean equations/expressions
  20628. AutoDrag, // automatic (or allowed) drag start
  20629. IsLabelHit, // the node's caption or images are hit
  20630. IsCellHit, // for grid extension or full row select (but not check box, button)
  20631. IsAnyHit, // either IsHit or IsCellHit
  20632. IsHeightTracking, // height tracking
  20633. MultiSelect, // multiselection is enabled
  20634. ShiftEmpty, // ShiftState = []
  20635. NodeSelected: Boolean; // the new node (if any) is selected
  20636. NewColumn: Boolean; // column changed
  20637. NewNode: Boolean; // Node changed.
  20638. NeedChange: Boolean; // change event is required for selection change
  20639. CanClear: Boolean;
  20640. NewCheckState: TCheckState;
  20641. AltPressed: Boolean; // Pressing the Alt key enables special processing for selection.
  20642. FullRowDrag: Boolean; // Start dragging anywhere within a node's bound.
  20643. NodeRect: TRect;
  20644. begin
  20645. if [tsWheelPanning, tsWheelScrolling] * FStates <> [] then
  20646. begin
  20647. StopWheelPanning;
  20648. Exit;
  20649. end;
  20650. if tsEditPending in FStates then
  20651. begin
  20652. StopTimer(EditTimer);
  20653. DoStateChange([], [tsEditPending]);
  20654. end;
  20655. if (tsEditing in FStates) then
  20656. DoEndEdit;
  20657. // Focus change. Don't use the SetFocus method as this does not work for MDI windows.
  20658. if not Focused and CanFocus then
  20659. begin
  20660. Windows.SetFocus(Handle);
  20661. // Repeat the hit test as an OnExit event might got triggered that could modify the tree.
  20662. GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo);
  20663. end;
  20664. // Keep clicked column in case the application needs it.
  20665. FHeader.FColumns.FClickIndex := HitInfo.HitColumn;
  20666. // Change column only if we have hit the node label.
  20667. if (hiOnItemLabel in HitInfo.HitPositions) or
  20668. (toFullRowSelect in FOptions.FSelectionOptions) or
  20669. (toGridExtensions in FOptions.FMiscOptions) then
  20670. begin
  20671. NewColumn := FFocusedColumn <> HitInfo.HitColumn;
  20672. if toExtendedFocus in FOptions.FSelectionOptions then
  20673. Column := HitInfo.HitColumn
  20674. else
  20675. Column := FHeader.MainColumn;
  20676. end
  20677. else
  20678. begin
  20679. NewColumn := False;
  20680. Column := FFocusedColumn;
  20681. end;
  20682. if NewColumn and not FHeader.AllowFocus(Column) then
  20683. begin
  20684. NewColumn := False;
  20685. Column := FFocusedColumn;
  20686. end;
  20687. NewNode := FFocusedNode <> HitInfo.HitNode;
  20688. // Translate keys and filter out shift and control key.
  20689. ShiftState := KeysToShiftState(Message.Keys) * [ssShift, ssCtrl, ssAlt];
  20690. if ssAlt in ShiftState then
  20691. begin
  20692. AltPressed := True;
  20693. // Remove the Alt key from the shift state. It is not meaningful there.
  20694. Exclude(ShiftState, ssAlt);
  20695. end
  20696. else
  20697. AltPressed := False;
  20698. // Various combinations determine what states the tree enters now.
  20699. // We initialize shorthand variables to avoid the following expressions getting too large
  20700. // and to avoid repeative expensive checks.
  20701. IsLabelHit := not AltPressed and not (toSimpleDrawSelection in FOptions.FSelectionOptions) and
  20702. ((hiOnItemLabel in HitInfo.HitPositions) or (hiOnNormalIcon in HitInfo.HitPositions));
  20703. IsCellHit := not AltPressed and not IsLabelHit and Assigned(HitInfo.HitNode) and
  20704. ([hiOnItemButton, hiOnItemCheckBox] * HitInfo.HitPositions = []) and
  20705. ((toFullRowSelect in FOptions.FSelectionOptions) or
  20706. ((toGridExtensions in FOptions.FMiscOptions) and (HitInfo.HitColumn > NoColumn)));
  20707. IsAnyHit := IsLabelHit or IsCellHit;
  20708. MultiSelect := toMultiSelect in FOptions.FSelectionOptions;
  20709. ShiftEmpty := ShiftState = [];
  20710. NodeSelected := IsAnyHit and (vsSelected in HitInfo.HitNode.States);
  20711. // Determine the Drag behavior.
  20712. if MultiSelect and not (toDisableDrawSelection in FOptions.FSelectionOptions) then
  20713. begin
  20714. // We have MultiSelect and want to draw a selection rectangle.
  20715. // We will start a full row drag only in case a label was hit,
  20716. // otherwise a multi selection will start.
  20717. FullRowDrag := (toFullRowDrag in FOptions.FMiscOptions) and IsCellHit and
  20718. not (hiNowhere in HitInfo.HitPositions) and
  20719. (NodeSelected or (hiOnItemLabel in HitInfo.HitPositions) or (hiOnNormalIcon in HitInfo.HitPositions));
  20720. end
  20721. else // No MultiSelect, hence we can start a drag anywhere in the row.
  20722. FullRowDrag := toFullRowDrag in FOptions.FMiscOptions;
  20723. IsHeightTracking := (Message.Msg = WM_LBUTTONDOWN) and
  20724. (hiOnItem in HitInfo.HitPositions) and
  20725. ([hiUpperSplitter, hiLowerSplitter] * HitInfo.HitPositions <> []);
  20726. // Dragging might be started in the inherited handler manually (which is discouraged for stability reasons)
  20727. // the test for manual mode is done below (after the focused node is set).
  20728. AutoDrag := ((DragMode = dmAutomatic) or Dragging) and (not IsCellHit or FullRowDrag);
  20729. // Query the application to learn if dragging may start now (if set to dmManual).
  20730. if Assigned(HitInfo.HitNode) and not AutoDrag and (DragMode = dmManual) then
  20731. AutoDrag := DoBeforeDrag(HitInfo.HitNode, Column) and (FullRowDrag or IsLabelHit);
  20732. // handle node height tracking
  20733. if IsHeightTracking then
  20734. begin
  20735. if hiUpperSplitter in HitInfo.HitPositions then
  20736. FHeightTrackNode := GetPreviousVisible(HitInfo.HitNode, True)
  20737. else
  20738. FHeightTrackNode := HitInfo.HitNode;
  20739. if CanSplitterResizeNode(Point(Message.XPos, Message.YPos), FHeightTrackNode, HitInfo.HitColumn) then
  20740. begin
  20741. FHeightTrackColumn := HitInfo.HitColumn;
  20742. NodeRect := GetDisplayRect(FHeightTrackNode, FHeightTrackColumn, False);
  20743. FHeightTrackPoint := Point(NodeRect.Left, NodeRect.Top);
  20744. DoStateChange([tsNodeHeightTrackPending]);
  20745. Exit;
  20746. end;
  20747. end;
  20748. // handle button clicks
  20749. if (hiOnItemButton in HitInfo.HitPositions) and (vsHasChildren in HitInfo.HitNode.States) then
  20750. begin
  20751. ToggleNode(HitInfo.HitNode);
  20752. Exit;
  20753. end;
  20754. // check event
  20755. if hiOnItemCheckBox in HitInfo.HitPositions then
  20756. begin
  20757. if (FStates * [tsMouseCheckPending, tsKeyCheckPending] = []) and not (vsDisabled in HitInfo.HitNode.States) then
  20758. begin
  20759. with HitInfo.HitNode^ do
  20760. NewCheckState := DetermineNextCheckState(CheckType, CheckState);
  20761. if (ssLeft in KeysToShiftState(Message.Keys)) and DoChecking(HitInfo.HitNode, NewCheckState) then
  20762. begin
  20763. DoStateChange([tsMouseCheckPending]);
  20764. FCheckNode := HitInfo.HitNode;
  20765. FPendingCheckState := NewCheckState;
  20766. FCheckNode.CheckState := PressedState[FCheckNode.CheckState];
  20767. InvalidateNode(HitInfo.HitNode);
  20768. end;
  20769. end;
  20770. Exit;
  20771. end;
  20772. // Keep this node's level in case we need it for constraint selection.
  20773. if (FRoot.ChildCount > 0) and ShiftEmpty or (FSelectionCount = 0) then
  20774. if Assigned(HitInfo.HitNode) then
  20775. FLastSelectionLevel := GetNodeLevel(HitInfo.HitNode)
  20776. else
  20777. FLastSelectionLevel := GetNodeLevel(GetLastVisibleNoInit(nil, True));
  20778. // pending clearance
  20779. if MultiSelect and ShiftEmpty and not (hiOnItemCheckbox in HitInfo.HitPositions) and IsAnyHit and AutoDrag and
  20780. NodeSelected and not FSelectionLocked then
  20781. DoStateChange([tsClearPending]);
  20782. // immediate clearance
  20783. // Determine for the right mouse button if there is a popup menu. In this case and if drag'n drop is pending
  20784. // the current selection has to stay as it is.
  20785. with HitInfo, Message do
  20786. CanClear := not AutoDrag and
  20787. (not (tsRightButtonDown in FStates) or not HasPopupMenu(HitNode, HitColumn, Point(XPos, YPos)));
  20788. // User starts a selection with a selection rectangle.
  20789. if not (toDisableDrawSelection in FOptions.FSelectionOptions) and not (IsLabelHit or FullRowDrag) and MultiSelect then
  20790. begin
  20791. SetCapture(Handle);
  20792. DoStateChange([tsDrawSelPending]);
  20793. FDrawSelShiftState := ShiftState;
  20794. FNewSelRect := Rect(Message.XPos + FEffectiveOffsetX, Message.YPos - FOffsetY, Message.XPos + FEffectiveOffsetX,
  20795. Message.YPos - FOffsetY);
  20796. FLastSelRect := Rect(0, 0, 0, 0);
  20797. end;
  20798. if not FSelectionLocked and ((not (IsAnyHit or FullRowDrag) and MultiSelect and ShiftEmpty) or
  20799. (IsAnyHit and (not NodeSelected or (NodeSelected and CanClear)) and (ShiftEmpty or not MultiSelect))) then
  20800. begin
  20801. Assert(not (tsClearPending in FStates), 'Pending and direct clearance are mutual exclusive!');
  20802. // If the currently hit node was already selected then we have to reselect it again after clearing the current
  20803. // selection, but without a change event if it is the only selected node.
  20804. // The same applies if the Alt key is pressed, which allows to start drawing the selection rectangle also
  20805. // on node captions and images. Here the previous selection state does not matter, though.
  20806. if NodeSelected or (AltPressed and Assigned(HitInfo.HitNode) and (HitInfo.HitColumn = FHeader.MainColumn)) and not (hiNowhere in HitInfo.HitPositions) then
  20807. begin
  20808. NeedChange := FSelectionCount > 1;
  20809. InternalClearSelection;
  20810. InternalAddToSelection(HitInfo.HitNode, True);
  20811. if NeedChange then
  20812. begin
  20813. Invalidate;
  20814. Change(nil);
  20815. end;
  20816. end
  20817. else if not ((hiNowhere in HitInfo.HitPositions) and (toAlwaysSelectNode in Self.TreeOptions.SelectionOptions)) then // When clicking in the free space we don't want the selection to be cleared in case toAlwaysSelectNode is set
  20818. ClearSelection;
  20819. end;
  20820. // pending node edit
  20821. if Focused and
  20822. ((hiOnItemLabel in HitInfo.HitPositions) or ((toGridExtensions in FOptions.FMiscOptions) and
  20823. (hiOnItem in HitInfo.HitPositions))) and NodeSelected and not NewColumn and ShiftEmpty then
  20824. begin
  20825. DoStateChange([tsEditPending]);
  20826. end;
  20827. if not (toDisableDrawSelection in FOptions.FSelectionOptions)
  20828. and not (IsLabelHit or FullRowDrag) and MultiSelect then
  20829. begin
  20830. // The original code here was moved up to fix issue #187.
  20831. // In order not to break the semantics of this procedure, we are leaving these if statements here
  20832. if not IsCellHit or (hiNowhere in HitInfo.HitPositions) then
  20833. Exit;
  20834. end;
  20835. // Keep current mouse position.
  20836. FLastClickPos := Point(Message.XPos, Message.YPos);
  20837. // Handle selection and node focus change.
  20838. if (IsLabelHit or IsCellHit) and
  20839. DoFocusChanging(FFocusedNode, HitInfo.HitNode, FFocusedColumn, Column) then
  20840. begin
  20841. if NewColumn then
  20842. begin
  20843. InvalidateColumn(FFocusedColumn);
  20844. InvalidateColumn(Column);
  20845. FFocusedColumn := Column;
  20846. end;
  20847. if DragKind = dkDock then
  20848. begin
  20849. StopTimer(ScrollTimer);
  20850. DoStateChange([], [tsScrollPending, tsScrolling]);
  20851. end;
  20852. // Get the currently focused node to make multiple multi-selection blocks possible.
  20853. LastFocused := FFocusedNode;
  20854. if NewNode then
  20855. DoFocusNode(HitInfo.HitNode, False);
  20856. if MultiSelect and not ShiftEmpty then
  20857. HandleClickSelection(LastFocused, HitInfo.HitNode, ShiftState, AutoDrag)
  20858. else
  20859. begin
  20860. if ShiftEmpty then
  20861. FRangeAnchor := HitInfo.HitNode;
  20862. // If the hit node is not yet selected then do it now.
  20863. if not NodeSelected then
  20864. AddToSelection(HitInfo.HitNode);
  20865. end;
  20866. if NewNode or NewColumn then
  20867. begin
  20868. ScrollIntoView(FFocusedNode, toCenterScrollIntoView in FOptions.SelectionOptions,
  20869. not (toDisableAutoscrollOnFocus in FOptions.FAutoOptions)
  20870. and not (toFullRowSelect in FOptions.SelectionOptions));
  20871. DoFocusChange(FFocusedNode, FFocusedColumn);
  20872. end;
  20873. end;
  20874. // Drag'n drop initiation
  20875. // If we lost focus in the interim the button states would be cleared in WM_KILLFOCUS.
  20876. if AutoDrag and IsAnyHit and (FStates * [tsLeftButtonDown, tsRightButtonDown, tsMiddleButtonDown] <> []) then
  20877. BeginDrag(False);
  20878. end;
  20879. //----------------------------------------------------------------------------------------------------------------------
  20880. procedure TBaseVirtualTree.HandleMouseUp(var Message: TWMMouse; const HitInfo: THitInfo);
  20881. // Counterpart to the mouse down handler.
  20882. var
  20883. ReselectFocusedNode: Boolean;
  20884. begin
  20885. ReleaseCapture;
  20886. if not (tsVCLDragPending in FStates) then
  20887. begin
  20888. // reset pending or persistent states
  20889. if IsMouseSelecting then
  20890. begin
  20891. DoStateChange([], [tsDrawSelecting, tsDrawSelPending, tsToggleFocusedSelection]);
  20892. Invalidate;
  20893. end;
  20894. if tsClearPending in FStates then
  20895. begin
  20896. ReselectFocusedNode := Assigned(FFocusedNode) and (vsSelected in FFocusedNode.States);
  20897. ClearSelection;
  20898. if ReselectFocusedNode then
  20899. AddToSelection(FFocusedNode);
  20900. end;
  20901. if (tsToggleFocusedSelection in FStates) and (HitInfo.HitNode = FFocusedNode) and Assigned(HitInfo.HitNode) then //Prevent AV when dereferencing HitInfo.HitNode below, see bug #100
  20902. begin
  20903. if vsSelected in HitInfo.HitNode.States then
  20904. begin
  20905. if not (toAlwaysSelectNode in TreeOptions.SelectionOptions) or (Self.SelectedCount > 1) then
  20906. RemoveFromSelection(HitInfo.HitNode);
  20907. end
  20908. else
  20909. AddToSelection(HitInfo.HitNode);
  20910. InvalidateNode(HitInfo.HitNode);
  20911. end;
  20912. DoStateChange([], [tsOLEDragPending, tsOLEDragging, tsClearPending, tsDrawSelPending, tsToggleFocusedSelection,
  20913. tsScrollPending, tsScrolling]);
  20914. StopTimer(ScrollTimer);
  20915. if tsMouseCheckPending in FStates then
  20916. begin
  20917. DoStateChange([], [tsMouseCheckPending]);
  20918. // Need check for nil, issue #285
  20919. // because when mouse down on checkbox but not yet released
  20920. // and in this time list starts to rebuild by timer
  20921. // after this when mouse release FCheckNode equal nil
  20922. if Assigned (FCheckNode) then
  20923. begin
  20924. // Is the mouse still over the same node?
  20925. if (HitInfo.HitNode = FCheckNode) and (hiOnItem in HitInfo.HitPositions) then
  20926. DoCheckClick(FCheckNode, FPendingCheckState)
  20927. else
  20928. FCheckNode.CheckState := UnpressedState[FCheckNode.CheckState];
  20929. InvalidateNode(FCheckNode);
  20930. end;
  20931. FCheckNode := nil;
  20932. end;
  20933. if (FHeader.FColumns.FClickIndex > NoColumn) and (FHeader.FColumns.FClickIndex = HitInfo.HitColumn) then
  20934. DoColumnClick(HitInfo.HitColumn, KeysToShiftState(Message.Keys));
  20935. if HitInfo.HitNode <> nil then
  20936. DoNodeClick(HitInfo);
  20937. // handle a pending edit event
  20938. if tsEditPending in FStates then
  20939. begin
  20940. // Is the mouse still over the same node?
  20941. if (HitInfo.HitNode = FFocusedNode) and (hiOnItem in HitInfo.HitPositions) and
  20942. (toEditOnClick in FOptions.FMiscOptions) and (FFocusedColumn = HitInfo.HitColumn) and
  20943. CanEdit(FFocusedNode, HitInfo.HitColumn) then
  20944. begin
  20945. FEditColumn := FFocusedColumn;
  20946. SetTimer(Handle, EditTimer, FEditDelay, nil);
  20947. end
  20948. else
  20949. DoStateChange([], [tsEditPending]);
  20950. end;
  20951. end;
  20952. end;
  20953. //----------------------------------------------------------------------------------------------------------------------
  20954. function TBaseVirtualTree.HasImage(Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex): Boolean;
  20955. // Determines whether the given node has got an image of the given kind in the given column.
  20956. // Returns True if so, otherwise False.
  20957. // The given node will be implicitly initialized if needed.
  20958. var
  20959. Ghosted: Boolean;
  20960. Index: Integer;
  20961. begin
  20962. if not (vsInitialized in Node.States) then
  20963. InitNode(Node);
  20964. Index := -1;
  20965. Ghosted := False;
  20966. DoGetImageIndex(Node, Kind, Column, Ghosted, Index);
  20967. Result := Index > -1;
  20968. end;
  20969. //----------------------------------------------------------------------------------------------------------------------
  20970. function TBaseVirtualTree.HasPopupMenu(Node: PVirtualNode; Column: TColumnIndex; Pos: TPoint): Boolean;
  20971. // Determines whether the tree got a popup menu, either in its PopupMenu property, via the OnGetPopupMenu event or
  20972. // through inheritance. The latter case must be checked by the descendant which must override this method.
  20973. begin
  20974. Result := Assigned(PopupMenu) or Assigned(DoGetPopupMenu(Node, Column, Pos));
  20975. end;
  20976. //----------------------------------------------------------------------------------------------------------------------
  20977. procedure TBaseVirtualTree.InitChildren(Node: PVirtualNode);
  20978. // Initiates the initialization of the child number of the given node.
  20979. var
  20980. Count: Cardinal;
  20981. begin
  20982. if Assigned(Node) and (Node <> FRoot) and (vsHasChildren in Node.States) then
  20983. begin
  20984. Count := Node.ChildCount;
  20985. if DoInitChildren(Node, Count) then
  20986. begin
  20987. SetChildCount(Node, Count);
  20988. if Count = 0 then
  20989. Exclude(Node.States, vsHasChildren);
  20990. end;
  20991. end;
  20992. end;
  20993. //----------------------------------------------------------------------------------------------------------------------
  20994. procedure TBaseVirtualTree.InitNode(Node: PVirtualNode);
  20995. // Initiates the initialization of the given node to allow the application to load needed data for it.
  20996. var
  20997. InitStates: TVirtualNodeInitStates;
  20998. begin
  20999. with Node^ do
  21000. begin
  21001. InitStates := [];
  21002. if vsInitialized in States then
  21003. Include(InitStates, ivsReInit);
  21004. Include(States, vsInitialized);
  21005. if Parent = FRoot then
  21006. DoInitNode(nil, Node, InitStates)
  21007. else
  21008. DoInitNode(Parent, Node, InitStates);
  21009. if ivsDisabled in InitStates then
  21010. Include(States, vsDisabled);
  21011. if ivsHasChildren in InitStates then
  21012. Include(States, vsHasChildren);
  21013. if ivsSelected in InitStates then
  21014. begin
  21015. FSingletonNodeArray[0] := Node;
  21016. InternalAddToSelection(FSingletonNodeArray, 1, False);
  21017. end;
  21018. if ivsMultiline in InitStates then
  21019. Include(States, vsMultiline);
  21020. if ivsFiltered in InitStates then
  21021. begin
  21022. Include(States, vsFiltered);
  21023. if not (toShowFilteredNodes in FOptions.FPaintOptions) then
  21024. begin
  21025. AdjustTotalHeight(Node, -NodeHeight, True);
  21026. if FullyVisible[Node] then
  21027. Dec(FVisibleCount);
  21028. UpdateScrollBars(True);
  21029. end;
  21030. end;
  21031. // Expanded may already be set (when called from ReinitNode) or be set in DoInitNode, allow both.
  21032. if (vsExpanded in Node.States) xor (ivsExpanded in InitStates) then
  21033. begin
  21034. // Expand node if not yet done (this will automatically initialize child nodes).
  21035. if ivsExpanded in InitStates then
  21036. ToggleNode(Node)
  21037. else
  21038. // If the node already was expanded then explicitly trigger child initialization.
  21039. if vsHasChildren in Node.States then
  21040. InitChildren(Node);
  21041. end;
  21042. end;
  21043. end;
  21044. //----------------------------------------------------------------------------------------------------------------------
  21045. procedure TBaseVirtualTree.InternalAddFromStream(Stream: TStream; Version: Integer; Node: PVirtualNode);
  21046. // Loads all details for Node (including its children) from the given stream.
  21047. // Because the new nodes might be selected this method also fixes the selection array.
  21048. var
  21049. Stop: PVirtualNode;
  21050. Index: Integer;
  21051. LastTotalHeight: Cardinal;
  21052. WasFullyVisible: Boolean;
  21053. begin
  21054. Assert(Node <> FRoot, 'The root node cannot be loaded from stream.');
  21055. // Keep the current total height value of Node as it has already been applied
  21056. // but might change in the load and fixup code. We have to adjust that afterwards.
  21057. LastTotalHeight := Node.TotalHeight;
  21058. WasFullyVisible := FullyVisible[Node] and not IsEffectivelyFiltered[Node];
  21059. // Read in the new nodes.
  21060. ReadNode(Stream, Version, Node);
  21061. // One time update of node-internal states and the global visibility counter.
  21062. // This is located here to ease and speed up the loading process.
  21063. FixupTotalCount(Node);
  21064. AdjustTotalCount(Node.Parent, Node.TotalCount - 1, True); // -1 because Node itself was already set.
  21065. FixupTotalHeight(Node);
  21066. AdjustTotalHeight(Node.Parent, Node.TotalHeight - LastTotalHeight, True);
  21067. // New nodes are always visible, so the visible node count has been increased already.
  21068. // If Node is now invisible we have to take back this increment and don't need to add any visible child node.
  21069. if not FullyVisible[Node] or IsEffectivelyFiltered[Node] then
  21070. begin
  21071. if WasFullyVisible then
  21072. Dec(FVisibleCount);
  21073. end
  21074. else
  21075. // It can never happen that the node is now fully visible but was not before as this would require
  21076. // that the visibility state of one of its parents has changed, which cannot happen during loading.
  21077. Inc(FVisibleCount, CountVisibleChildren(Node));
  21078. // Fix selection array.
  21079. ClearTempCache;
  21080. if Node = FRoot then
  21081. Stop := nil
  21082. else
  21083. Stop := Node.NextSibling;
  21084. if toMultiSelect in FOptions.FSelectionOptions then
  21085. begin
  21086. // Add all nodes which were selected before to the current selection (unless they are already there).
  21087. while Node <> Stop do
  21088. begin
  21089. if (vsSelected in Node.States) and not FindNodeInSelection(Node, Index, 0, High(FSelection)) then
  21090. InternalCacheNode(Node);
  21091. Node := GetNextNoInit(Node);
  21092. end;
  21093. if FTempNodeCount > 0 then
  21094. AddToSelection(FTempNodeCache, FTempNodeCount, True);
  21095. ClearTempCache;
  21096. end
  21097. else // No further selected nodes allowed so delete the corresponding flag in all new nodes.
  21098. while Node <> Stop do
  21099. begin
  21100. Exclude(Node.States, vsSelected);
  21101. Node := GetNextNoInit(Node);
  21102. end;
  21103. end;
  21104. //----------------------------------------------------------------------------------------------------------------------
  21105. function TBaseVirtualTree.InternalAddToSelection(Node: PVirtualNode; ForceInsert: Boolean): Boolean;
  21106. begin
  21107. Assert(Assigned(Node), 'Node must not be nil!');
  21108. FSingletonNodeArray[0] := Node;
  21109. Result := InternalAddToSelection(FSingletonNodeArray, 1, ForceInsert);
  21110. end;
  21111. //----------------------------------------------------------------------------------------------------------------------
  21112. function TBaseVirtualTree.InternalAddToSelection(const NewItems: TNodeArray; NewLength: Integer;
  21113. ForceInsert: Boolean): Boolean;
  21114. // Internal version of method AddToSelection which does not trigger OnChange events
  21115. var
  21116. I, J: Integer;
  21117. CurrentEnd: Integer;
  21118. Constrained,
  21119. SiblingConstrained: Boolean;
  21120. begin
  21121. // The idea behind this code is to use a kind of reverse merge sort. QuickSort is quite fast
  21122. // and would do the job here too but has a serious problem with already sorted lists like FSelection.
  21123. // 1) Remove already selected items, mark all other as being selected.
  21124. if ForceInsert then
  21125. begin
  21126. for I := 0 to NewLength - 1 do
  21127. begin
  21128. Include(NewItems[I].States, vsSelected);
  21129. if Assigned(FOnAddToSelection) then
  21130. FOnAddToSelection(Self, NewItems[I]);
  21131. end;
  21132. end
  21133. else
  21134. begin
  21135. Constrained := toLevelSelectConstraint in FOptions.FSelectionOptions;
  21136. if Constrained and (FLastSelectionLevel = -1) then
  21137. FLastSelectionLevel := GetNodeLevel(NewItems[0]);
  21138. SiblingConstrained := toSiblingSelectConstraint in FOptions.FSelectionOptions;
  21139. if SiblingConstrained and (FRangeAnchor = nil) then
  21140. FRangeAnchor := NewItems[0];
  21141. for I := 0 to NewLength - 1 do
  21142. if ([vsSelected, vsDisabled] * NewItems[I].States <> []) or
  21143. (Constrained and (Cardinal(FLastSelectionLevel) <> GetNodeLevel(NewItems[I]))) or
  21144. (SiblingConstrained and (FRangeAnchor.Parent <> NewItems[I].Parent)) then
  21145. Inc(PAnsiChar(NewItems[I]))
  21146. else
  21147. begin
  21148. Include(NewItems[I].States, vsSelected);
  21149. if Assigned(FOnAddToSelection) then
  21150. FOnAddToSelection(Self, NewItems[I]);
  21151. end;
  21152. end;
  21153. I := PackArray(NewItems, NewLength);
  21154. if I > -1 then
  21155. NewLength := I;
  21156. Result := NewLength > 0;
  21157. if Result then
  21158. begin
  21159. // 2) Sort the new item list so we can easily traverse it.
  21160. if NewLength > 1 then
  21161. QuickSort(NewItems, 0, NewLength - 1);
  21162. // 3) Make room in FSelection for the new items.
  21163. if FSelectionCount + NewLength >= Length(FSelection) then
  21164. SetLength(FSelection, FSelectionCount + NewLength);
  21165. // 4) Merge in new items
  21166. J := NewLength - 1;
  21167. CurrentEnd := FSelectionCount - 1;
  21168. while J >= 0 do
  21169. begin
  21170. // First insert all new entries which are greater than the greatest entry in the old list.
  21171. // If the current end marker is < 0 then there's nothing more to move in the selection
  21172. // array and only the remaining new items must be inserted.
  21173. if CurrentEnd >= 0 then
  21174. begin
  21175. while (J >= 0) and (PAnsiChar(NewItems[J]) > PAnsiChar(FSelection[CurrentEnd])) do
  21176. begin
  21177. FSelection[CurrentEnd + J + 1] := NewItems[J];
  21178. Dec(J);
  21179. end;
  21180. // early out if nothing more needs to be copied
  21181. if J < 0 then
  21182. Break;
  21183. end
  21184. else
  21185. begin
  21186. // insert remaining new entries at position 0
  21187. Move(NewItems[0], FSelection[0], (J + 1) * SizeOf(Pointer));
  21188. // nothing more to do so exit main loop
  21189. Break;
  21190. end;
  21191. // find the last entry in the remaining selection list which is smaller then the largest
  21192. // entry in the remaining new items list
  21193. FindNodeInSelection(NewItems[J], I, 0, CurrentEnd);
  21194. Dec(I);
  21195. // move all entries which are greater than the greatest entry in the new items list up
  21196. // so the remaining gap travels down to where new items must be inserted
  21197. Move(FSelection[I + 1], FSelection[I + J + 2], (CurrentEnd - I) * SizeOf(Pointer));
  21198. CurrentEnd := I;
  21199. end;
  21200. Inc(FSelectionCount, NewLength);
  21201. end;
  21202. end;
  21203. //----------------------------------------------------------------------------------------------------------------------
  21204. procedure TBaseVirtualTree.InternalCacheNode(Node: PVirtualNode);
  21205. // Adds the given node to the temporary node cache (used when collecting possibly large amounts of nodes).
  21206. var
  21207. Len: Cardinal;
  21208. begin
  21209. Len := Length(FTempNodeCache);
  21210. if FTempNodeCount = Len then
  21211. begin
  21212. if Len < 100 then
  21213. Len := 100
  21214. else
  21215. Len := Len + Len div 10;
  21216. SetLength(FTempNodeCache, Len);
  21217. end;
  21218. FTempNodeCache[FTempNodeCount] := Node;
  21219. Inc(FTempNodeCount);
  21220. end;
  21221. //----------------------------------------------------------------------------------------------------------------------
  21222. procedure TBaseVirtualTree.InternalClearSelection;
  21223. var
  21224. Count: Integer;
  21225. begin
  21226. // It is possible that there are invalid node references in the selection array
  21227. // if the tree update is locked and changes in the structure were made.
  21228. // Handle this potentially dangerous situation by packing the selection array explicitely.
  21229. if FUpdateCount > 0 then
  21230. begin
  21231. Count := PackArray(FSelection, FSelectionCount);
  21232. if Count > -1 then
  21233. begin
  21234. FSelectionCount := Count;
  21235. SetLength(FSelection, FSelectionCount);
  21236. end;
  21237. end;
  21238. while FSelectionCount > 0 do
  21239. begin
  21240. Dec(FSelectionCount);
  21241. Exclude(FSelection[FSelectionCount].States, vsSelected);
  21242. DoRemoveFromSelection(FSelection[FSelectionCount]);
  21243. end;
  21244. ResetRangeAnchor;
  21245. FSelection := nil;
  21246. DoStateChange([], [tsClearPending]);
  21247. end;
  21248. //----------------------------------------------------------------------------------------------------------------------
  21249. procedure TBaseVirtualTree.InternalConnectNode(Node, Destination: PVirtualNode; Target: TBaseVirtualTree;
  21250. Mode: TVTNodeAttachMode);
  21251. // Connects Node with Destination depending on Mode.
  21252. // No error checking takes place. Node as well as Destination must be valid. Node must never be a root node and
  21253. // Destination must not be a root node if Mode is amInsertBefore or amInsertAfter.
  21254. var
  21255. Run: PVirtualNode;
  21256. begin
  21257. // Keep in mind that the destination node might belong to another tree.
  21258. with Target do
  21259. begin
  21260. case Mode of
  21261. amInsertBefore:
  21262. begin
  21263. Node.PrevSibling := Destination.PrevSibling;
  21264. Destination.PrevSibling := Node;
  21265. Node.NextSibling := Destination;
  21266. Node.Parent := Destination.Parent;
  21267. Node.Index := Destination.Index;
  21268. if Node.PrevSibling = nil then
  21269. Node.Parent.FirstChild := Node
  21270. else
  21271. Node.PrevSibling.NextSibling := Node;
  21272. // reindex all following nodes
  21273. Run := Destination;
  21274. while Assigned(Run) do
  21275. begin
  21276. Inc(Run.Index);
  21277. Run := Run.NextSibling;
  21278. end;
  21279. Inc(Destination.Parent.ChildCount);
  21280. Include(Destination.Parent.States, vsHasChildren);
  21281. AdjustTotalCount(Destination.Parent, Node.TotalCount, True);
  21282. // Add the new node's height only if its parent is expanded.
  21283. if FullyVisible[Node] then
  21284. begin
  21285. AdjustTotalHeight(Destination.Parent, Node.TotalHeight, True);
  21286. Inc(FVisibleCount, CountVisibleChildren(Node) + Cardinal(IfThen(IsEffectivelyVisible[Node], 1)));
  21287. end;
  21288. end;
  21289. amInsertAfter:
  21290. begin
  21291. Node.NextSibling := Destination.NextSibling;
  21292. Destination.NextSibling := Node;
  21293. Node.PrevSibling := Destination;
  21294. Node.Parent := Destination.Parent;
  21295. if Node.NextSibling = nil then
  21296. Node.Parent.LastChild := Node
  21297. else
  21298. Node.NextSibling.PrevSibling := Node;
  21299. Node.Index := Destination.Index;
  21300. // reindex all following nodes
  21301. Run := Node;
  21302. while Assigned(Run) do
  21303. begin
  21304. Inc(Run.Index);
  21305. Run := Run.NextSibling;
  21306. end;
  21307. Inc(Destination.Parent.ChildCount);
  21308. Include(Destination.Parent.States, vsHasChildren);
  21309. AdjustTotalCount(Destination.Parent, Node.TotalCount, True);
  21310. // Add the new node's height only if its parent is expanded.
  21311. if FullyVisible[Node] then
  21312. begin
  21313. AdjustTotalHeight(Destination.Parent, Node.TotalHeight, True);
  21314. Inc(FVisibleCount, CountVisibleChildren(Node) + Cardinal(IfThen(IsEffectivelyVisible[Node], 1)));
  21315. end;
  21316. end;
  21317. amAddChildFirst:
  21318. begin
  21319. if Assigned(Destination.FirstChild) then
  21320. begin
  21321. // If there's a first child then there must also be a last child.
  21322. Destination.FirstChild.PrevSibling := Node;
  21323. Node.NextSibling := Destination.FirstChild;
  21324. Destination.FirstChild := Node;
  21325. end
  21326. else
  21327. begin
  21328. // First child node at this location.
  21329. Destination.FirstChild := Node;
  21330. Destination.LastChild := Node;
  21331. Node.NextSibling := nil;
  21332. end;
  21333. Node.PrevSibling := nil;
  21334. Node.Parent := Destination;
  21335. Node.Index := 0;
  21336. // reindex all following nodes
  21337. Run := Node.NextSibling;
  21338. while Assigned(Run) do
  21339. begin
  21340. Inc(Run.Index);
  21341. Run := Run.NextSibling;
  21342. end;
  21343. Inc(Destination.ChildCount);
  21344. Include(Destination.States, vsHasChildren);
  21345. AdjustTotalCount(Destination, Node.TotalCount, True);
  21346. // Add the new node's height only if its parent is expanded.
  21347. if FullyVisible[Node] then
  21348. begin
  21349. AdjustTotalHeight(Destination, Node.TotalHeight, True);
  21350. Inc(FVisibleCount, CountVisibleChildren(Node) + Cardinal(IfThen(IsEffectivelyVisible[Node], 1)));
  21351. end;
  21352. end;
  21353. amAddChildLast:
  21354. begin
  21355. if Assigned(Destination.LastChild) then
  21356. begin
  21357. // If there's a last child then there must also be a first child.
  21358. Destination.LastChild.NextSibling := Node;
  21359. Node.PrevSibling := Destination.LastChild;
  21360. Destination.LastChild := Node;
  21361. end
  21362. else
  21363. begin
  21364. // first child node at this location
  21365. Destination.FirstChild := Node;
  21366. Destination.LastChild := Node;
  21367. Node.PrevSibling := nil;
  21368. end;
  21369. Node.NextSibling := nil;
  21370. Node.Parent := Destination;
  21371. if Assigned(Node.PrevSibling) then
  21372. Node.Index := Node.PrevSibling.Index + 1
  21373. else
  21374. Node.Index := 0;
  21375. Inc(Destination.ChildCount);
  21376. Include(Destination.States, vsHasChildren);
  21377. AdjustTotalCount(Destination, Node.TotalCount, True);
  21378. // Add the new node's height only if its parent is expanded.
  21379. if FullyVisible[Node] then
  21380. begin
  21381. AdjustTotalHeight(Destination, Node.TotalHeight, True);
  21382. Inc(FVisibleCount, CountVisibleChildren(Node) + Cardinal(IfThen(IsEffectivelyVisible[Node], 1)));
  21383. end;
  21384. end;
  21385. else
  21386. // amNoWhere: do nothing
  21387. end;
  21388. // Remove temporary states.
  21389. Node.States := Node.States - [vsChecking, vsCutOrCopy, vsDeleting, vsClearing];
  21390. // Update the hidden children flag of the parent.
  21391. if (Mode <> amNoWhere) and (Node.Parent <> FRoot) then
  21392. begin
  21393. // If we have added a visible node then simply remove the all-children-hidden flag.
  21394. if IsEffectivelyVisible[Node] then
  21395. Exclude(Node.Parent.States, vsAllChildrenHidden)
  21396. else
  21397. // If we have added an invisible node and this is the only child node then
  21398. // make sure the all-children-hidden flag is in a determined state.
  21399. // If there were child nodes before then no action is needed.
  21400. if Node.Parent.ChildCount = 1 then
  21401. Include(Node.Parent.States, vsAllChildrenHidden);
  21402. end;
  21403. end;
  21404. end;
  21405. //----------------------------------------------------------------------------------------------------------------------
  21406. function TBaseVirtualTree.InternalData(Node: PVirtualNode): Pointer;
  21407. begin
  21408. Result := nil;
  21409. end;
  21410. //----------------------------------------------------------------------------------------------------------------------
  21411. procedure TBaseVirtualTree.InternalDisconnectNode(Node: PVirtualNode; KeepFocus: Boolean; Reindex: Boolean = True);
  21412. // Disconnects the given node from its parent and siblings. The node's pointer are not reset so they can still be used
  21413. // after return from this method (probably a very short time only!).
  21414. // If KeepFocus is True then the focused node is not reset. This is useful if the given node is reconnected to the tree
  21415. // immediately after return of this method and should stay being the focused node if it was it before.
  21416. // Note: Node must not be nil or the root node.
  21417. var
  21418. Parent,
  21419. Run: PVirtualNode;
  21420. Index: Integer;
  21421. AdjustHeight: Boolean;
  21422. begin
  21423. Assert(Assigned(Node) and (Node <> FRoot), 'Node must neither be nil nor the root node.');
  21424. if (Node = FFocusedNode) and not KeepFocus then
  21425. begin
  21426. DoFocusNode(nil, False);
  21427. DoFocusChange(FFocusedNode, FFocusedColumn);
  21428. end;
  21429. if Node = FRangeAnchor then
  21430. ResetRangeAnchor;
  21431. // Update the hidden children flag of the parent.
  21432. if (Node.Parent <> FRoot) and not (vsClearing in Node.Parent.States) then
  21433. if FUpdateCount = 0 then
  21434. DetermineHiddenChildrenFlag(Node.Parent)
  21435. else
  21436. Include(FStates, tsUpdateHiddenChildrenNeeded);
  21437. if not (vsDeleting in Node.States) then
  21438. begin
  21439. // Some states are only temporary so take them out.
  21440. Node.States := Node.States - [vsChecking];
  21441. Parent := Node.Parent;
  21442. Dec(Parent.ChildCount);
  21443. AdjustHeight := (vsExpanded in Parent.States) and (vsVisible in Node.States);
  21444. if Parent.ChildCount = 0 then
  21445. begin
  21446. Parent.States := Parent.States - [vsAllChildrenHidden, vsHasChildren];
  21447. if (Parent <> FRoot) and (vsExpanded in Parent.States) then
  21448. Exclude(Parent.States, vsExpanded);
  21449. end;
  21450. AdjustTotalCount(Parent, -Integer(Node.TotalCount), True);
  21451. if AdjustHeight then
  21452. AdjustTotalHeight(Parent, -Integer(Node.TotalHeight), True);
  21453. if FullyVisible[Node] then
  21454. Dec(FVisibleCount, CountVisibleChildren(Node) + Cardinal(IfThen(IsEffectivelyVisible[Node], 1)));
  21455. if Assigned(Node.PrevSibling) then
  21456. Node.PrevSibling.NextSibling := Node.NextSibling
  21457. else
  21458. Parent.FirstChild := Node.NextSibling;
  21459. if Assigned(Node.NextSibling) then
  21460. begin
  21461. Node.NextSibling.PrevSibling := Node.PrevSibling;
  21462. // Reindex all following nodes.
  21463. if Reindex then
  21464. begin
  21465. Run := Node.NextSibling;
  21466. Index := Node.Index;
  21467. while Assigned(Run) do
  21468. begin
  21469. Run.Index := Index;
  21470. Inc(Index);
  21471. Run := Run.NextSibling;
  21472. end;
  21473. end;
  21474. end
  21475. else
  21476. Parent.LastChild := Node.PrevSibling;
  21477. end;
  21478. end;
  21479. //----------------------------------------------------------------------------------------------------------------------
  21480. procedure TBaseVirtualTree.InternalRemoveFromSelection(Node: PVirtualNode);
  21481. // Special version to mark a node to be no longer in the current selection. PackArray must
  21482. // be used to remove finally those entries.
  21483. var
  21484. Index: Integer;
  21485. begin
  21486. // Because pointers are always DWORD aligned we can simply increment all those
  21487. // which we want to have removed (see also PackArray) and still have the
  21488. // order in the list preserved.
  21489. if FindNodeInSelection(Node, Index, -1, -1) then
  21490. begin
  21491. Exclude(Node.States, vsSelected);
  21492. Inc(PAnsiChar(FSelection[Index]));
  21493. DoRemoveFromSelection(Node);
  21494. AdviseChangeEvent(False, Node, crIgnore);
  21495. end;
  21496. end;
  21497. //----------------------------------------------------------------------------------------------------------------------
  21498. procedure TBaseVirtualTree.InvalidateCache;
  21499. // Marks the cache as invalid.
  21500. begin
  21501. DoStateChange([tsValidationNeeded], [tsUseCache]);
  21502. //ChangeTreeStatesAsync([csValidationNeeded], [csUseCache]);
  21503. end;
  21504. //----------------------------------------------------------------------------------------------------------------------
  21505. procedure TBaseVirtualTree.MarkCutCopyNodes;
  21506. // Sets the vsCutOrCopy style in every currently selected but not disabled node to indicate it is
  21507. // now part of a clipboard operation.
  21508. var
  21509. Nodes: TNodeArray;
  21510. I: Integer;
  21511. begin
  21512. Nodes := nil;
  21513. if FSelectionCount > 0 then
  21514. begin
  21515. // need the current selection sorted to exclude selected nodes which are children, grandchildren etc. of
  21516. // already selected nodes
  21517. Nodes := GetSortedSelection(False);
  21518. for I := 0 to High(Nodes) do
  21519. with Nodes[I]^ do
  21520. if not (vsDisabled in States) then
  21521. Include(States, vsCutOrCopy);
  21522. end;
  21523. end;
  21524. //----------------------------------------------------------------------------------------------------------------------
  21525. procedure TBaseVirtualTree.Loaded;
  21526. var
  21527. LastRootCount: Cardinal;
  21528. IsReadOnly: Boolean;
  21529. begin
  21530. inherited;
  21531. // Call RegisterDragDrop after all visual inheritance changes to MiscOptions have been applied.
  21532. if not (csDesigning in ComponentState) and (toAcceptOLEDrop in FOptions.FMiscOptions) then
  21533. if HandleAllocated then
  21534. RegisterDragDrop(Handle, DragManager as IDropTarget);
  21535. {$IF CompilerVersion >= 23}
  21536. FSavedBorderWidth := BorderWidth;
  21537. FSavedBevelKind := BevelKind;
  21538. {$IFEND}
  21539. VclStyleChanged;
  21540. // If a root node count has been set during load of the tree then update its child structure now
  21541. // as this hasn't been done yet in this case.
  21542. if (tsNeedRootCountUpdate in FStates) and (FRoot.ChildCount > 0) then
  21543. begin
  21544. DoStateChange([], [tsNeedRootCountUpdate]);
  21545. IsReadOnly := toReadOnly in FOptions.FMiscOptions;
  21546. Exclude(FOptions.FMiscOptions, toReadOnly);
  21547. LastRootCount := FRoot.ChildCount;
  21548. FRoot.ChildCount := 0;
  21549. BeginUpdate;
  21550. SetChildCount(FRoot, LastRootCount);
  21551. EndUpdate;
  21552. if IsReadOnly then
  21553. Include(FOptions.FMiscOptions, toReadOnly);
  21554. end;
  21555. // Prevent the object inspector at design time from marking the header as being modified
  21556. // when auto resize is enabled.
  21557. Updating;
  21558. try
  21559. FHeader.UpdateMainColumn;
  21560. FHeader.FColumns.FixPositions;
  21561. if toAutoBidiColumnOrdering in FOptions.FAutoOptions then
  21562. FHeader.FColumns.ReorderColumns(UseRightToLeftAlignment);
  21563. // Because of the special recursion and update stopper when creating the window (or resizing it)
  21564. // we have to manually trigger the auto size calculation here.
  21565. if hsNeedScaling in FHeader.FStates then
  21566. FHeader.RescaleHeader
  21567. else
  21568. FHeader.RecalculateHeader;
  21569. if hoAutoResize in FHeader.FOptions then
  21570. FHeader.FColumns.AdjustAutoSize(InvalidColumn, True);
  21571. finally
  21572. Updated;
  21573. end;
  21574. end;
  21575. //----------------------------------------------------------------------------------------------------------------------
  21576. procedure TBaseVirtualTree.MainColumnChanged;
  21577. begin
  21578. DoCancelEdit;
  21579. if Assigned(FAccessibleItem) then
  21580. NotifyWinEvent(EVENT_OBJECT_NAMECHANGE, Handle, OBJID_CLIENT, CHILDID_SELF);
  21581. end;
  21582. //----------------------------------------------------------------------------------------------------------------------
  21583. procedure TBaseVirtualTree.MouseMove(Shift: TShiftState; X, Y: Integer);
  21584. var
  21585. R: TRect;
  21586. begin
  21587. if tsNodeHeightTrackPending in FStates then
  21588. begin
  21589. // Remove hint if shown currently.
  21590. Application.CancelHint;
  21591. // Stop wheel panning if active.
  21592. StopWheelPanning;
  21593. // Stop timers
  21594. StopTimer(ExpandTimer);
  21595. StopTimer(EditTimer);
  21596. StopTimer(HeaderTimer);
  21597. StopTimer(ScrollTimer);
  21598. StopTimer(SearchTimer);
  21599. FSearchBuffer := '';
  21600. FLastSearchNode := nil;
  21601. DoStateChange([tsNodeHeightTracking], [tsScrollPending, tsScrolling, tsEditPending, tsOLEDragPending, tsVCLDragPending,
  21602. tsIncrementalSearching, tsNodeHeightTrackPending]);
  21603. end;
  21604. if tsDrawSelPending in FStates then
  21605. begin
  21606. // Remove current selection in case the user clicked somewhere in the window (but not a node)
  21607. // and moved the mouse.
  21608. if CalculateSelectionRect(X, Y) then
  21609. begin
  21610. InvalidateRect(Handle, @FNewSelRect, False);
  21611. UpdateWindow(Handle);
  21612. if (Abs(FNewSelRect.Right - FNewSelRect.Left) > Mouse.DragThreshold) or
  21613. (Abs(FNewSelRect.Bottom - FNewSelRect.Top) > Mouse.DragThreshold) then
  21614. begin
  21615. if tsClearPending in FStates then
  21616. begin
  21617. DoStateChange([], [tsClearPending]);
  21618. ClearSelection;
  21619. end;
  21620. DoStateChange([tsDrawSelecting], [tsDrawSelPending]);
  21621. // Reset to main column for multiselection.
  21622. FocusedColumn := FHeader.MainColumn;
  21623. // The current rectangle may already include some node captions. Handle this.
  21624. if HandleDrawSelection(X, Y) then
  21625. InvalidateRect(Handle, nil, False);
  21626. end;
  21627. end;
  21628. end
  21629. else
  21630. begin
  21631. if tsNodeHeightTracking in FStates then
  21632. begin
  21633. // Handle height tracking.
  21634. if DoNodeHeightTracking(FHeightTrackNode, FHeightTrackColumn, FHeader.GetShiftState,
  21635. FHeightTrackPoint, Point(X, Y)) then
  21636. begin
  21637. // Avoid negative (or zero) node heights.
  21638. if FHeightTrackPoint.Y >= Y then
  21639. Y := FHeightTrackPoint.Y + 1;
  21640. SetNodeHeight(FHeightTrackNode, Y - FHeightTrackPoint.Y);
  21641. UpdateWindow(Handle);
  21642. Exit;
  21643. end;
  21644. end;
  21645. // If both wheel panning and auto scrolling are pending then the user moved the mouse while holding down the
  21646. // middle mouse button. This means panning is being used, hence remove the wheel scroll flag.
  21647. if [tsWheelPanning, tsWheelScrolling] * FStates = [tsWheelPanning, tsWheelScrolling] then
  21648. begin
  21649. if ((Abs(FLastClickPos.X - X) >= Mouse.DragThreshold) or (Abs(FLastClickPos.Y - Y) >= Mouse.DragThreshold)) then
  21650. DoStateChange([], [tsWheelScrolling]);
  21651. end;
  21652. // Really start dragging if the mouse has been moved more than the threshold.
  21653. if (tsOLEDragPending in FStates) and ((Abs(FLastClickPos.X - X) >= FDragThreshold) or
  21654. (Abs(FLastClickPos.Y - Y) >= FDragThreshold)) then
  21655. DoDragging(FLastClickPos)
  21656. else
  21657. begin
  21658. if CanAutoScroll then
  21659. DoAutoScroll(X, Y);
  21660. if [tsWheelPanning, tsWheelScrolling] * FStates <> [] then
  21661. AdjustPanningCursor(X, Y);
  21662. if not IsMouseSelecting then
  21663. begin
  21664. HandleHotTrack(X, Y);
  21665. inherited MouseMove(Shift, X, Y);
  21666. end
  21667. else
  21668. begin
  21669. // Handle draw selection if required, but don't do the work twice if the
  21670. // auto scrolling code already cares about the selection.
  21671. if not (tsScrolling in FStates) and CalculateSelectionRect(X, Y) then
  21672. begin
  21673. // If something in the selection changed then invalidate the entire
  21674. // tree instead trying to figure out the display rects of all changed nodes.
  21675. if HandleDrawSelection(X, Y) then
  21676. InvalidateRect(Handle, nil, False)
  21677. else
  21678. begin
  21679. UnionRect(R, OrderRect(FNewSelRect), OrderRect(FLastSelRect));
  21680. OffsetRect(R, -FEffectiveOffsetX, FOffsetY);
  21681. InvalidateRect(Handle, @R, False);
  21682. end;
  21683. UpdateWindow(Handle);
  21684. end;
  21685. end;
  21686. end;
  21687. end;
  21688. end;
  21689. //----------------------------------------------------------------------------------------------------------------------
  21690. procedure TBaseVirtualTree.Notification(AComponent: TComponent; Operation: TOperation);
  21691. begin
  21692. if (AComponent <> Self) and (Operation = opRemove) then
  21693. begin
  21694. // Check for components linked to the tree.
  21695. if AComponent = FImages then
  21696. begin
  21697. Images := nil;
  21698. if not (csDestroying in ComponentState) then
  21699. Invalidate;
  21700. end
  21701. else
  21702. if AComponent = FStateImages then
  21703. begin
  21704. StateImages := nil;
  21705. if not (csDestroying in ComponentState) then
  21706. Invalidate;
  21707. end
  21708. else
  21709. if AComponent = FCustomCheckImages then
  21710. begin
  21711. CustomCheckImages := nil;
  21712. FCheckImageKind := ckSystemDefault;
  21713. if not (csDestroying in ComponentState) then
  21714. Invalidate;
  21715. end
  21716. else
  21717. if AComponent = PopupMenu then
  21718. PopupMenu := nil
  21719. else
  21720. // Check for components linked to the header.
  21721. if Assigned(FHeader) then
  21722. begin
  21723. if AComponent = FHeader.FImages then
  21724. FHeader.Images := nil
  21725. else
  21726. if AComponent = FHeader.PopupMenu then
  21727. FHeader.PopupMenu := nil;
  21728. end;
  21729. end;
  21730. inherited;
  21731. end;
  21732. //----------------------------------------------------------------------------------------------------------------------
  21733. procedure TBaseVirtualTree.OriginalWMNCPaint(DC: HDC);
  21734. // Unfortunately, the painting for the non-client area in TControl is not always correct and does also not consider
  21735. // existing clipping regions, so it has been modified here to take this into account.
  21736. const
  21737. InnerStyles: array[TBevelCut] of Integer = (0, BDR_SUNKENINNER, BDR_RAISEDINNER, 0);
  21738. OuterStyles: array[TBevelCut] of Integer = (0, BDR_SUNKENOUTER, BDR_RAISEDOUTER, 0);
  21739. EdgeStyles: array[TBevelKind] of Integer = (0, 0, BF_SOFT, BF_FLAT);
  21740. Ctl3DStyles: array[Boolean] of Integer = (BF_MONO, 0);
  21741. var
  21742. RC, RW: TRect;
  21743. EdgeSize: Integer;
  21744. Size: TSize;
  21745. begin
  21746. if (BevelKind <> bkNone) or (BorderWidth > 0) then
  21747. begin
  21748. RC := Rect(0, 0, Width, Height);
  21749. Size := GetBorderDimensions;
  21750. InflateRect(RC, Size.cx, Size.cy);
  21751. RW := RC;
  21752. if BevelKind <> bkNone then
  21753. begin
  21754. DrawEdge(DC, RC, InnerStyles[BevelInner] or OuterStyles[BevelOuter], Byte(BevelEdges) or EdgeStyles[BevelKind] or
  21755. Ctl3DStyles[Ctl3D]);
  21756. EdgeSize := 0;
  21757. if BevelInner <> bvNone then
  21758. Inc(EdgeSize, BevelWidth);
  21759. if BevelOuter <> bvNone then
  21760. Inc(EdgeSize, BevelWidth);
  21761. with TWithSafeRect(RC) do
  21762. begin
  21763. if beLeft in BevelEdges then
  21764. Inc(Left, EdgeSize);
  21765. if beTop in BevelEdges then
  21766. Inc(Top, EdgeSize);
  21767. if beRight in BevelEdges then
  21768. Dec(Right, EdgeSize);
  21769. if beBottom in BevelEdges then
  21770. Dec(Bottom, EdgeSize);
  21771. end;
  21772. end;
  21773. // Repaint only the part in the original clipping region and not yet drawn parts.
  21774. IntersectClipRect(DC, RC.Left, RC.Top, RC.Right, RC.Bottom);
  21775. // Determine inner rectangle to exclude (RC corresponds then to the client area).
  21776. InflateRect(RC, -Integer(BorderWidth), -Integer(BorderWidth));
  21777. // Remove the inner rectangle.
  21778. ExcludeClipRect(DC, RC.Left, RC.Top, RC.Right, RC.Bottom);
  21779. // Erase parts not drawn.
  21780. Brush.Color := FColors.BorderColor;
  21781. Windows.FillRect(DC, RW, Brush.Handle);
  21782. end;
  21783. end;
  21784. //----------------------------------------------------------------------------------------------------------------------
  21785. procedure TBaseVirtualTree.Paint;
  21786. // Window paint routine. Used when the tree window needs to be updated.
  21787. var
  21788. Window: TRect;
  21789. Target: TPoint;
  21790. Temp: Integer;
  21791. Options: TVTInternalPaintOptions;
  21792. RTLOffset: Integer;
  21793. begin
  21794. Options := [poBackground, poColumnColor, poDrawFocusRect, poDrawDropMark, poDrawSelection, poGridLines];
  21795. if UseRightToLeftAlignment and FHeader.UseColumns then
  21796. RTLOffset := ComputeRTLOffset(True)
  21797. else
  21798. RTLOffset := 0;
  21799. // The update rect has already been filled in WMPaint, as it is the window's update rect, which gets
  21800. // reset when BeginPaint is called (in the ancestor).
  21801. // The difference to the DC's clipbox is that it is also valid with internal paint operations used
  21802. // e.g. by the Explorer while dragging, but show window content while dragging is disabled.
  21803. if not IsRectEmpty(FUpdateRect) then
  21804. begin
  21805. Temp := Header.Columns.GetVisibleFixedWidth;
  21806. if Temp = 0 then
  21807. begin
  21808. Window := FUpdateRect;
  21809. Target := Window.TopLeft;
  21810. // The clipping rectangle is given in client coordinates of the window. We have to convert it into
  21811. // a sliding window of the tree image.
  21812. OffsetRect(Window, FEffectiveOffsetX - RTLOffset, -FOffsetY);
  21813. PaintTree(Canvas, Window, Target, Options);
  21814. end
  21815. else
  21816. begin
  21817. // First part, fixed columns
  21818. Window := ClientRect;
  21819. Window.Right := Temp;
  21820. Target := Window.TopLeft;
  21821. OffsetRect(Window, -RTLOffset, -FOffsetY);
  21822. PaintTree(Canvas, Window, Target, Options);
  21823. // Second part, other columns
  21824. Window := GetClientRect;
  21825. if Temp > Window.Right then
  21826. Exit;
  21827. Window.Left := Temp;
  21828. Target := Window.TopLeft;
  21829. OffsetRect(Window, FEffectiveOffsetX - RTLOffset, -FOffsetY);
  21830. PaintTree(Canvas, Window, Target, Options);
  21831. end;
  21832. end;
  21833. end;
  21834. //----------------------------------------------------------------------------------------------------------------------
  21835. procedure TBaseVirtualTree.PaintCheckImage(Canvas: TCanvas; const ImageInfo: TVTImageInfo; Selected: Boolean);
  21836. var
  21837. ForegroundColor: COLORREF;
  21838. R: TRect;
  21839. Details: TThemedElementDetails;
  21840. begin
  21841. with ImageInfo do
  21842. begin
  21843. if (tsUseThemes in FStates) and (FCheckImageKind = ckSystemDefault) then
  21844. begin
  21845. R := Rect(XPos - 1, YPos + 1, XPos + 16, YPos + 16);
  21846. Details.Element := teButton;
  21847. case Index of
  21848. // ctRadioButton
  21849. 1 : Details := StyleServices.GetElementDetails(tbRadioButtonUncheckedNormal);
  21850. 2 : Details := StyleServices.GetElementDetails(tbRadioButtonUncheckedHot);
  21851. 3 : Details := StyleServices.GetElementDetails(tbRadioButtonUncheckedPressed);
  21852. 4 : Details := StyleServices.GetElementDetails(tbRadioButtonUncheckedDisabled);
  21853. 5 : Details := StyleServices.GetElementDetails(tbRadioButtonCheckedNormal);
  21854. 6 : Details := StyleServices.GetElementDetails(tbRadioButtonCheckedHot);
  21855. 7 : Details := StyleServices.GetElementDetails(tbRadioButtonCheckedPressed);
  21856. 8 : Details := StyleServices.GetElementDetails(tbRadioButtonCheckedDisabled);
  21857. // ct(TriState)CheckBox
  21858. 9 : Details := StyleServices.GetElementDetails(tbCheckBoxUncheckedNormal);
  21859. 10 : Details := StyleServices.GetElementDetails(tbCheckBoxUncheckedHot);
  21860. 11 : Details := StyleServices.GetElementDetails(tbCheckBoxUncheckedPressed);
  21861. 12 : Details := StyleServices.GetElementDetails(tbCheckBoxUncheckedDisabled);
  21862. 13 : Details := StyleServices.GetElementDetails(tbCheckBoxCheckedNormal);
  21863. 14 : Details := StyleServices.GetElementDetails(tbCheckBoxCheckedHot);
  21864. 15 : Details := StyleServices.GetElementDetails(tbCheckBoxCheckedPressed);
  21865. 16 : Details := StyleServices.GetElementDetails(tbCheckBoxCheckedDisabled);
  21866. 17 : Details := StyleServices.GetElementDetails(tbCheckBoxMixedNormal);
  21867. 18 : Details := StyleServices.GetElementDetails(tbCheckBoxMixedHot);
  21868. 19 : Details := StyleServices.GetElementDetails(tbCheckBoxMixedPressed);
  21869. 20 : Details := StyleServices.GetElementDetails(tbCheckBoxMixedDisabled);
  21870. // ctButton
  21871. 21 : Details := StyleServices.GetElementDetails(tbPushButtonNormal);
  21872. 22 : Details := StyleServices.GetElementDetails(tbPushButtonHot);
  21873. 23 : Details := StyleServices.GetElementDetails(tbPushButtonPressed);
  21874. 24 : Details := StyleServices.GetElementDetails(tbPushButtonDisabled);
  21875. else
  21876. Details := StyleServices.GetElementDetails(tbButtonRoot);
  21877. end;
  21878. StyleServices.DrawElement(Canvas.Handle, Details, R);
  21879. if Index in [21..24] then
  21880. UtilityImages.Draw(Canvas, XPos - 1, YPos, 4);
  21881. end
  21882. else
  21883. with FCheckImages do
  21884. begin
  21885. if Selected and not Ghosted then
  21886. begin
  21887. if Focused or (toPopupMode in FOptions.FPaintOptions) then
  21888. ForegroundColor := ColorToRGB(FColors.FocusedSelectionColor)
  21889. else
  21890. ForegroundColor := ColorToRGB(FColors.UnfocusedSelectionColor);
  21891. end
  21892. else
  21893. ForegroundColor := GetRGBColor(BlendColor);
  21894. ImageList_DrawEx(Handle, Index, Canvas.Handle, XPos, YPos, 0, 0, GetRGBColor(BkColor), ForegroundColor,
  21895. ILD_TRANSPARENT);
  21896. end;
  21897. end;
  21898. end;
  21899. //----------------------------------------------------------------------------------------------------------------------
  21900. type
  21901. TCustomImageListCast = class(TCustomImageList);
  21902. procedure DrawImage(ImageList: TCustomImageList; Index: Integer; Canvas: TCanvas; X, Y: Integer; Style: Cardinal; Enabled: Boolean);
  21903. procedure DrawDisabledImage(ImageList: TCustomImageList; Canvas: TCanvas; X, Y, Index: Integer);
  21904. {$if CompilerVersion >= 21}
  21905. var
  21906. Params: TImageListDrawParams;
  21907. begin
  21908. FillChar(Params, SizeOf(Params), 0);
  21909. Params.cbSize := SizeOf(Params);
  21910. Params.himl := ImageList.Handle;
  21911. Params.i := Index;
  21912. Params.hdcDst := Canvas.Handle;
  21913. Params.x := X;
  21914. Params.y := Y;
  21915. Params.fState := ILS_SATURATE;
  21916. ImageList_DrawIndirect(@Params);
  21917. {$else}
  21918. begin
  21919. TCustomImageListCast(ImageList).DoDraw(Index, Canvas, X, Y, Style, False);
  21920. {$ifend}
  21921. end;
  21922. begin
  21923. if Enabled then
  21924. TCustomImageListCast(ImageList).DoDraw(Index, Canvas, X, Y, Style, Enabled)
  21925. else
  21926. DrawDisabledImage(ImageList, Canvas, X, Y, Index);
  21927. end;
  21928. //----------------------------------------------------------------------------------------------------------------------
  21929. procedure TBaseVirtualTree.PaintImage(var PaintInfo: TVTPaintInfo; ImageInfoIndex: TVTImageInfoIndex; DoOverlay: Boolean);
  21930. const
  21931. Style: array[TImageType] of Cardinal = (0, ILD_MASK);
  21932. var
  21933. ExtraStyle: Cardinal;
  21934. CutNode: Boolean;
  21935. PaintFocused: Boolean;
  21936. DrawEnabled: Boolean;
  21937. begin
  21938. with PaintInfo do
  21939. begin
  21940. CutNode := (vsCutOrCopy in Node.States) and (tsCutPending in FStates);
  21941. PaintFocused := Focused or (toGhostedIfUnfocused in FOptions.FPaintOptions);
  21942. // Since the overlay image must be specified together with the image to draw
  21943. // it is meaningfull to retrieve it in advance.
  21944. if DoOverlay then
  21945. GetImageIndex(PaintInfo, ikOverlay, iiOverlay, Images)
  21946. else
  21947. PaintInfo.ImageInfo[iiOverlay].Index := -1;
  21948. DrawEnabled := not (vsDisabled in Node.States) and Enabled;
  21949. with ImageInfo[ImageInfoIndex] do
  21950. begin
  21951. if (vsSelected in Node.States) and not(Ghosted or CutNode) then
  21952. begin
  21953. if PaintFocused or (toPopupMode in FOptions.FPaintOptions) then
  21954. Images.BlendColor := FColors.FocusedSelectionColor
  21955. else
  21956. Images.BlendColor := FColors.UnfocusedSelectionColor;
  21957. end
  21958. else
  21959. Images.BlendColor := Color;
  21960. // If the user returned an index >= 15 then we cannot use the built-in overlay image drawing.
  21961. // Instead we do it manually.
  21962. if (ImageInfo[iiOverlay].Index > -1) and (ImageInfo[iiOverlay].Index < 15) then
  21963. ExtraStyle := ILD_TRANSPARENT or ILD_OVERLAYMASK and IndexToOverlayMask(ImageInfo[iiOverlay].Index + 1)
  21964. else
  21965. ExtraStyle := ILD_TRANSPARENT;
  21966. // Blend image if enabled and the tree has the focus (or ghosted images must be drawn also if unfocused) ...
  21967. if (toUseBlendedImages in FOptions.FPaintOptions) and PaintFocused
  21968. // ... and the image is ghosted...
  21969. and (Ghosted or
  21970. // ... or it is not the check image and the node is selected (but selection is not for the entire row)...
  21971. ((vsSelected in Node.States) and
  21972. not (toFullRowSelect in FOptions.FSelectionOptions) and
  21973. not (toGridExtensions in FOptions.FMiscOptions)) or
  21974. // ... or the node must be shown in cut mode.
  21975. CutNode) then
  21976. ExtraStyle := ExtraStyle or ILD_BLEND50;
  21977. if (vsSelected in Node.States) and not Ghosted then
  21978. Images.BlendColor := clDefault;
  21979. DrawImage(Images, Index, Canvas, XPos, YPos, Style[Images.ImageType] or ExtraStyle, DrawEnabled);
  21980. // Now, draw the overlay. This circumnavigates limitations in the overlay mask index (it has to be 4 bits in size,
  21981. // anything larger will be truncated by the ILD_OVERLAYMASK).
  21982. // However this will only be done if the overlay image index is > 15, to avoid breaking code that relies
  21983. // on overlay image indices (e.g. when using system image lists).
  21984. if PaintInfo.ImageInfo[iiOverlay].Index >= 15 then
  21985. // Note: XPos and YPos are those of the normal images.
  21986. DrawImage(ImageInfo[iiOverlay].Images, ImageInfo[iiOverlay].Index, Canvas, XPos, YPos,
  21987. Style[ImageInfo[iiOverlay].Images.ImageType] or ExtraStyle, DrawEnabled);
  21988. end;
  21989. end;
  21990. end;
  21991. //----------------------------------------------------------------------------------------------------------------------
  21992. procedure TBaseVirtualTree.PaintNodeButton(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; const R: TRect;
  21993. ButtonX, ButtonY: Integer; BidiMode: TBiDiMode);
  21994. var
  21995. Bitmap: TBitmap;
  21996. XPos: Integer;
  21997. IsHot: Boolean;
  21998. Theme: HTHEME;
  21999. Glyph: Integer;
  22000. State: Integer;
  22001. Pos: TRect;
  22002. begin
  22003. IsHot := (toHotTrack in FOptions.FPaintOptions) and (FCurrentHotNode = Node) and FHotNodeButtonHit;
  22004. // Draw the node's plus/minus button according to the directionality.
  22005. if BidiMode = bdLeftToRight then
  22006. XPos := R.Left + ButtonX
  22007. else
  22008. XPos := R.Right - ButtonX - FPlusBM.Width;
  22009. if tsUseExplorerTheme in FStates then
  22010. begin
  22011. Glyph := IfThen(IsHot, TVP_HOTGLYPH, TVP_GLYPH);
  22012. State := IfThen(vsExpanded in Node.States, GLPS_OPENED, GLPS_CLOSED);
  22013. Pos := Rect(XPos, R.Top + ButtonY, XPos + FPlusBM.Width, R.Top + ButtonY + FPlusBM.Height);
  22014. Theme := OpenThemeData(Handle, 'TREEVIEW');
  22015. DrawThemeBackground(Theme, Canvas.Handle, Glyph, State, Pos, nil);
  22016. CloseThemeData(Theme);
  22017. end
  22018. else
  22019. begin
  22020. if vsExpanded in Node.States then
  22021. begin
  22022. if IsHot then
  22023. Bitmap := FHotMinusBM
  22024. else
  22025. Bitmap := FMinusBM;
  22026. end
  22027. else
  22028. begin
  22029. if IsHot then
  22030. Bitmap := FHotPlusBM
  22031. else
  22032. Bitmap := FPlusBM;
  22033. end;
  22034. // Need to draw this masked.
  22035. Canvas.Draw(XPos, R.Top + ButtonY, Bitmap);
  22036. end;
  22037. end;
  22038. //----------------------------------------------------------------------------------------------------------------------
  22039. procedure TBaseVirtualTree.PaintTreeLines(const PaintInfo: TVTPaintInfo; VAlignment, IndentSize: Integer;
  22040. LineImage: TLineImage);
  22041. var
  22042. I: Integer;
  22043. XPos,
  22044. Offset: Integer;
  22045. NewStyles: TLineImage;
  22046. begin
  22047. NewStyles := nil;
  22048. with PaintInfo do
  22049. begin
  22050. if BidiMode = bdLeftToRight then
  22051. begin
  22052. XPos := CellRect.Left;
  22053. Offset := FIndent;
  22054. end
  22055. else
  22056. begin
  22057. Offset := -Integer(FIndent);
  22058. XPos := CellRect.Right + Offset;
  22059. end;
  22060. case FLineMode of
  22061. lmBands:
  22062. if poGridLines in PaintInfo.PaintOptions then
  22063. begin
  22064. // Convert the line images in correct bands.
  22065. SetLength(NewStyles, Length(LineImage));
  22066. for I := IndentSize - 1 downto 0 do
  22067. begin
  22068. if (vsExpanded in Node.States) and not (vsAllChildrenHidden in Node.States) then
  22069. NewStyles[I] := ltLeft
  22070. else
  22071. case LineImage[I] of
  22072. ltRight,
  22073. ltBottomRight,
  22074. ltTopDownRight,
  22075. ltTopRight:
  22076. NewStyles[I] := ltLeftBottom;
  22077. ltNone:
  22078. // Have to take over the image to the right of this one. A no line entry can never appear as
  22079. // last entry so I don't need an end check here.
  22080. if LineImage[I + 1] in [ltNone, ltTopRight] then
  22081. NewStyles[I] := NewStyles[I + 1]
  22082. else
  22083. NewStyles[I] := ltLeft;
  22084. ltTopDown:
  22085. // Have to check the image to the right of this one. A top down line can never appear as
  22086. // last entry so I don't need an end check here.
  22087. if LineImage[I + 1] in [ltNone, ltTopRight] then
  22088. NewStyles[I] := NewStyles[I + 1]
  22089. else
  22090. NewStyles[I] := ltLeft;
  22091. end;
  22092. end;
  22093. PaintInfo.Canvas.Font.Color := FColors.GridLineColor;
  22094. for I := 0 to IndentSize - 1 do
  22095. begin
  22096. DoBeforeDrawLineImage(PaintInfo.Node, I + Ord(not (toShowRoot in TreeOptions.PaintOptions)), XPos);
  22097. DrawLineImage(PaintInfo, XPos, CellRect.Top, NodeHeight[Node] - 1, VAlignment - 1, NewStyles[I],
  22098. BidiMode <> bdLeftToRight);
  22099. Inc(XPos, Offset);
  22100. end;
  22101. end;
  22102. else // lmNormal
  22103. PaintInfo.Canvas.Font.Color := FColors.TreeLineColor;
  22104. for I := 0 to IndentSize - 1 do
  22105. begin
  22106. DoBeforeDrawLineImage(PaintInfo.Node, I + Ord(not (toShowRoot in TreeOptions.PaintOptions)), XPos);
  22107. DrawLineImage(PaintInfo, XPos, CellRect.Top, NodeHeight[Node], VAlignment - 1, LineImage[I],
  22108. BidiMode <> bdLeftToRight);
  22109. Inc(XPos, Offset);
  22110. end;
  22111. end;
  22112. end;
  22113. end;
  22114. //----------------------------------------------------------------------------------------------------------------------
  22115. procedure TBaseVirtualTree.PaintSelectionRectangle(Target: TCanvas; WindowOrgX: Integer; const SelectionRect: TRect;
  22116. TargetRect: TRect);
  22117. // Helper routine to draw a selection rectangle in the mode determined by DrawSelectionMode.
  22118. var
  22119. BlendRect: TRect;
  22120. TextColorBackup,
  22121. BackColorBackup: COLORREF; // used to restore forground and background colors when drawing a selection rectangle
  22122. begin
  22123. if ((FDrawSelectionMode = smDottedRectangle) and not (tsUseThemes in FStates)) or
  22124. not MMXAvailable then
  22125. begin
  22126. // Classical selection rectangle using dotted borderlines.
  22127. TextColorBackup := GetTextColor(Target.Handle);
  22128. SetTextColor(Target.Handle, $FFFFFF);
  22129. BackColorBackup := GetBkColor(Target.Handle);
  22130. SetBkColor(Target.Handle, 0);
  22131. Target.DrawFocusRect(SelectionRect);
  22132. SetTextColor(Target.Handle, TextColorBackup);
  22133. SetBkColor(Target.Handle, BackColorBackup);
  22134. end
  22135. else
  22136. begin
  22137. // Modern alpha blended style.
  22138. OffsetRect(TargetRect, WindowOrgX, 0);
  22139. if IntersectRect(BlendRect, OrderRect(SelectionRect), TargetRect) then
  22140. begin
  22141. OffsetRect(BlendRect, -WindowOrgX, 0);
  22142. AlphaBlend(0, Target.Handle, BlendRect, Point(0, 0), bmConstantAlphaAndColor, FSelectionBlendFactor,
  22143. ColorToRGB(FColors.SelectionRectangleBlendColor));
  22144. Target.Brush.Color := FColors.SelectionRectangleBorderColor;
  22145. Target.FrameRect(SelectionRect);
  22146. end;
  22147. end;
  22148. end;
  22149. //----------------------------------------------------------------------------------------------------------------------
  22150. procedure TBaseVirtualTree.PanningWindowProc(var Message: TMessage);
  22151. var
  22152. PS: TPaintStruct;
  22153. Canvas: TCanvas;
  22154. begin
  22155. if Message.Msg = WM_PAINT then
  22156. begin
  22157. BeginPaint(FPanningWindow, PS);
  22158. Canvas := TCanvas.Create;
  22159. Canvas.Handle := PS.hdc;
  22160. try
  22161. Canvas.Draw(0, 0, FPanningImage);
  22162. finally
  22163. Canvas.Handle := 0;
  22164. Canvas.Free;
  22165. EndPaint(FPanningWindow, PS);
  22166. end;
  22167. Message.Result := 0;
  22168. end
  22169. else
  22170. with Message do
  22171. Result := DefWindowProc(FPanningWindow, Msg, wParam, lParam);
  22172. end;
  22173. //----------------------------------------------------------------------------------------------------------------------
  22174. procedure TBaseVirtualTree.PrepareCell(var PaintInfo: TVTPaintInfo; WindowOrgX, MaxWidth: Integer);
  22175. // This method is called immediately before a cell's content is drawn und is responsible to paint selection colors etc.
  22176. var
  22177. TextColorBackup,
  22178. BackColorBackup: COLORREF;
  22179. FocusRect,
  22180. InnerRect: TRect;
  22181. RowRect: TRect;
  22182. Theme: HTHEME;
  22183. {$if CompilerVersion < 19}
  22184. const
  22185. TREIS_HOTSELECTED = 6;
  22186. {$ifend}
  22187. //--------------- local functions -------------------------------------------
  22188. procedure AlphaBlendSelection(Color: TColor);
  22189. var
  22190. R: TRect;
  22191. begin
  22192. // Take into account any window offset and size limitations in the target bitmap, as this is only as large
  22193. // as necessary and might not cover the whole node. For normal painting this does not matter (because of
  22194. // clipping) but for the MMX code there is no such check and it will crash badly when bitmap boundaries are
  22195. // crossed.
  22196. R := InnerRect;
  22197. OffsetRect(R, -WindowOrgX, 0);
  22198. if R.Left < 0 then
  22199. R.Left := 0;
  22200. if R.Right > MaxWidth then
  22201. R.Right := MaxWidth;
  22202. AlphaBlend(0, PaintInfo.Canvas.Handle, R, Point(0, 0), bmConstantAlphaAndColor,
  22203. FSelectionBlendFactor, ColorToRGB(Color));
  22204. end;
  22205. //---------------------------------------------------------------------------
  22206. procedure DrawBackground(State: Integer);
  22207. begin
  22208. // if the full row selection is disabled or toGridExtensions is in the MiscOptions, draw the selection
  22209. // into the InnerRect, otherwise into the RowRect
  22210. if not (toFullRowSelect in FOptions.FSelectionOptions) or (toGridExtensions in FOptions.FMiscOptions) then
  22211. DrawThemeBackground(Theme, PaintInfo.Canvas.Handle, TVP_TREEITEM, State, InnerRect, nil)
  22212. else
  22213. DrawThemeBackground(Theme, PaintInfo.Canvas.Handle, TVP_TREEITEM, State, RowRect, nil);
  22214. end;
  22215. procedure DrawThemedFocusRect(State: Integer);
  22216. var
  22217. Theme: HTHEME;
  22218. begin
  22219. Theme := OpenThemeData(Application.{$if CompilerVersion >= 20}ActiveFormHandle{$else}Handle{$ifend}, 'Explorer::ItemsView');
  22220. if not (toFullRowSelect in FOptions.FSelectionOptions) or (toGridExtensions in FOptions.FMiscOptions) then
  22221. DrawThemeBackground(Theme, PaintInfo.Canvas.Handle, LVP_LISTDETAIL, State, InnerRect, nil)
  22222. else
  22223. DrawThemeBackground(Theme, PaintInfo.Canvas.Handle, LVP_LISTDETAIL, State, RowRect, nil);
  22224. CloseThemeData(Theme);
  22225. end;
  22226. //--------------- end local functions ---------------------------------------
  22227. begin
  22228. if tsUseExplorerTheme in FStates then
  22229. begin
  22230. Theme := OpenThemeData(Application.{$if CompilerVersion >= 20}ActiveFormHandle{$else}Handle{$ifend}, 'Explorer::TreeView');
  22231. RowRect := Rect(0, PaintInfo.CellRect.Top, FRangeX, PaintInfo.CellRect.Bottom);
  22232. if (Header.Columns.Count = 0) and (toFullRowSelect in TreeOptions.SelectionOptions) then
  22233. RowRect.Right := Max(ClientWidth, RowRect.Right);
  22234. if toShowVertGridLines in FOptions.PaintOptions then
  22235. Dec(RowRect.Right);
  22236. end;
  22237. with PaintInfo, Canvas do
  22238. begin
  22239. // Fill cell background if its color differs from tree background.
  22240. with FHeader.FColumns do
  22241. if poColumnColor in PaintOptions then
  22242. begin
  22243. if (VclStyleEnabled and not (coParentColor in FHeader.FColumns[Column].FOptions)) then
  22244. Brush.Color := FColors.BackGroundColor
  22245. else
  22246. Brush.Color := Items[Column].Color;
  22247. FillRect(CellRect);
  22248. end;
  22249. // Let the application customize the cell background and the content rectangle.
  22250. DoBeforeCellPaint(Canvas, Node, Column, cpmPaint, CellRect, ContentRect);
  22251. InnerRect := ContentRect;
  22252. // The selection rectangle depends on alignment.
  22253. if not (toGridExtensions in FOptions.FMiscOptions) then
  22254. begin
  22255. case Alignment of
  22256. taLeftJustify:
  22257. with TWithSafeRect(InnerRect) do
  22258. if Left + NodeWidth < Right then
  22259. Right := Left + NodeWidth;
  22260. taCenter:
  22261. with TWithSafeRect(InnerRect) do
  22262. if (Right - Left) > NodeWidth then
  22263. begin
  22264. Left := (Left + Right - NodeWidth) div 2;
  22265. Right := Left + NodeWidth;
  22266. end;
  22267. taRightJustify:
  22268. with TWithSafeRect(InnerRect) do
  22269. if (Right - Left) > NodeWidth then
  22270. Left := Right - NodeWidth;
  22271. end;
  22272. end;
  22273. if (Column = FFocusedColumn) or (toFullRowSelect in FOptions.FSelectionOptions) then
  22274. begin
  22275. // Fill the selection rectangle.
  22276. if poDrawSelection in PaintOptions then
  22277. begin
  22278. if Node = FDropTargetNode then
  22279. begin
  22280. if (FLastDropMode = dmOnNode) or (vsSelected in Node.States) then
  22281. begin
  22282. Brush.Color := FColors.DropTargetColor;
  22283. Pen.Color := FColors.DropTargetBorderColor;
  22284. if (toGridExtensions in FOptions.FMiscOptions) or
  22285. (toFullRowSelect in FOptions.FSelectionOptions) then
  22286. InnerRect := CellRect;
  22287. if not IsRectEmpty(InnerRect) then
  22288. if tsUseExplorerTheme in FStates then
  22289. DrawBackground(TREIS_SELECTED)
  22290. else
  22291. if MMXAvailable and (toUseBlendedSelection in FOptions.PaintOptions) then
  22292. AlphaBlendSelection(Brush.Color)
  22293. else
  22294. with TWithSafeRect(InnerRect) do
  22295. RoundRect(Left, Top, Right, Bottom, FSelectionCurveRadius, FSelectionCurveRadius);
  22296. end
  22297. else
  22298. begin
  22299. Brush.Style := bsClear;
  22300. end;
  22301. end
  22302. else
  22303. if vsSelected in Node.States then
  22304. begin
  22305. if Focused or (toPopupMode in FOptions.FPaintOptions) then
  22306. begin
  22307. Brush.Color := FColors.FocusedSelectionColor;
  22308. Pen.Color := FColors.FocusedSelectionBorderColor;
  22309. end
  22310. else
  22311. begin
  22312. Brush.Color := FColors.UnfocusedSelectionColor;
  22313. Pen.Color := FColors.UnfocusedSelectionBorderColor;
  22314. end;
  22315. if (toGridExtensions in FOptions.FMiscOptions) or (toFullRowSelect in FOptions.FSelectionOptions) then
  22316. InnerRect := CellRect;
  22317. if not IsRectEmpty(InnerRect) then
  22318. if tsUseExplorerTheme in FStates then
  22319. begin
  22320. // If the node is also hot, its background will be drawn later.
  22321. if not (toHotTrack in FOptions.FPaintOptions) or (Node <> FCurrentHotNode) or
  22322. ((Column <> FCurrentHotColumn) and not (toFullRowSelect in FOptions.FSelectionOptions)) then
  22323. DrawBackground(IfThen(Self.Focused, TREIS_SELECTED, TREIS_SELECTEDNOTFOCUS));
  22324. end
  22325. else
  22326. if MMXAvailable and (toUseBlendedSelection in FOptions.PaintOptions) then
  22327. AlphaBlendSelection(Brush.Color)
  22328. else
  22329. with TWithSafeRect(InnerRect) do
  22330. RoundRect(Left, Top, Right, Bottom, FSelectionCurveRadius, FSelectionCurveRadius);
  22331. end;
  22332. end;
  22333. end;
  22334. if (tsUseExplorerTheme in FStates) and (toHotTrack in FOptions.FPaintOptions) and (Node = FCurrentHotNode) and
  22335. ((Column = FCurrentHotColumn) or (toFullRowSelect in FOptions.FSelectionOptions)) then
  22336. DrawBackground(IfThen((vsSelected in Node.States) and not (toAlwaysHideSelection in FOptions.FPaintOptions),
  22337. TREIS_HOTSELECTED, TREIS_HOT));
  22338. if (Column = FFocusedColumn) or (toFullRowSelect in FOptions.FSelectionOptions) then
  22339. begin
  22340. // draw focus rect
  22341. if (poDrawFocusRect in PaintOptions) and
  22342. (Focused or (toPopupMode in FOptions.FPaintOptions)) and (FFocusedNode = Node) and
  22343. ( (Column = FFocusedColumn) or
  22344. ((not (toExtendedFocus in FOptions.FSelectionOptions) or IsWinVistaOrAbove) and
  22345. (toFullRowSelect in FOptions.FSelectionOptions) and
  22346. (tsUseExplorerTheme in FStates) ) ) then
  22347. begin
  22348. TextColorBackup := GetTextColor(Handle);
  22349. SetTextColor(Handle, $FFFFFF);
  22350. BackColorBackup := GetBkColor(Handle);
  22351. SetBkColor(Handle, 0);
  22352. if not (toExtendedFocus in FOptions.FSelectionOptions) and (toFullRowSelect in FOptions.FSelectionOptions) and
  22353. (tsUseExplorerTheme in FStates) then
  22354. FocusRect := RowRect
  22355. else
  22356. if toGridExtensions in FOptions.FMiscOptions then
  22357. FocusRect := CellRect
  22358. else
  22359. FocusRect := InnerRect;
  22360. if tsUseExplorerTheme in FStates then
  22361. InflateRect(FocusRect, -1, -1);
  22362. if (tsUseExplorerTheme in FStates) and IsWinVistaOrAbove then
  22363. begin
  22364. //Draw focused unselected style like Windows 7 Explorer
  22365. if not (vsSelected in Node.States) then
  22366. DrawThemedFocusRect(LIS_NORMAL)
  22367. else
  22368. DrawBackground(TREIS_HOTSELECTED);
  22369. end
  22370. else
  22371. Windows.DrawFocusRect(Handle, FocusRect);
  22372. SetTextColor(Handle, TextColorBackup);
  22373. SetBkColor(Handle, BackColorBackup);
  22374. end;
  22375. end;
  22376. end;
  22377. if tsUseExplorerTheme in FStates then
  22378. CloseThemeData(Theme);
  22379. end;
  22380. //----------------------------------------------------------------------------------------------------------------------
  22381. function TBaseVirtualTree.ReadChunk(Stream: TStream; Version: Integer; Node: PVirtualNode; ChunkType,
  22382. ChunkSize: Integer): Boolean;
  22383. // Called while loading a tree structure, Node is already valid (allocated) at this point.
  22384. // The function handles the base and user chunks, any other chunk is marked as being unknown (result becomes False)
  22385. // and skipped. descendants may handle them by overriding this method.
  22386. // Returns True if the chunk could be handled, otherwise False.
  22387. var
  22388. ChunkBody: TBaseChunkBody;
  22389. Run: PVirtualNode;
  22390. LastPosition: Integer;
  22391. begin
  22392. case ChunkType of
  22393. BaseChunk:
  22394. begin
  22395. // Load base chunk's body (chunk header has already been consumed).
  22396. if Version > 1 then
  22397. Stream.Read(ChunkBody, SizeOf(ChunkBody))
  22398. else
  22399. begin
  22400. with ChunkBody do
  22401. begin
  22402. // In version prior to 2 there was a smaller chunk body. Hence we have to read it entry by entry now.
  22403. Stream.Read(ChildCount, SizeOf(ChildCount));
  22404. Stream.Read(NodeHeight, SizeOf(NodeHeight));
  22405. // TVirtualNodeStates was a byte sized type in version 1.
  22406. States := [];
  22407. Stream.Read(States, SizeOf(Byte));
  22408. // vsVisible is now in the place where vsSelected was before, but every node was visible in the old version
  22409. // so we need to fix this too.
  22410. if vsVisible in States then
  22411. Include(States, vsSelected)
  22412. else
  22413. Include(States, vsVisible);
  22414. Stream.Read(Align, SizeOf(Align));
  22415. Stream.Read(CheckState, SizeOf(CheckState));
  22416. Stream.Read(CheckType, SizeOf(CheckType));
  22417. end;
  22418. end;
  22419. with Node^ do
  22420. begin
  22421. // Set states first, in case the node is invisible.
  22422. States := ChunkBody.States;
  22423. NodeHeight := ChunkBody.NodeHeight;
  22424. TotalHeight := NodeHeight;
  22425. Align := ChunkBody.Align;
  22426. CheckState := ChunkBody.CheckState;
  22427. CheckType := ChunkBody.CheckType;
  22428. ChildCount := ChunkBody.ChildCount;
  22429. // Create and read child nodes.
  22430. while ChunkBody.ChildCount > 0 do
  22431. begin
  22432. Run := MakeNewNode;
  22433. Run.PrevSibling := Node.LastChild;
  22434. if Assigned(Run.PrevSibling) then
  22435. Run.Index := Run.PrevSibling.Index + 1;
  22436. if Assigned(Node.LastChild) then
  22437. Node.LastChild.NextSibling := Run
  22438. else
  22439. Node.FirstChild := Run;
  22440. Node.LastChild := Run;
  22441. Run.Parent := Node;
  22442. ReadNode(Stream, Version, Run);
  22443. Dec(ChunkBody.ChildCount);
  22444. end;
  22445. end;
  22446. Result := True;
  22447. end;
  22448. UserChunk:
  22449. if ChunkSize > 0 then
  22450. begin
  22451. // need to know whether the data was read
  22452. LastPosition := Stream.Position;
  22453. DoLoadUserData(Node, Stream);
  22454. // compare stream position to learn whether the data was read
  22455. Result := Stream.Position > LastPosition;
  22456. // Improve stability by advancing the stream to the chunk's real end if
  22457. // the application did not read what has been written.
  22458. if not Result or (Stream.Position <> (LastPosition + ChunkSize)) then
  22459. Stream.Position := LastPosition + ChunkSize;
  22460. end
  22461. else
  22462. Result := True;
  22463. else
  22464. // unknown chunk, skip it
  22465. Stream.Position := Stream.Position + ChunkSize;
  22466. Result := False;
  22467. end;
  22468. end;
  22469. //----------------------------------------------------------------------------------------------------------------------
  22470. procedure TBaseVirtualTree.ReadNode(Stream: TStream; Version: Integer; Node: PVirtualNode);
  22471. // Reads the anchor chunk of each node and initiates reading the sub chunks for this node
  22472. var
  22473. Header: TChunkHeader;
  22474. EndPosition: Integer;
  22475. begin
  22476. with Stream do
  22477. begin
  22478. // Read anchor chunk of the node.
  22479. Stream.Read(Header, SizeOf(Header));
  22480. if Header.ChunkType = NodeChunk then
  22481. begin
  22482. EndPosition := Stream.Position + Header.ChunkSize;
  22483. // Read all subchunks until the indicated chunk end position is reached in the stream.
  22484. while Position < EndPosition do
  22485. begin
  22486. // Read new chunk header.
  22487. Stream.Read(Header, SizeOf(Header));
  22488. ReadChunk(Stream, Version, Node, Header.ChunkType, Header.ChunkSize);
  22489. end;
  22490. // If the last chunk does not end at the given end position then there is something wrong.
  22491. if Position <> EndPosition then
  22492. ShowError(SCorruptStream2, hcTFCorruptStream2);
  22493. end
  22494. else
  22495. ShowError(SCorruptStream1, hcTFCorruptStream1);
  22496. end;
  22497. end;
  22498. //----------------------------------------------------------------------------------------------------------------------
  22499. procedure TBaseVirtualTree.RedirectFontChangeEvent(Canvas: TCanvas);
  22500. begin
  22501. if @Canvas.Font.OnChange <> @FOldFontChange then
  22502. begin
  22503. FOldFontChange := Canvas.Font.OnChange;
  22504. Canvas.Font.OnChange := FontChanged;
  22505. end;
  22506. end;
  22507. //----------------------------------------------------------------------------------------------------------------------
  22508. procedure TBaseVirtualTree.RemoveFromSelection(Node: PVirtualNode);
  22509. var
  22510. Index: Integer;
  22511. begin
  22512. if not FSelectionLocked then
  22513. begin
  22514. Assert(Assigned(Node), 'Node must not be nil!');
  22515. if vsSelected in Node.States then
  22516. begin
  22517. Exclude(Node.States, vsSelected);
  22518. if FindNodeInSelection(Node, Index, -1, -1) and (Index < FSelectionCount - 1) then
  22519. Move(FSelection[Index + 1], FSelection[Index], (FSelectionCount - Index - 1) * SizeOf(Pointer));
  22520. if FSelectionCount > 0 then
  22521. Dec(FSelectionCount);
  22522. SetLength(FSelection, FSelectionCount);
  22523. if FSelectionCount = 0 then
  22524. ResetRangeAnchor;
  22525. if FSelectionCount <= 1 then
  22526. UpdateNextNodeToSelect(Node);
  22527. DoRemoveFromSelection(Node);
  22528. Change(Node);
  22529. end;
  22530. end;
  22531. end;
  22532. //----------------------------------------------------------------------------------------------------------------------
  22533. procedure TBaseVirtualTree.UpdateNextNodeToSelect(Node: PVirtualNode);
  22534. // save a potential node to select after the currently selected node will be deleted.
  22535. // This will make the VT to behave more like the Win32 TreeView, which always selecta a new node if the currently
  22536. // selected one gets deleted.
  22537. begin
  22538. if not (toAlwaysSelectNode in TreeOptions.SelectionOptions) then
  22539. Exit;
  22540. if GetNextSibling(Node) <> nil then
  22541. FNextNodeToSelect := GetNextSibling(Node)
  22542. else if GetPreviousSibling(Node) <> nil then
  22543. FNextNodeToSelect := GetPreviousSibling(Node)
  22544. else if GetNodeLevel(Node) > 0 then
  22545. FNextNodeToSelect := Node.Parent
  22546. else
  22547. FNextNodeToSelect := GetFirstChild(Node);
  22548. end;//if Assigned(Node);
  22549. //----------------------------------------------------------------------------------------------------------------------
  22550. function TBaseVirtualTree.RenderOLEData(const FormatEtcIn: TFormatEtc; out Medium: TStgMedium;
  22551. ForClipboard: Boolean): HResult;
  22552. // Returns a memory expression of all currently selected nodes in the Medium structure.
  22553. // Note: The memory requirement of this method might be very high. This depends however on the requested storage format.
  22554. // For HGlobal (a global memory block) we need to render first all nodes to local memory and copy this then to
  22555. // the global memory in Medium. This is necessary because we have first to determine how much
  22556. // memory is needed before we can allocate it. Hence for a short moment we need twice the space as used by the
  22557. // nodes alone (plus the amount the nodes need in the tree anyway)!
  22558. // With IStream this does not happen. We directly stream out the nodes and pass the constructed stream along.
  22559. //--------------- local function --------------------------------------------
  22560. procedure WriteNodes(Stream: TStream);
  22561. var
  22562. Selection: TNodeArray;
  22563. I: Integer;
  22564. begin
  22565. if ForClipboard then
  22566. Selection := GetSortedCutCopySet(True)
  22567. else
  22568. Selection := GetSortedSelection(True);
  22569. for I := 0 to High(Selection) do
  22570. WriteNode(Stream, Selection[I]);
  22571. end;
  22572. //--------------- end local function ----------------------------------------
  22573. var
  22574. Data: PCardinal;
  22575. ResPointer: Pointer;
  22576. ResSize: Integer;
  22577. OLEStream: IStream;
  22578. VCLStream: TStream;
  22579. begin
  22580. ZeroMemory (@Medium, SizeOf(Medium));
  22581. // We can render the native clipboard format in two different storage media.
  22582. if (FormatEtcIn.cfFormat = CF_VIRTUALTREE) and (FormatEtcIn.tymed and (TYMED_HGLOBAL or TYMED_ISTREAM) <> 0) then
  22583. begin
  22584. VCLStream := nil;
  22585. try
  22586. Medium.unkForRelease := nil;
  22587. // Return data in one of the supported storage formats, prefer IStream.
  22588. if FormatEtcIn.tymed and TYMED_ISTREAM <> 0 then
  22589. begin
  22590. // Create an IStream on a memory handle (here it is 0 which indicates to implicitely allocated a handle).
  22591. // Do not use TStreamAdapter as it is not compatible with OLE (when flushing the clipboard OLE wants the HGlobal
  22592. // back which is not supported by TStreamAdapater).
  22593. CreateStreamOnHGlobal(0, True, OLEStream);
  22594. VCLStream := TOLEStream.Create(OLEStream);
  22595. WriteNodes(VCLStream);
  22596. // Rewind stream.
  22597. VCLStream.Position := 0;
  22598. Medium.tymed := TYMED_ISTREAM;
  22599. IUnknown(Medium.stm) := OLEStream;
  22600. Result := S_OK;
  22601. end
  22602. else
  22603. begin
  22604. VCLStream := TMemoryStream.Create;
  22605. WriteNodes(VCLStream);
  22606. ResPointer := TMemoryStream(VCLStream).Memory;
  22607. ResSize := VCLStream.Position;
  22608. // Allocate memory to hold the string.
  22609. if ResSize > 0 then
  22610. begin
  22611. Medium.hGlobal := GlobalAlloc(GHND or GMEM_SHARE, ResSize + SizeOf(Cardinal));
  22612. Data := GlobalLock(Medium.hGlobal);
  22613. // Store the size of the data too, for easy retrival.
  22614. Data^ := ResSize;
  22615. Inc(Data);
  22616. Move(ResPointer^, Data^, ResSize);
  22617. GlobalUnlock(Medium.hGlobal);
  22618. Medium.tymed := TYMED_HGLOBAL;
  22619. Result := S_OK;
  22620. end
  22621. else
  22622. Result := E_FAIL;
  22623. end;
  22624. finally
  22625. // We can free the VCL stream here since it was either a pure memory stream or only a wrapper around
  22626. // the OLEStream which exists independently.
  22627. VCLStream.Free;
  22628. end;
  22629. end
  22630. else // Ask application descendants to render self defined formats.
  22631. Result := DoRenderOLEData(FormatEtcIn, Medium, ForClipboard);
  22632. end;
  22633. //----------------------------------------------------------------------------------------------------------------------
  22634. procedure TBaseVirtualTree.ResetRangeAnchor;
  22635. // Called when there is no selected node anymore and the selection range anchor needs a new value.
  22636. begin
  22637. FRangeAnchor := FFocusedNode;
  22638. FLastSelectionLevel := -1;
  22639. end;
  22640. //----------------------------------------------------------------------------------------------------------------------
  22641. procedure TBaseVirtualTree.RestoreFontChangeEvent(Canvas: TCanvas);
  22642. begin
  22643. Canvas.Font.OnChange := FOldFontChange;
  22644. FOldFontChange := nil;
  22645. end;
  22646. //----------------------------------------------------------------------------------------------------------------------
  22647. procedure TBaseVirtualTree.SelectNodes(StartNode, EndNode: PVirtualNode; AddOnly: Boolean);
  22648. // Selects a range of nodes and unselects all other eventually selected nodes which are not in this range if
  22649. // AddOnly is False.
  22650. // EndNode must be visible while StartNode does not necessarily as in the case where the last focused node is the start
  22651. // node but it is a child of a node which has been collapsed previously. In this case the first visible parent node
  22652. // is used as start node. StartNode can be nil in which case the very first node in the tree is used.
  22653. var
  22654. NodeFrom,
  22655. NodeTo,
  22656. LastAnchor: PVirtualNode;
  22657. Index: Integer;
  22658. begin
  22659. Assert(Assigned(EndNode), 'EndNode must not be nil!');
  22660. if not FSelectionLocked then
  22661. begin
  22662. ClearTempCache;
  22663. if StartNode = nil then
  22664. StartNode := GetFirstVisibleNoInit(nil, True)
  22665. else
  22666. if not FullyVisible[StartNode] then
  22667. begin
  22668. StartNode := GetPreviousVisible(StartNode, True);
  22669. if StartNode = nil then
  22670. StartNode := GetFirstVisibleNoInit(nil, True);
  22671. end;
  22672. if CompareNodePositions(StartNode, EndNode, True) < 0 then
  22673. begin
  22674. NodeFrom := StartNode;
  22675. NodeTo := EndNode;
  22676. end
  22677. else
  22678. begin
  22679. NodeFrom := EndNode;
  22680. NodeTo := StartNode;
  22681. end;
  22682. // The range anchor will be reset by the following call.
  22683. LastAnchor := FRangeAnchor;
  22684. if not AddOnly then
  22685. InternalClearSelection;
  22686. while NodeFrom <> NodeTo do
  22687. begin
  22688. InternalCacheNode(NodeFrom);
  22689. NodeFrom := GetNextVisible(NodeFrom, True);
  22690. end;
  22691. // select last node too
  22692. InternalCacheNode(NodeFrom);
  22693. // now add them all in "one" step
  22694. AddToSelection(FTempNodeCache, FTempNodeCount);
  22695. ClearTempCache;
  22696. if Assigned(LastAnchor) and FindNodeInSelection(LastAnchor, Index, -1, -1) then
  22697. FRangeAnchor := LastAnchor;
  22698. end;
  22699. end;
  22700. //----------------------------------------------------------------------------------------------------------------------
  22701. procedure TBaseVirtualTree.SetFocusedNodeAndColumn(Node: PVirtualNode; Column: TColumnIndex);
  22702. var
  22703. OldColumn: TColumnIndex;
  22704. WasDifferent: Boolean;
  22705. begin
  22706. if not FHeader.AllowFocus(Column) then
  22707. Column := FFocusedColumn;
  22708. WasDifferent := (Node <> FFocusedNode) or (Column <> FFocusedColumn);
  22709. OldColumn := FFocusedColumn;
  22710. FFocusedColumn := Column;
  22711. DoFocusNode(Node, True);
  22712. // Check if the change was accepted.
  22713. if FFocusedNode = Node then
  22714. begin
  22715. CancelEditNode;
  22716. if WasDifferent then
  22717. DoFocusChange(FFocusedNode, FFocusedColumn);
  22718. end
  22719. else
  22720. // If the user did not accept the new cell to focus then set also the focused column back
  22721. // to its original state.
  22722. FFocusedColumn := OldColumn;
  22723. end;
  22724. //----------------------------------------------------------------------------------------------------------------------
  22725. procedure TBaseVirtualTree.SkipNode(Stream: TStream);
  22726. // Skips the data for the next node in the given stream (including the child nodes).
  22727. var
  22728. Header: TChunkHeader;
  22729. begin
  22730. with Stream do
  22731. begin
  22732. // read achor chunk of the node
  22733. Stream.Read(Header, SizeOf(Header));
  22734. if Header.ChunkType = NodeChunk then
  22735. Stream.Position := Stream.Position + Header.ChunkSize
  22736. else
  22737. ShowError(SCorruptStream1, hcTFCorruptStream1);
  22738. end;
  22739. end;
  22740. //----------------------------------------------------------------------------------------------------------------------
  22741. var
  22742. PanningWindowClass: TWndClass = (
  22743. style: 0;
  22744. lpfnWndProc: @DefWindowProc;
  22745. cbClsExtra: 0;
  22746. cbWndExtra: 0;
  22747. hInstance: 0;
  22748. hIcon: 0;
  22749. hCursor: 0;
  22750. hbrBackground: 0;
  22751. lpszMenuName: nil;
  22752. lpszClassName: 'VTPanningWindow'
  22753. );
  22754. procedure TBaseVirtualTree.StartWheelPanning(Position: TPoint);
  22755. // Called when wheel panning should start. A little helper window is created to indicate the reference position,
  22756. // which determines in which direction and how far wheel panning/scrolling will happen.
  22757. //--------------- local function --------------------------------------------
  22758. function CreateClipRegion: HRGN;
  22759. // In order to avoid doing all the transparent drawing ourselves we use a
  22760. // window region for the wheel window.
  22761. // Since we only work on a very small image (32x32 pixels) this is acceptable.
  22762. var
  22763. Start, X, Y: Integer;
  22764. Temp: HRGN;
  22765. begin
  22766. Assert(not FPanningImage.Empty, 'Invalid wheel panning image.');
  22767. // Create an initial region on which we operate.
  22768. Result := CreateRectRgn(0, 0, 0, 0);
  22769. with FPanningImage, Canvas do
  22770. begin
  22771. for Y := 0 to Height - 1 do
  22772. begin
  22773. Start := -1;
  22774. for X := 0 to Width - 1 do
  22775. begin
  22776. // Start a new span if we found a non-transparent pixel and no span is currently started.
  22777. if (Start = -1) and (Pixels[X, Y] <> clFuchsia) then
  22778. Start := X
  22779. else
  22780. if (Start > -1) and (Pixels[X, Y] = clFuchsia) then
  22781. begin
  22782. // A non-transparent span is finished. Add it to the result region.
  22783. Temp := CreateRectRgn(Start, Y, X, Y + 1);
  22784. CombineRgn(Result, Result, Temp, RGN_OR);
  22785. DeleteObject(Temp);
  22786. Start := -1;
  22787. end;
  22788. end;
  22789. // If there is an open span then add this also to the result region.
  22790. if Start > -1 then
  22791. begin
  22792. Temp := CreateRectRgn(Start, Y, Width, Y + 1);
  22793. CombineRgn(Result, Result, Temp, RGN_OR);
  22794. DeleteObject(Temp);
  22795. end;
  22796. end;
  22797. end;
  22798. // The resulting region is used as window region so we must not delete it.
  22799. // Windows will own it after the assignment below.
  22800. end;
  22801. //--------------- end local function ----------------------------------------
  22802. var
  22803. TempClass: TWndClass;
  22804. ClassRegistered: Boolean;
  22805. ImageName: string;
  22806. Pt: TPoint;
  22807. begin
  22808. // Set both panning and scrolling flag. One will be removed shortly depending on whether the middle mouse button is
  22809. // released before the mouse is moved or vice versa. The first case is referred to as wheel scrolling while the
  22810. // latter is called wheel panning.
  22811. StopTimer(ScrollTimer);
  22812. DoStateChange([tsWheelPanning, tsWheelScrolling]);
  22813. // Register the helper window class.
  22814. PanningWindowClass.hInstance := HInstance;
  22815. ClassRegistered := GetClassInfo(HInstance, PanningWindowClass.lpszClassName, TempClass);
  22816. if not ClassRegistered or (TempClass.lpfnWndProc <> @DefWindowProc) then
  22817. begin
  22818. if ClassRegistered then
  22819. Windows.UnregisterClass(PanningWindowClass.lpszClassName, HInstance);
  22820. Windows.RegisterClass(PanningWindowClass);
  22821. end;
  22822. // Create the helper window and show it at the given position without activating it.
  22823. Pt := ClientToScreen(Position);
  22824. FPanningWindow := CreateWindowEx(WS_EX_TOOLWINDOW, PanningWindowClass.lpszClassName, nil, WS_POPUP, Pt.X - 16, Pt.Y - 16,
  22825. 32, 32, Handle, 0, HInstance, nil);
  22826. FPanningImage := TBitmap.Create;
  22827. if Integer(FRangeX) > ClientWidth then
  22828. begin
  22829. if Integer(FRangeY) > ClientHeight then
  22830. ImageName := 'VT_MOVEALL'
  22831. else
  22832. ImageName := 'VT_MOVEEW';
  22833. end
  22834. else
  22835. ImageName := 'VT_MOVENS';
  22836. FPanningImage.LoadFromResourceName(HInstance, ImageName);
  22837. SetWindowRgn(FPanningWindow, CreateClipRegion, False);
  22838. {$ifdef CPUX64}
  22839. SetWindowLongPtr(FPanningWindow, GWLP_WNDPROC, LONG_PTR(Classes.MakeObjectInstance(PanningWindowProc)));
  22840. {$else}
  22841. SetWindowLong(FPanningWindow, GWL_WNDPROC, Longint(Classes.MakeObjectInstance(PanningWindowProc)));
  22842. {$endif CPUX64}
  22843. ShowWindow(FPanningWindow, SW_SHOWNOACTIVATE);
  22844. // Setup the panscroll timer and capture all mouse input.
  22845. SetFocus;
  22846. SetCapture(Handle);
  22847. SetTimer(Handle, ScrollTimer, 20, nil);
  22848. end;
  22849. //----------------------------------------------------------------------------------------------------------------------
  22850. procedure TBaseVirtualTree.StopWheelPanning;
  22851. // Stops panning if currently active and destroys the helper window.
  22852. var
  22853. Instance: Pointer;
  22854. begin
  22855. if [tsWheelPanning, tsWheelScrolling] * FStates <> [] then
  22856. begin
  22857. // Release the mouse capture and stop the panscroll timer.
  22858. StopTimer(ScrollTimer);
  22859. ReleaseCapture;
  22860. DoStateChange([], [tsWheelPanning, tsWheelScrolling]);
  22861. // Destroy the helper window.
  22862. {$ifdef CPUX64}
  22863. Instance := Pointer(GetWindowLongPtr(FPanningWindow, GWLP_WNDPROC));
  22864. {$else}
  22865. Instance := Pointer(GetWindowLong(FPanningWindow, GWL_WNDPROC));
  22866. {$endif CPUX64}
  22867. DestroyWindow(FPanningWindow);
  22868. if Instance <> @DefWindowProc then
  22869. Classes.FreeObjectInstance(Instance);
  22870. FPanningWindow := 0;
  22871. FPanningImage.Free;
  22872. FPanningImage := nil;
  22873. DeleteObject(FPanningCursor);
  22874. FPanningCursor := 0;
  22875. Windows.SetCursor(Screen.Cursors[Cursor]);
  22876. end;
  22877. end;
  22878. //----------------------------------------------------------------------------------------------------------------------
  22879. procedure TBaseVirtualTree.StructureChange(Node: PVirtualNode; Reason: TChangeReason);
  22880. begin
  22881. AdviseChangeEvent(True, Node, Reason);
  22882. if FUpdateCount = 0 then
  22883. begin
  22884. if (FChangeDelay > 0) and not (tsSynchMode in FStates) then
  22885. SetTimer(Handle, StructureChangeTimer, FChangeDelay, nil)
  22886. else
  22887. DoStructureChange(Node, Reason);
  22888. end;
  22889. end;
  22890. //----------------------------------------------------------------------------------------------------------------------
  22891. function TBaseVirtualTree.SuggestDropEffect(Source: TObject; Shift: TShiftState; Pt: TPoint;
  22892. AllowedEffects: Integer): Integer;
  22893. // determines the drop action to take if the drag'n drop operation ends on this tree
  22894. // Note: Source can be any Delphi object not just a virtual tree
  22895. begin
  22896. Result := AllowedEffects;
  22897. // prefer MOVE if source and target are the same control, otherwise whatever is allowed as initial value
  22898. if Assigned(Source) and (Source = Self) then
  22899. if (AllowedEffects and DROPEFFECT_MOVE) <> 0 then
  22900. Result := DROPEFFECT_MOVE
  22901. else // no change
  22902. else
  22903. // drag between different applicatons
  22904. if (AllowedEffects and DROPEFFECT_COPY) <> 0 then
  22905. Result := DROPEFFECT_COPY;
  22906. // consider modifier keys and what is allowed at the moment, if none of the following conditions apply then
  22907. // the initial value just set is used
  22908. if ssCtrl in Shift then
  22909. begin
  22910. // copy or link
  22911. if ssShift in Shift then
  22912. begin
  22913. // link
  22914. if (AllowedEffects and DROPEFFECT_LINK) <> 0 then
  22915. Result := DROPEFFECT_LINK;
  22916. end
  22917. else
  22918. begin
  22919. // copy
  22920. if (AllowedEffects and DROPEFFECT_COPY) <> 0 then
  22921. Result := DROPEFFECT_COPY;
  22922. end;
  22923. end
  22924. else
  22925. begin
  22926. // move, link or default
  22927. if ssShift in Shift then
  22928. begin
  22929. // move
  22930. if (AllowedEffects and DROPEFFECT_MOVE) <> 0 then
  22931. Result := DROPEFFECT_MOVE;
  22932. end
  22933. else
  22934. begin
  22935. // link or default
  22936. if ssAlt in Shift then
  22937. begin
  22938. // link
  22939. if (AllowedEffects and DROPEFFECT_LINK) <> 0 then
  22940. Result := DROPEFFECT_LINK;
  22941. end;
  22942. // else default
  22943. end;
  22944. end;
  22945. end;
  22946. //----------------------------------------------------------------------------------------------------------------------
  22947. procedure TBaseVirtualTree.ToggleSelection(StartNode, EndNode: PVirtualNode);
  22948. // Switchs the selection state of a range of nodes.
  22949. // Note: This method is specifically designed to help selecting ranges with the keyboard and considers therefore
  22950. // the range anchor.
  22951. var
  22952. NodeFrom,
  22953. NodeTo: PVirtualNode;
  22954. NewSize: Integer;
  22955. Position: Integer;
  22956. begin
  22957. if not FSelectionLocked then
  22958. begin
  22959. Assert(Assigned(EndNode), 'EndNode must not be nil!');
  22960. if StartNode = nil then
  22961. StartNode := FRoot.FirstChild
  22962. else
  22963. if not FullyVisible[StartNode] then
  22964. StartNode := GetPreviousVisible(StartNode, True);
  22965. Position := CompareNodePositions(StartNode, EndNode);
  22966. // nothing to do if start and end node are the same
  22967. if Position <> 0 then
  22968. begin
  22969. if Position < 0 then
  22970. begin
  22971. NodeFrom := StartNode;
  22972. NodeTo := EndNode;
  22973. end
  22974. else
  22975. begin
  22976. NodeFrom := EndNode;
  22977. NodeTo := StartNode;
  22978. end;
  22979. ClearTempCache;
  22980. // 1) toggle the start node if it is before the range anchor
  22981. if CompareNodePositions(NodeFrom, FRangeAnchor) < 0 then
  22982. if not (vsSelected in NodeFrom.States) then
  22983. InternalCacheNode(NodeFrom)
  22984. else
  22985. InternalRemoveFromSelection(NodeFrom);
  22986. // 2) toggle all nodes within the range
  22987. NodeFrom := GetNextVisible(NodeFrom, True);
  22988. while NodeFrom <> NodeTo do
  22989. begin
  22990. if not (vsSelected in NodeFrom.States) then
  22991. InternalCacheNode(NodeFrom)
  22992. else
  22993. InternalRemoveFromSelection(NodeFrom);
  22994. NodeFrom := GetNextVisible(NodeFrom, True);
  22995. end;
  22996. // 3) toggle end node if it is after the range anchor
  22997. if CompareNodePositions(NodeFrom, FRangeAnchor) > 0 then
  22998. if not (vsSelected in NodeFrom.States) then
  22999. InternalCacheNode(NodeFrom)
  23000. else
  23001. InternalRemoveFromSelection(NodeFrom);
  23002. // Do some housekeeping if there was a change.
  23003. NewSize := PackArray(FSelection, FSelectionCount);
  23004. if NewSize > -1 then
  23005. begin
  23006. FSelectionCount := NewSize;
  23007. SetLength(FSelection, FSelectionCount);
  23008. end;
  23009. // If the range went over the anchor then we need to reselect it.
  23010. if not (vsSelected in FRangeAnchor.States) then
  23011. InternalCacheNode(FRangeAnchor);
  23012. if FTempNodeCount > 0 then
  23013. AddToSelection(FTempNodeCache, FTempNodeCount);
  23014. ClearTempCache;
  23015. end;
  23016. end;
  23017. end;
  23018. //----------------------------------------------------------------------------------------------------------------------
  23019. procedure TBaseVirtualTree.UnselectNodes(StartNode, EndNode: PVirtualNode);
  23020. // Deselects a range of nodes.
  23021. // EndNode must be visible while StartNode must not as in the case where the last focused node is the start node
  23022. // but it is a child of a node which has been collapsed previously. In this case the first visible parent node
  23023. // is used as start node. StartNode can be nil in which case the very first node in the tree is used.
  23024. var
  23025. NodeFrom,
  23026. NodeTo: PVirtualNode;
  23027. NewSize: Integer;
  23028. begin
  23029. if not FSelectionLocked then
  23030. begin
  23031. Assert(Assigned(EndNode), 'EndNode must not be nil!');
  23032. if StartNode = nil then
  23033. StartNode := FRoot.FirstChild
  23034. else
  23035. if not FullyVisible[StartNode] then
  23036. begin
  23037. StartNode := GetPreviousVisible(StartNode, True);
  23038. if StartNode = nil then
  23039. StartNode := FRoot.FirstChild;
  23040. end;
  23041. if CompareNodePositions(StartNode, EndNode) < 0 then
  23042. begin
  23043. NodeFrom := StartNode;
  23044. NodeTo := EndNode;
  23045. end
  23046. else
  23047. begin
  23048. NodeFrom := EndNode;
  23049. NodeTo := StartNode;
  23050. end;
  23051. while NodeFrom <> NodeTo do
  23052. begin
  23053. InternalRemoveFromSelection(NodeFrom);
  23054. NodeFrom := GetNextVisible(NodeFrom, True);
  23055. end;
  23056. // Deselect last node too.
  23057. InternalRemoveFromSelection(NodeFrom);
  23058. // Do some housekeeping.
  23059. NewSize := PackArray(FSelection, FSelectionCount);
  23060. if NewSize > -1 then
  23061. begin
  23062. FSelectionCount := NewSize;
  23063. SetLength(FSelection, FSelectionCount);
  23064. end;
  23065. end;
  23066. end;
  23067. //----------------------------------------------------------------------------------------------------------------------
  23068. procedure TBaseVirtualTree.UpdateColumnCheckState(Col: TVirtualTreeColumn);
  23069. begin
  23070. Col.CheckState := DetermineNextCheckState(Col.CheckType, Col.CheckState);
  23071. end;
  23072. //----------------------------------------------------------------------------------------------------------------------
  23073. procedure TBaseVirtualTree.UpdateDesigner;
  23074. var
  23075. ParentForm: TCustomForm;
  23076. begin
  23077. if (csDesigning in ComponentState) and not (csUpdating in ComponentState) then
  23078. begin
  23079. ParentForm := GetParentForm(Self);
  23080. if Assigned(ParentForm) and Assigned(ParentForm.Designer) then
  23081. ParentForm.Designer.Modified;
  23082. end;
  23083. end;
  23084. //----------------------------------------------------------------------------------------------------------------------
  23085. procedure TBaseVirtualTree.UpdateHeaderRect;
  23086. // Calculates the rectangle the header occupies in non-client area.
  23087. // These coordinates are in window rectangle.
  23088. var
  23089. OffsetX,
  23090. OffsetY: Integer;
  23091. EdgeSize: Integer;
  23092. Size: TSize;
  23093. begin
  23094. FHeaderRect := Rect(0, 0, Width, Height);
  23095. // Consider borders...
  23096. Size := GetBorderDimensions;
  23097. InflateRect(FHeaderRect, Size.cx, Size.cy);
  23098. // ... and bevels.
  23099. OffsetX := BorderWidth;
  23100. OffsetY := BorderWidth;
  23101. if BevelKind <> bkNone then
  23102. begin
  23103. EdgeSize := 0;
  23104. if BevelInner <> bvNone then
  23105. Inc(EdgeSize, BevelWidth);
  23106. if BevelOuter <> bvNone then
  23107. Inc(EdgeSize, BevelWidth);
  23108. if beLeft in BevelEdges then
  23109. Inc(OffsetX, EdgeSize);
  23110. if beTop in BevelEdges then
  23111. Inc(OffsetY, EdgeSize);
  23112. end;
  23113. InflateRect(FHeaderRect, -OffsetX, -OffsetY);
  23114. if hoVisible in FHeader.FOptions then
  23115. begin
  23116. if FHeaderRect.Left <= FHeaderRect.Right then
  23117. FHeaderRect.Bottom := FHeaderRect.Top + Integer(FHeader.FHeight)
  23118. else
  23119. FHeaderRect := Rect(0, 0, 0, 0);
  23120. end
  23121. else
  23122. FHeaderRect.Bottom := FHeaderRect.Top;
  23123. end;
  23124. //----------------------------------------------------------------------------------------------------------------------
  23125. procedure TBaseVirtualTree.UpdateEditBounds;
  23126. // Used to update the bounds of the current node editor if editing is currently active.
  23127. var
  23128. R: TRect;
  23129. Dummy: Integer;
  23130. CurrentAlignment: TAlignment;
  23131. CurrentBidiMode: TBidiMode;
  23132. begin
  23133. if (tsEditing in FStates) and Assigned(FFocusedNode) and
  23134. (FEditColumn < FHeader.Columns.Count) then // prevent EArgumentOutOfRangeException
  23135. begin
  23136. if (GetCurrentThreadId <> MainThreadID) then
  23137. begin
  23138. // UpdateEditBounds() will be called at the end of the thread
  23139. Exit;
  23140. end;
  23141. if vsMultiline in FFocusedNode.States then
  23142. R := GetDisplayRect(FFocusedNode, FEditColumn, True, False)
  23143. else
  23144. R := GetDisplayRect(FFocusedNode, FEditColumn, True, True);
  23145. if (toGridExtensions in FOptions.FMiscOptions) then
  23146. begin
  23147. // Adjust edit bounds depending on alignment and bidi mode.
  23148. if FEditColumn <= NoColumn then
  23149. begin
  23150. CurrentAlignment := Alignment;
  23151. CurrentBidiMode := BiDiMode;
  23152. end
  23153. else
  23154. begin
  23155. CurrentAlignment := FHeader.Columns[FEditColumn].FAlignment;
  23156. CurrentBidiMode := FHeader.Columns[FEditColumn].FBiDiMode;
  23157. end;
  23158. // Consider bidi mode here. In RTL context does left alignment actually mean right alignment and vice versa.
  23159. if CurrentBidiMode <> bdLeftToRight then
  23160. ChangeBiDiModeAlignment(CurrentAlignment);
  23161. if CurrentAlignment = taLeftJustify then
  23162. FHeader.Columns.GetColumnBounds(FEditColumn, Dummy, R.Right)
  23163. else
  23164. FHeader.Columns.GetColumnBounds(FEditColumn, R.Left, Dummy);
  23165. end;
  23166. if toShowHorzGridLines in TreeOptions.PaintOptions then
  23167. Dec(R.Bottom);
  23168. R.Bottom := R.Top + Max(R.Bottom - R.Top, FEditLink.GetBounds.Bottom - FEditLink.GetBounds.Top); // Ensure to never decrease the size of the currently active edit control. Helps to prevent issue #159
  23169. FEditLink.SetBounds(R);
  23170. end;
  23171. end;
  23172. //----------------------------------------------------------------------------------------------------------------------
  23173. const
  23174. ScrollMasks: array[Boolean] of Cardinal = (0, SIF_DISABLENOSCROLL);
  23175. const // Region identifiers for GetRandomRgn
  23176. CLIPRGN = 1;
  23177. METARGN = 2;
  23178. APIRGN = 3;
  23179. SYSRGN = 4;
  23180. function GetRandomRgn(DC: HDC; Rgn: HRGN; iNum: Integer): Integer; stdcall; external 'GDI32.DLL';
  23181. procedure TBaseVirtualTree.UpdateWindowAndDragImage(const Tree: TBaseVirtualTree; TreeRect: TRect; UpdateNCArea,
  23182. ReshowDragImage: Boolean);
  23183. // Method to repaint part of the window area which is not covered by the drag image and to initiate a recapture
  23184. // of the drag image.
  23185. // Note: This method must only be called during a drag operation and the tree passed in is the one managing the current
  23186. // drag image (so it is the actual drag source).
  23187. var
  23188. DragRegion, // the region representing the drag image
  23189. UpdateRegion, // the unclipped region within the tree to be updated
  23190. NCRegion: HRGN; // the region representing the non-client area of the tree
  23191. DragRect,
  23192. NCRect: TRect;
  23193. RedrawFlags: Cardinal;
  23194. VisibleTreeRegion: HRGN;
  23195. DC: HDC;
  23196. begin
  23197. if IntersectRect(TreeRect, TreeRect, ClientRect) then
  23198. begin
  23199. // Retrieve the visible region of the window. This is important to avoid overpainting parts of other windows
  23200. // which overlap this one.
  23201. VisibleTreeRegion := CreateRectRgn(0, 0, 1, 1);
  23202. DC := GetDCEx(Handle, 0, DCX_CACHE or DCX_WINDOW or DCX_CLIPSIBLINGS or DCX_CLIPCHILDREN);
  23203. GetRandomRgn(DC, VisibleTreeRegion, SYSRGN);
  23204. ReleaseDC(Handle, DC);
  23205. // The drag image will figure out itself what part of the rectangle can be recaptured.
  23206. // Recapturing is not done by taking a snapshot of the screen, but by letting the tree draw itself
  23207. // into the back bitmap of the drag image. So the order here is unimportant.
  23208. Tree.FDragImage.RecaptureBackground(Self, TreeRect, VisibleTreeRegion, UpdateNCArea, ReshowDragImage);
  23209. // Calculate the screen area not covered by the drag image and which needs an update.
  23210. DragRect := Tree.FDragImage.GetDragImageRect;
  23211. MapWindowPoints(0, Handle, DragRect, 2);
  23212. DragRegion := CreateRectRgnIndirect(DragRect);
  23213. // Start with non-client area if requested.
  23214. if UpdateNCArea then
  23215. begin
  23216. // Compute the part of the non-client area which must be updated.
  23217. // Determine the outer rectangle of the entire tree window.
  23218. GetWindowRect(Handle, NCRect);
  23219. // Express the tree window rectangle in client coordinates (because RedrawWindow wants them so).
  23220. MapWindowPoints(0, Handle, NCRect, 2);
  23221. NCRegion := CreateRectRgnIndirect(NCRect);
  23222. // Determine client rect in screen coordinates and create another region for it.
  23223. UpdateRegion := CreateRectRgnIndirect(ClientRect);
  23224. // Create a region which only contains the NC part by subtracting out the client area.
  23225. CombineRgn(NCRegion, NCRegion, UpdateRegion, RGN_DIFF);
  23226. // Subtract also out what is hidden by the drag image.
  23227. CombineRgn(NCRegion, NCRegion, DragRegion, RGN_DIFF);
  23228. RedrawWindow(Handle, nil, NCRegion, RDW_FRAME or RDW_NOERASE or RDW_NOCHILDREN or RDW_INVALIDATE or RDW_VALIDATE or
  23229. RDW_UPDATENOW);
  23230. DeleteObject(NCRegion);
  23231. DeleteObject(UpdateRegion);
  23232. end;
  23233. UpdateRegion := CreateRectRgnIndirect(TreeRect);
  23234. RedrawFlags := RDW_INVALIDATE or RDW_VALIDATE or RDW_UPDATENOW or RDW_NOERASE or RDW_NOCHILDREN;
  23235. // Remove the part of the update region which is covered by the drag image.
  23236. CombineRgn(UpdateRegion, UpdateRegion, DragRegion, RGN_DIFF);
  23237. RedrawWindow(Handle, nil, UpdateRegion, RedrawFlags);
  23238. DeleteObject(UpdateRegion);
  23239. DeleteObject(DragRegion);
  23240. DeleteObject(VisibleTreeRegion);
  23241. end;
  23242. end;
  23243. //----------------------------------------------------------------------------------------------------------------------
  23244. procedure TBaseVirtualTree.ValidateCache;
  23245. // Starts cache validation if not already done by adding this instance to the worker thread's waiter list
  23246. // (if not already there) and signalling the thread it can start validating.
  23247. begin
  23248. // Wait for thread to stop validation if it is currently validating this tree's cache.
  23249. InterruptValidation;
  23250. FStartIndex := 0;
  23251. if (tsValidationNeeded in FStates) and (FVisibleCount > CacheThreshold) then
  23252. begin
  23253. // Tell the thread this tree needs actually something to do.
  23254. WorkerThread.AddTree(Self);
  23255. SetEvent(WorkEvent);
  23256. end;
  23257. end;
  23258. //----------------------------------------------------------------------------------------------------------------------
  23259. procedure TBaseVirtualTree.ValidateNodeDataSize(var Size: Integer);
  23260. begin
  23261. Size := SizeOf(Pointer);
  23262. if Assigned(FOnGetNodeDataSize) then
  23263. FOnGetNodeDataSize(Self, Size);
  23264. end;
  23265. //----------------------------------------------------------------------------------------------------------------------
  23266. procedure TBaseVirtualTree.VclStyleChanged;
  23267. begin
  23268. {$if CompilerVersion >= 23 }
  23269. FSetOrRestoreBevelKindAndBevelWidth := True;
  23270. FVclStyleEnabled := StyleServices.Enabled and not StyleServices.IsSystemStyle;
  23271. if not VclStyleEnabled then
  23272. begin
  23273. if FSavedBevelKind <> BevelKind then
  23274. BevelKind := FSavedBevelKind;
  23275. if FSavedBorderWidth <> BorderWidth then
  23276. BorderWidth := FSavedBorderWidth;
  23277. end
  23278. else
  23279. begin
  23280. if BevelKind <> bkNone then
  23281. BevelKind := bkNone;
  23282. if BorderWidth <> 0 then
  23283. BorderWidth := 0;
  23284. end;
  23285. FSetOrRestoreBevelKindAndBevelWidth := False;
  23286. {$else}
  23287. FVclStyleEnabled := False;
  23288. {$ifend}
  23289. end;
  23290. //----------------------------------------------------------------------------------------------------------------------
  23291. //PROFILE-NO
  23292. procedure TBaseVirtualTree.WndProc(var Message: TMessage);
  23293. var
  23294. Handled: Boolean;
  23295. begin
  23296. Handled := False;
  23297. // Try the header whether it needs to take this message.
  23298. if Assigned(FHeader) and (FHeader.FStates <> []) then
  23299. Handled := FHeader.HandleMessage(Message);
  23300. if not Handled then
  23301. begin
  23302. // For auto drag mode, let tree handle itself, instead of TControl.
  23303. if not (csDesigning in ComponentState) and
  23304. ((Message.Msg = WM_LBUTTONDOWN) or (Message.Msg = WM_LBUTTONDBLCLK)) then
  23305. begin
  23306. if (DragMode = dmAutomatic) and (DragKind = dkDrag) then
  23307. begin
  23308. if IsControlMouseMsg(TWMMouse(Message)) then
  23309. Handled := True;
  23310. if not Handled then
  23311. begin
  23312. ControlState := ControlState + [csLButtonDown];
  23313. Dispatch(Message); // overrides TControl's BeginDrag
  23314. Handled := True;
  23315. end;
  23316. end;
  23317. end;
  23318. if not Handled and Assigned(FHeader) then
  23319. Handled := FHeader.HandleMessage(Message);
  23320. if not Handled then
  23321. begin
  23322. if (Message.Msg in [WM_NCLBUTTONDOWN, WM_NCRBUTTONDOWN, WM_NCMBUTTONDOWN]) and not Focused and CanFocus then
  23323. SetFocus;
  23324. inherited;
  23325. end;
  23326. end;
  23327. end;
  23328. //PROFILE-YES
  23329. //----------------------------------------------------------------------------------------------------------------------
  23330. procedure TBaseVirtualTree.WriteChunks(Stream: TStream; Node: PVirtualNode);
  23331. // Writes the core chunks for Node into the stream.
  23332. // Note: descendants can optionally override this method to add other node specific chunks.
  23333. // Keep in mind that this method is also called for the root node. Using this fact in descendants you can
  23334. // create a kind of "global" chunks not directly bound to a specific node.
  23335. var
  23336. Header: TChunkHeader;
  23337. LastPosition,
  23338. ChunkSize: Integer;
  23339. Chunk: TBaseChunk;
  23340. Run: PVirtualNode;
  23341. begin
  23342. with Stream do
  23343. begin
  23344. // 1. The base chunk...
  23345. LastPosition := Position;
  23346. Chunk.Header.ChunkType := BaseChunk;
  23347. with Node^, Chunk do
  23348. begin
  23349. Body.ChildCount := ChildCount;
  23350. Body.NodeHeight := NodeHeight;
  23351. // Some states are only temporary so take them out as they make no sense at the new location.
  23352. Body.States := States - [vsChecking, vsCutOrCopy, vsDeleting, vsOnFreeNodeCallRequired, vsHeightMeasured];
  23353. Body.Align := Align;
  23354. Body.CheckState := CheckState;
  23355. Body.CheckType := CheckType;
  23356. Body.Reserved := 0;
  23357. end;
  23358. // write the base chunk
  23359. Write(Chunk, SizeOf(Chunk));
  23360. // 2. ... directly followed by the child node chunks (actually they are child chunks of
  23361. // the base chunk)
  23362. if vsInitialized in Node.States then
  23363. begin
  23364. Run := Node.FirstChild;
  23365. while Assigned(Run) do
  23366. begin
  23367. WriteNode(Stream, Run);
  23368. Run := Run.NextSibling;
  23369. end;
  23370. end;
  23371. FinishChunkHeader(Stream, LastPosition, Position);
  23372. // 3. write user data
  23373. LastPosition := Position;
  23374. Header.ChunkType := UserChunk;
  23375. Write(Header, SizeOf(Header));
  23376. DoSaveUserData(Node, Stream);
  23377. // check if the application actually wrote data
  23378. ChunkSize := Position - LastPosition - SizeOf(TChunkHeader);
  23379. // seek back to start of chunk if nothing has been written
  23380. if ChunkSize = 0 then
  23381. begin
  23382. Position := LastPosition;
  23383. Size := Size - SizeOf(Header);
  23384. end
  23385. else
  23386. FinishChunkHeader(Stream, LastPosition, Position);
  23387. end;
  23388. end;
  23389. //----------------------------------------------------------------------------------------------------------------------
  23390. procedure TBaseVirtualTree.WriteNode(Stream: TStream; Node: PVirtualNode);
  23391. // Writes the "cover" chunk for Node to Stream and initiates writing child nodes and chunks.
  23392. var
  23393. LastPosition: Integer;
  23394. Header: TChunkHeader;
  23395. begin
  23396. // Initialize the node first if necessary and wanted.
  23397. if toInitOnSave in FOptions.FMiscOptions then
  23398. begin
  23399. if not (vsInitialized in Node.States) then
  23400. InitNode(Node);
  23401. if (vsHasChildren in Node.States) and (Node.ChildCount = 0) then
  23402. InitChildren(Node);
  23403. end;
  23404. with Stream do
  23405. begin
  23406. LastPosition := Position;
  23407. // Emit the anchor chunk.
  23408. Header.ChunkType := NodeChunk;
  23409. Write(Header, SizeOf(Header));
  23410. // Write other chunks to stream taking their size into this chunk's size.
  23411. WriteChunks(Stream, Node);
  23412. // Update chunk size.
  23413. FinishChunkHeader(Stream, LastPosition, Position);
  23414. end;
  23415. end;
  23416. //----------------------------------------------------------------------------------------------------------------------
  23417. function TBaseVirtualTree.AbsoluteIndex(Node: PVirtualNode): Cardinal;
  23418. begin
  23419. Result := 0;
  23420. while Assigned(Node) and (Node <> FRoot) do
  23421. begin
  23422. if not (vsInitialized in Node.States) then
  23423. InitNode(Node);
  23424. if Assigned(Node.PrevSibling) then
  23425. begin
  23426. // if there's a previous sibling then add its total count to the result
  23427. Node := Node.PrevSibling;
  23428. Inc(Result, Node.TotalCount);
  23429. end
  23430. else
  23431. begin
  23432. Node := Node.Parent;
  23433. if Node <> FRoot then
  23434. Inc(Result);
  23435. end;
  23436. end;
  23437. end;
  23438. //----------------------------------------------------------------------------------------------------------------------
  23439. function TBaseVirtualTree.AddChild(Parent: PVirtualNode; UserData: Pointer = nil): PVirtualNode;
  23440. // Adds a new node to the given parent node. This is simply done by increasing the child count of the
  23441. // parent node. If Parent is nil then the new node is added as (last) top level node.
  23442. // UserData can be used to set the first SizeOf(Pointer) bytes of the user data area to an initial value which can be used
  23443. // in OnInitNode and will also cause to trigger the OnFreeNode event (if <> nil) even if the node is not yet
  23444. // "officially" initialized.
  23445. // AddChild is a compatibility method and will implicitly validate the parent node. This is however
  23446. // against the virtual paradigm and hence I dissuade from its usage.
  23447. var
  23448. NodeData: ^Pointer;
  23449. begin
  23450. if not (toReadOnly in FOptions.FMiscOptions) then
  23451. begin
  23452. CancelEditNode;
  23453. if Parent = nil then
  23454. Parent := FRoot;
  23455. if not (vsInitialized in Parent.States) then
  23456. InitNode(Parent);
  23457. // Locally stop updates of the tree in order to avoid usage of the new node before it is correctly set up.
  23458. // If the update count was 0 on enter then there will be a correct update at the end of this method.
  23459. Inc(FUpdateCount);
  23460. try
  23461. SetChildCount(Parent, Parent.ChildCount + 1);
  23462. // Update the hidden children flag of the parent. Nodes are added as being visible by default.
  23463. Exclude(Parent.States, vsAllChildrenHidden);
  23464. finally
  23465. Dec(FUpdateCount);
  23466. end;
  23467. Result := Parent.LastChild;
  23468. // Check if there is initial user data and there is also enough user data space allocated.
  23469. if Assigned(UserData) then
  23470. if FNodeDataSize >= SizeOf(Pointer) then
  23471. begin
  23472. NodeData := Pointer(PByte(@Result.Data) + FTotalInternalDataSize);
  23473. NodeData^ := UserData;
  23474. Include(Result.States, vsOnFreeNodeCallRequired);
  23475. end
  23476. else
  23477. ShowError(SCannotSetUserData, hcTFCannotSetUserData);
  23478. InvalidateCache;
  23479. if FUpdateCount = 0 then
  23480. begin
  23481. ValidateCache;
  23482. if tsStructureChangePending in FStates then
  23483. begin
  23484. if Parent = FRoot then
  23485. StructureChange(nil, crChildAdded)
  23486. else
  23487. StructureChange(Parent, crChildAdded);
  23488. end;
  23489. if (toAutoSort in FOptions.FAutoOptions) and (FHeader.FSortColumn > InvalidColumn) then
  23490. Sort(Parent, FHeader.FSortColumn, FHeader.FSortDirection, True);
  23491. InvalidateToBottom(Parent);
  23492. UpdateScrollBars(True);
  23493. end;
  23494. end
  23495. else
  23496. Result := nil;
  23497. end;
  23498. //----------------------------------------------------------------------------------------------------------------------
  23499. procedure TBaseVirtualTree.AddFromStream(Stream: TStream; TargetNode: PVirtualNode);
  23500. // loads nodes from the given stream and adds them to TargetNode
  23501. // the current content is not cleared before the load process starts (see also LoadFromStream)
  23502. var
  23503. ThisID: TMagicID;
  23504. Version,
  23505. Count: Cardinal;
  23506. Node: PVirtualNode;
  23507. begin
  23508. if not (toReadOnly in FOptions.FMiscOptions) then
  23509. begin
  23510. // check first whether this is a stream we can read
  23511. Stream.ReadBuffer(ThisID, SizeOf(TMagicID));
  23512. if (ThisID[0] = MagicID[0]) and
  23513. (ThisID[1] = MagicID[1]) and
  23514. (ThisID[2] = MagicID[2]) and
  23515. (ThisID[5] = MagicID[5]) then
  23516. begin
  23517. Version := Word(ThisID[3]);
  23518. if Version <= VTTreeStreamVersion then
  23519. begin
  23520. BeginUpdate;
  23521. try
  23522. if Version < 2 then
  23523. Count := MaxInt
  23524. else
  23525. Stream.ReadBuffer(Count, SizeOf(Count));
  23526. while (Stream.Position < Stream.Size) and (Count > 0) do
  23527. begin
  23528. Dec(Count);
  23529. Node := MakeNewNode;
  23530. InternalConnectNode(Node, TargetNode, Self, amAddChildLast);
  23531. InternalAddFromStream(Stream, Version, Node);
  23532. end;
  23533. if TargetNode = FRoot then
  23534. DoNodeCopied(nil)
  23535. else
  23536. DoNodeCopied(TargetNode);
  23537. finally
  23538. EndUpdate;
  23539. end;
  23540. end
  23541. else
  23542. ShowError(SWrongStreamVersion, hcTFWrongStreamVersion);
  23543. end
  23544. else
  23545. ShowError(SWrongStreamVersion, hcTFWrongStreamVersion);
  23546. end;
  23547. end;
  23548. //----------------------------------------------------------------------------------------------------------------------
  23549. procedure TBaseVirtualTree.AfterConstruction;
  23550. begin
  23551. inherited;
  23552. if FRoot = nil then
  23553. InitRootNode;
  23554. end;
  23555. //----------------------------------------------------------------------------------------------------------------------
  23556. procedure TBaseVirtualTree.Assign(Source: TPersistent);
  23557. begin
  23558. if (Source is TBaseVirtualTree) and not (toReadOnly in FOptions.FMiscOptions) then
  23559. with Source as TBaseVirtualTree do
  23560. begin
  23561. Self.Align := Align;
  23562. Self.Anchors := Anchors;
  23563. Self.AutoScrollDelay := AutoScrollDelay;
  23564. Self.AutoScrollInterval := AutoScrollInterval;
  23565. Self.AutoSize := AutoSize;
  23566. Self.Background := Background;
  23567. Self.BevelEdges := BevelEdges;
  23568. Self.BevelInner := BevelInner;
  23569. Self.BevelKind := BevelKind;
  23570. Self.BevelOuter := BevelOuter;
  23571. Self.BevelWidth := BevelWidth;
  23572. Self.BiDiMode := BiDiMode;
  23573. Self.BorderStyle := BorderStyle;
  23574. Self.BorderWidth := BorderWidth;
  23575. Self.ChangeDelay := ChangeDelay;
  23576. Self.CheckImageKind := CheckImageKind;
  23577. Self.Color := Color;
  23578. Self.Colors.Assign(Colors);
  23579. Self.Constraints.Assign(Constraints);
  23580. Self.Ctl3D := Ctl3D;
  23581. Self.DefaultNodeHeight := DefaultNodeHeight;
  23582. Self.DefaultPasteMode := DefaultPasteMode;
  23583. Self.DragCursor := DragCursor;
  23584. Self.DragImageKind := DragImageKind;
  23585. Self.DragKind := DragKind;
  23586. Self.DragMode := DragMode;
  23587. Self.Enabled := Enabled;
  23588. Self.Font := Font;
  23589. Self.Header := Header;
  23590. Self.HintAnimation := HintAnimation;
  23591. Self.HintMode := HintMode;
  23592. Self.HotCursor := HotCursor;
  23593. Self.Images := Images;
  23594. Self.ImeMode := ImeMode;
  23595. Self.ImeName := ImeName;
  23596. Self.Indent := Indent;
  23597. Self.Margin := Margin;
  23598. Self.NodeAlignment := NodeAlignment;
  23599. Self.NodeDataSize := NodeDataSize;
  23600. Self.TreeOptions := TreeOptions;
  23601. Self.ParentBiDiMode := ParentBiDiMode;
  23602. Self.ParentColor := ParentColor;
  23603. Self.ParentCtl3D := ParentCtl3D;
  23604. Self.ParentFont := ParentFont;
  23605. Self.ParentShowHint := ParentShowHint;
  23606. Self.PopupMenu := PopupMenu;
  23607. Self.RootNodeCount := RootNodeCount;
  23608. Self.ScrollBarOptions := ScrollBarOptions;
  23609. Self.ShowHint := ShowHint;
  23610. Self.StateImages := StateImages;
  23611. {$if CompilerVersion >= 24}
  23612. Self.StyleElements := StyleElements;
  23613. {$ifend}
  23614. Self.TabOrder := TabOrder;
  23615. Self.TabStop := TabStop;
  23616. Self.Visible := Visible;
  23617. Self.SelectionCurveRadius := SelectionCurveRadius;
  23618. Self.SelectionBlendFactor := SelectionBlendFactor;
  23619. Self.EmptyListMessage := EmptyListMessage;
  23620. end
  23621. else
  23622. inherited;
  23623. end;
  23624. //----------------------------------------------------------------------------------------------------------------------
  23625. procedure TBaseVirtualTree.AutoScale();
  23626. // If toAutoChangeScale is set, this method ensures that the defaulz node height is set corectly.
  23627. var
  23628. lTextHeight: Cardinal;
  23629. begin
  23630. if (toAutoChangeScale in TreeOptions.AutoOptions) then
  23631. begin
  23632. Canvas.Font.Assign(Self.Font);
  23633. lTextHeight := Canvas.TextHeight('Tg');
  23634. if (lTextHeight > Self.DefaultNodeHeight) then
  23635. Self.DefaultNodeHeight := lTextHeight;
  23636. end;
  23637. end;
  23638. //----------------------------------------------------------------------------------------------------------------------
  23639. procedure TBaseVirtualTree.BeginDrag(Immediate: Boolean; Threshold: Integer);
  23640. // Reintroduced method to allow to start OLE drag'n drop as well as VCL drag'n drop.
  23641. begin
  23642. if FDragType = dtVCL then
  23643. begin
  23644. DoStateChange([tsVCLDragPending]);
  23645. inherited;
  23646. end
  23647. else
  23648. if (FStates * [tsOLEDragPending, tsOLEDragging]) = [] then
  23649. begin
  23650. // Drag start position has already been recorded in WMMouseDown.
  23651. if Threshold < 0 then
  23652. FDragThreshold := Mouse.DragThreshold
  23653. else
  23654. FDragThreshold := Threshold;
  23655. if Immediate then
  23656. DoDragging(FLastClickPos)
  23657. else
  23658. DoStateChange([tsOLEDragPending]);
  23659. end;
  23660. end;
  23661. //----------------------------------------------------------------------------------------------------------------------
  23662. procedure TBaseVirtualTree.BeginSynch;
  23663. // Starts the synchronous update mode (if not already active).
  23664. begin
  23665. if not (csDestroying in ComponentState) then
  23666. begin
  23667. if FSynchUpdateCount = 0 then
  23668. begin
  23669. DoUpdating(usBeginSynch);
  23670. // Stop all timers...
  23671. StopTimer(ChangeTimer);
  23672. StopTimer(StructureChangeTimer);
  23673. StopTimer(ExpandTimer);
  23674. StopTimer(EditTimer);
  23675. StopTimer(HeaderTimer);
  23676. StopTimer(ScrollTimer);
  23677. StopTimer(SearchTimer);
  23678. FSearchBuffer := '';
  23679. FLastSearchNode := nil;
  23680. DoStateChange([], [tsEditPending, tsScrollPending, tsScrolling, tsIncrementalSearching]);
  23681. // ...and trigger pending update states.
  23682. if tsStructureChangePending in FStates then
  23683. DoStructureChange(FLastStructureChangeNode, FLastStructureChangeReason);
  23684. if tsChangePending in FStates then
  23685. DoChange(FLastChangedNode);
  23686. end
  23687. else
  23688. DoUpdating(usSynch);
  23689. end;
  23690. Inc(FSynchUpdateCount);
  23691. DoStateChange([tsSynchMode]);
  23692. end;
  23693. //----------------------------------------------------------------------------------------------------------------------
  23694. procedure TBaseVirtualTree.BeginUpdate;
  23695. begin
  23696. if not (csDestroying in ComponentState) then
  23697. begin
  23698. if FUpdateCount = 0 then
  23699. begin
  23700. DoUpdating(usBegin);
  23701. SetUpdateState(True);
  23702. end
  23703. else
  23704. DoUpdating(usUpdate);
  23705. end;
  23706. Inc(FUpdateCount);
  23707. DoStateChange([tsUpdating]);
  23708. end;
  23709. //----------------------------------------------------------------------------------------------------------------------
  23710. procedure TBaseVirtualTree.CancelCutOrCopy;
  23711. // Resets nodes which are marked as being cut.
  23712. var
  23713. Run: PVirtualNode;
  23714. begin
  23715. if ([tsCutPending, tsCopyPending] * FStates) <> [] then
  23716. begin
  23717. Run := FRoot.FirstChild;
  23718. while Assigned(Run) do
  23719. begin
  23720. if vsCutOrCopy in Run.States then
  23721. Exclude(Run.States, vsCutOrCopy);
  23722. Run := GetNextNoInit(Run);
  23723. end;
  23724. end;
  23725. DoStateChange([], [tsCutPending, tsCopyPending]);
  23726. end;
  23727. //----------------------------------------------------------------------------------------------------------------------
  23728. function TBaseVirtualTree.CancelEditNode: Boolean;
  23729. // Called by the application or the current edit link to cancel the edit action.
  23730. begin
  23731. if HandleAllocated and ([tsEditing, tsEditPending] * FStates <> []) then
  23732. Result := DoCancelEdit
  23733. else
  23734. Result := True;
  23735. end;
  23736. //----------------------------------------------------------------------------------------------------------------------
  23737. procedure TBaseVirtualTree.CancelOperation;
  23738. // Called by the application to cancel a long-running operation.
  23739. begin
  23740. if FOperationCount > 0 then
  23741. FOperationCanceled := True;
  23742. end;
  23743. //----------------------------------------------------------------------------------------------------------------------
  23744. function TBaseVirtualTree.CanEdit(Node: PVirtualNode; Column: TColumnIndex): Boolean;
  23745. // Returns True if the given node can be edited.
  23746. begin
  23747. Result := (toEditable in FOptions.FMiscOptions) and Enabled and not (toReadOnly in FOptions.FMiscOptions)
  23748. and ((Column < 0) or (coEditable in FHeader.Columns[Column].Options));
  23749. DoCanEdit(Node, Column, Result);
  23750. end;
  23751. //----------------------------------------------------------------------------------------------------------------------
  23752. function TBaseVirtualTree.CanFocus: Boolean;
  23753. var
  23754. Form: TCustomForm;
  23755. begin
  23756. Result := inherited CanFocus;
  23757. if Result and not (csDesigning in ComponentState) then
  23758. begin
  23759. Form := GetParentForm(Self);
  23760. Result := (Form = nil) or (Form.Enabled and Form.Visible);
  23761. end;
  23762. end;
  23763. //----------------------------------------------------------------------------------------------------------------------
  23764. procedure TBaseVirtualTree.Clear;
  23765. begin
  23766. if not (toReadOnly in FOptions.FMiscOptions) or (csDestroying in ComponentState) then
  23767. begin
  23768. BeginUpdate;
  23769. try
  23770. InterruptValidation;
  23771. if IsEditing then
  23772. CancelEditNode;
  23773. if ClipboardStates * FStates <> [] then
  23774. begin
  23775. OleSetClipboard(nil);
  23776. DoStateChange([], ClipboardStates);
  23777. end;
  23778. ClearSelection;
  23779. FFocusedNode := nil;
  23780. FLastSelected := nil;
  23781. FCurrentHotNode := nil;
  23782. FDropTargetNode := nil;
  23783. FLastChangedNode := nil;
  23784. FRangeAnchor := nil;
  23785. FCheckNode := nil;
  23786. FLastVCLDragTarget := nil;
  23787. FLastSearchNode := nil;
  23788. DeleteChildren(FRoot, True);
  23789. FOffsetX := 0;
  23790. FOffsetY := 0;
  23791. finally
  23792. EndUpdate;
  23793. end;
  23794. end;
  23795. end;
  23796. //----------------------------------------------------------------------------------------------------------------------
  23797. procedure TBaseVirtualTree.ClearChecked;
  23798. var
  23799. Node: PVirtualNode;
  23800. begin
  23801. Node := RootNode.FirstChild;
  23802. while Assigned(Node) do
  23803. begin
  23804. if Node.CheckState <> csUncheckedNormal then
  23805. CheckState[Node] := csUncheckedNormal;
  23806. Node := GetNextNoInit(Node);
  23807. end;
  23808. end;
  23809. //----------------------------------------------------------------------------------------------------------------------
  23810. procedure TBaseVirtualTree.ClearSelection;
  23811. var
  23812. Node: PVirtualNode;
  23813. Dummy: Integer;
  23814. R: TRect;
  23815. Counter: Integer;
  23816. begin
  23817. if not FSelectionLocked and (FSelectionCount > 0) and not (csDestroying in ComponentState) then
  23818. begin
  23819. if (FUpdateCount = 0) and HandleAllocated and (FVisibleCount > 0) then
  23820. begin
  23821. // Iterate through nodes currently visible in the client area and invalidate them.
  23822. Node := GetNodeAt(0, 0, True, Dummy);
  23823. if Assigned(Node) then
  23824. R := GetDisplayRect(Node, NoColumn, False);
  23825. Counter := FSelectionCount;
  23826. while Assigned(Node) do
  23827. begin
  23828. R.Bottom := R.Top + Integer(NodeHeight[Node]);
  23829. if vsSelected in Node.States then
  23830. begin
  23831. InvalidateRect(Handle, @R, False);
  23832. Dec(Counter);
  23833. // Only try as many nodes as are selected.
  23834. if Counter = 0 then
  23835. Break;
  23836. end;
  23837. R.Top := R.Bottom;
  23838. if R.Top > ClientHeight then
  23839. Break;
  23840. Node := GetNextVisibleNoInit(Node, True);
  23841. end;
  23842. end;
  23843. InternalClearSelection;
  23844. Change(nil);
  23845. end;
  23846. end;
  23847. //----------------------------------------------------------------------------------------------------------------------
  23848. function TBaseVirtualTree.CopyTo(Source: PVirtualNode; Tree: TBaseVirtualTree; Mode: TVTNodeAttachMode;
  23849. ChildrenOnly: Boolean): PVirtualNode;
  23850. // A simplified CopyTo method to allow to copy nodes to the root of another tree.
  23851. begin
  23852. Result := CopyTo(Source, Tree.FRoot, Mode, ChildrenOnly);
  23853. end;
  23854. //----------------------------------------------------------------------------------------------------------------------
  23855. function TBaseVirtualTree.CopyTo(Source, Target: PVirtualNode; Mode: TVTNodeAttachMode;
  23856. ChildrenOnly: Boolean): PVirtualNode;
  23857. // Copies Source and all its child nodes to Target.
  23858. // Mode is used to specify further where to add the new node actually (as sibling of Target or as child of Target).
  23859. // Result is the newly created node to which source has been copied if ChildrenOnly is False or just contains Target
  23860. // in the other case.
  23861. // ChildrenOnly determines whether to copy also the source node or only its child nodes.
  23862. var
  23863. TargetTree: TBaseVirtualTree;
  23864. Stream: TMemoryStream;
  23865. begin
  23866. Assert(TreeFromNode(Source) = Self, 'The source tree must contain the source node.');
  23867. Result := nil;
  23868. if (Mode <> amNoWhere) and Assigned(Source) and (Source <> FRoot) then
  23869. begin
  23870. // Assume that an empty destination means the root in this (the source) tree.
  23871. if Target = nil then
  23872. begin
  23873. TargetTree := Self;
  23874. Target := FRoot;
  23875. Mode := amAddChildFirst;
  23876. end
  23877. else
  23878. TargetTree := TreeFromNode(Target);
  23879. if not (toReadOnly in TargetTree.FOptions.FMiscOptions) then
  23880. begin
  23881. if Target = TargetTree.FRoot then
  23882. begin
  23883. case Mode of
  23884. amInsertBefore:
  23885. Mode := amAddChildFirst;
  23886. amInsertAfter:
  23887. Mode := amAddChildLast;
  23888. end;
  23889. end;
  23890. Stream := TMemoryStream.Create;
  23891. try
  23892. // Write all nodes into a temprary stream depending on the ChildrenOnly flag.
  23893. if not ChildrenOnly then
  23894. WriteNode(Stream, Source)
  23895. else
  23896. begin
  23897. Source := Source.FirstChild;
  23898. while Assigned(Source) do
  23899. begin
  23900. WriteNode(Stream, Source);
  23901. Source := Source.NextSibling;
  23902. end;
  23903. end;
  23904. // Now load the serialized nodes into the target node (tree).
  23905. TargetTree.BeginUpdate;
  23906. try
  23907. Stream.Position := 0;
  23908. while Stream.Position < Stream.Size do
  23909. begin
  23910. Result := TargetTree.MakeNewNode;
  23911. InternalConnectNode(Result, Target, TargetTree, Mode);
  23912. TargetTree.InternalAddFromStream(Stream, VTTreeStreamVersion, Result);
  23913. if not DoNodeCopying(Result, Target) then
  23914. begin
  23915. TargetTree.DeleteNode(Result);
  23916. Result := nil;
  23917. end
  23918. else
  23919. DoNodeCopied(Result);
  23920. end;
  23921. if ChildrenOnly then
  23922. Result := Target;
  23923. finally
  23924. TargetTree.EndUpdate;
  23925. end;
  23926. finally
  23927. Stream.Free;
  23928. end;
  23929. with TargetTree do
  23930. begin
  23931. InvalidateCache;
  23932. if FUpdateCount = 0 then
  23933. begin
  23934. ValidateCache;
  23935. UpdateScrollBars(True);
  23936. Invalidate;
  23937. end;
  23938. StructureChange(Source, crNodeCopied);
  23939. end;
  23940. end;
  23941. end;
  23942. end;
  23943. //----------------------------------------------------------------------------------------------------------------------
  23944. procedure TBaseVirtualTree.CopyToClipboard;
  23945. var
  23946. DataObject: IDataObject;
  23947. begin
  23948. if FSelectionCount > 0 then
  23949. begin
  23950. DataObject := TVTDataObject.Create(Self, True) as IDataObject;
  23951. if OleSetClipboard(DataObject) = S_OK then
  23952. begin
  23953. MarkCutCopyNodes;
  23954. DoStateChange([tsCopyPending]);
  23955. Invalidate;
  23956. end;
  23957. end;
  23958. end;
  23959. //----------------------------------------------------------------------------------------------------------------------
  23960. procedure TBaseVirtualTree.CutToClipboard;
  23961. begin
  23962. if (FSelectionCount > 0) and not (toReadOnly in FOptions.FMiscOptions) then
  23963. begin
  23964. if OleSetClipboard(TVTDataObject.Create(Self, True)) = S_OK then
  23965. begin
  23966. MarkCutCopyNodes;
  23967. DoStateChange([tsCutPending], [tsCopyPending]);
  23968. Invalidate;
  23969. end;
  23970. end;
  23971. end;
  23972. //----------------------------------------------------------------------------------------------------------------------
  23973. procedure TBaseVirtualTree.DeleteChildren(Node: PVirtualNode; ResetHasChildren: Boolean = False);
  23974. // Removes all children and their children from memory without changing the vsHasChildren style by default.
  23975. var
  23976. Run,
  23977. Mark: PVirtualNode;
  23978. LastTop,
  23979. LastLeft,
  23980. NewSize: Integer;
  23981. ParentVisible: Boolean;
  23982. begin
  23983. if Assigned(Node) and (Node.ChildCount > 0) and not (toReadOnly in FOptions.FMiscOptions) then
  23984. begin
  23985. Assert(not (tsIterating in FStates), 'Deleting nodes during tree iteration leads to invalid pointers.');
  23986. // The code below uses some flags for speed improvements which may cause invalid pointers if updates of
  23987. // the tree happen. Hence switch updates off until we have finished the operation.
  23988. Inc(FUpdateCount);
  23989. try
  23990. InterruptValidation;
  23991. LastLeft := -FEffectiveOffsetX;
  23992. LastTop := FOffsetY;
  23993. // Make a local copy of the visibility state of this node to speed up
  23994. // adjusting the visible nodes count.
  23995. ParentVisible := Node = FRoot;
  23996. if not ParentVisible then
  23997. ParentVisible := FullyVisible[Node] and (vsExpanded in Node.States);
  23998. // Show that we are clearing the child list, to avoid registering structure change events.
  23999. Include(Node.States, vsClearing);
  24000. Run := Node.LastChild;
  24001. while Assigned(Run) do
  24002. begin
  24003. if ParentVisible and IsEffectivelyVisible[Run] then
  24004. Dec(FVisibleCount);
  24005. Include(Run.States, vsDeleting);
  24006. Mark := Run;
  24007. Run := Run.PrevSibling;
  24008. // Important, to avoid exchange of invalid pointers while disconnecting the node.
  24009. if Assigned(Run) then
  24010. Run.NextSibling := nil;
  24011. DeleteNode(Mark);
  24012. end;
  24013. Exclude(Node.States, vsClearing);
  24014. if ResetHasChildren then
  24015. Exclude(Node.States, vsHasChildren);
  24016. if Node <> FRoot then
  24017. Exclude(Node.States, vsExpanded);
  24018. Node.ChildCount := 0;
  24019. if (Node = FRoot) or (vsDeleting in Node.States) then
  24020. begin
  24021. Node.TotalHeight := FDefaultNodeHeight + NodeHeight[Node];
  24022. Node.TotalCount := 1;
  24023. end
  24024. else
  24025. begin
  24026. AdjustTotalHeight(Node, NodeHeight[Node]);
  24027. AdjustTotalCount(Node, 1);
  24028. end;
  24029. Node.FirstChild := nil;
  24030. Node.LastChild := nil;
  24031. finally
  24032. Dec(FUpdateCount);
  24033. end;
  24034. InvalidateCache;
  24035. if FUpdateCount = 0 then
  24036. begin
  24037. NewSize := PackArray(FSelection, FSelectionCount);
  24038. if NewSize > -1 then
  24039. begin
  24040. FSelectionCount := NewSize;
  24041. SetLength(FSelection, FSelectionCount);
  24042. end;
  24043. ValidateCache;
  24044. UpdateScrollBars(True);
  24045. // Invalidate entire tree if it scrolled e.g. to make the last node also the
  24046. // bottom node in the treeview.
  24047. if (LastLeft <> FOffsetX) or (LastTop <> FOffsetY) then
  24048. Invalidate
  24049. else
  24050. InvalidateToBottom(Node);
  24051. end;
  24052. StructureChange(Node, crChildDeleted);
  24053. end
  24054. else if ResetHasChildren then
  24055. Exclude(Node.States, vsHasChildren);
  24056. end;
  24057. //----------------------------------------------------------------------------------------------------------------------
  24058. procedure TBaseVirtualTree.DeleteNode(Node: PVirtualNode; Reindex: Boolean = True);
  24059. var
  24060. LastTop,
  24061. LastLeft: Integer;
  24062. LastParent: PVirtualNode;
  24063. WasInSynchMode: Boolean;
  24064. ParentClearing: Boolean;
  24065. begin
  24066. if Assigned(Node) and (Node <> FRoot) and not (toReadOnly in FOptions.FMiscOptions) then
  24067. begin
  24068. Assert(not (tsIterating in FStates), 'Deleting nodes during tree iteration leads to invalid pointers.');
  24069. // Determine parent node for structure change notification.
  24070. ParentClearing := vsClearing in Node.Parent.States;
  24071. LastParent := Node.Parent;
  24072. if not ParentClearing then
  24073. begin
  24074. if LastParent = FRoot then
  24075. StructureChange(nil, crChildDeleted)
  24076. else
  24077. StructureChange(LastParent, crChildDeleted);
  24078. end;
  24079. LastLeft := -FEffectiveOffsetX;
  24080. LastTop := FOffsetY;
  24081. if vsSelected in Node.States then
  24082. begin
  24083. if FUpdateCount = 0 then
  24084. begin
  24085. // Go temporarily into sync mode to avoid a delayed change event for the node
  24086. // when unselecting.
  24087. WasInSynchMode := tsSynchMode in FStates;
  24088. Include(FStates, tsSynchMode);
  24089. RemoveFromSelection(Node);
  24090. if not WasInSynchMode then
  24091. Exclude(FStates, tsSynchMode);
  24092. InvalidateToBottom(LastParent);
  24093. end
  24094. else
  24095. InternalRemoveFromSelection(Node);
  24096. end
  24097. else
  24098. InvalidateToBottom(LastParent);
  24099. if tsHint in FStates then
  24100. begin
  24101. Application.CancelHint;
  24102. DoStateChange([], [tsHint]);
  24103. end;
  24104. if not ParentClearing then
  24105. InterruptValidation;
  24106. DeleteChildren(Node);
  24107. InternalDisconnectNode(Node, False, Reindex);
  24108. DoFreeNode(Node);
  24109. if not ParentClearing then
  24110. begin
  24111. DetermineHiddenChildrenFlag(LastParent);
  24112. InvalidateCache;
  24113. if FUpdateCount = 0 then
  24114. begin
  24115. ValidateCache;
  24116. UpdateScrollBars(True);
  24117. // Invalidate entire tree if it scrolled e.g. to make the last node also the
  24118. // bottom node in the treeview.
  24119. if (LastLeft <> FOffsetX) or (LastTop <> FOffsetY) then
  24120. Invalidate;
  24121. end;
  24122. end;
  24123. end;
  24124. end;
  24125. //----------------------------------------------------------------------------------------------------------------------
  24126. procedure TBaseVirtualTree.DeleteSelectedNodes;
  24127. // Deletes all currently selected nodes (including their child nodes).
  24128. var
  24129. Nodes: TNodeArray;
  24130. I: Integer;
  24131. LevelChange: Boolean;
  24132. begin
  24133. Nodes := nil;
  24134. if (FSelectionCount > 0) and not (toReadOnly in FOptions.FMiscOptions) then
  24135. begin
  24136. BeginUpdate;
  24137. try
  24138. Nodes := GetSortedSelection(True);
  24139. for I := High(Nodes) downto 1 do
  24140. begin
  24141. LevelChange := Nodes[I].Parent <> Nodes[I - 1].Parent;
  24142. DeleteNode(Nodes[I], LevelChange);
  24143. end;
  24144. DeleteNode(Nodes[0]);
  24145. finally
  24146. EndUpdate;
  24147. end;
  24148. end;
  24149. end;
  24150. //----------------------------------------------------------------------------------------------------------------------
  24151. function TBaseVirtualTree.Dragging: Boolean;
  24152. begin
  24153. // Check for both OLE drag'n drop as well as VCL drag'n drop.
  24154. Result := ([tsOLEDragPending, tsOLEDragging] * FStates <> []) or inherited Dragging;
  24155. end;
  24156. //----------------------------------------------------------------------------------------------------------------------
  24157. function TBaseVirtualTree.EditNode(Node: PVirtualNode; Column: TColumnIndex): Boolean;
  24158. // Application triggered edit event for the given node.
  24159. // Returns True if the tree started editing otherwise False.
  24160. begin
  24161. Assert(Assigned(Node), 'Node must not be nil.');
  24162. Assert((Column > InvalidColumn) and (Column < FHeader.Columns.Count),
  24163. 'Column must be a valid column index (-1 if no header is shown).');
  24164. Result := tsEditing in FStates;
  24165. // If the tree is already editing then we don't disrupt this.
  24166. if not Result and not (toReadOnly in FOptions.FMiscOptions) then
  24167. begin
  24168. FocusedNode := Node;
  24169. if Assigned(FFocusedNode) and (Node = FFocusedNode) and CanEdit(FFocusedNode, Column) then
  24170. begin
  24171. FEditColumn := Column;
  24172. if not (vsInitialized in Node.States) then
  24173. InitNode(Node);
  24174. DoEdit;
  24175. Result := tsEditing in FStates;
  24176. end
  24177. else
  24178. Result := False;
  24179. end;
  24180. end;
  24181. //----------------------------------------------------------------------------------------------------------------------
  24182. function TBaseVirtualTree.EndEditNode: Boolean;
  24183. // Called to finish a current edit action or stop the edit timer if an edit operation is pending.
  24184. begin
  24185. if [tsEditing, tsEditPending] * FStates <> [] then
  24186. Result := DoEndEdit
  24187. else
  24188. Result := True;
  24189. end;
  24190. //----------------------------------------------------------------------------------------------------------------------
  24191. procedure TBaseVirtualTree.EndSynch;
  24192. begin
  24193. if FSynchUpdateCount > 0 then
  24194. Dec(FSynchUpdateCount);
  24195. if not (csDestroying in ComponentState) then
  24196. begin
  24197. if FSynchUpdateCount = 0 then
  24198. begin
  24199. DoStateChange([], [tsSynchMode]);
  24200. DoUpdating(usEndSynch);
  24201. end
  24202. else
  24203. DoUpdating(usSynch);
  24204. end;
  24205. end;
  24206. //----------------------------------------------------------------------------------------------------------------------
  24207. procedure TBaseVirtualTree.EndUpdate;
  24208. var
  24209. NewSize: Integer;
  24210. begin
  24211. if FUpdateCount > 0 then
  24212. Dec(FUpdateCount);
  24213. if not (csDestroying in ComponentState) then
  24214. begin
  24215. if (FUpdateCount = 0) and (tsUpdating in FStates) then
  24216. begin
  24217. if tsUpdateHiddenChildrenNeeded in FStates then
  24218. begin
  24219. DetermineHiddenChildrenFlagAllNodes;
  24220. Exclude(FStates, tsUpdateHiddenChildrenNeeded);
  24221. end;
  24222. DoStateChange([], [tsUpdating]);
  24223. NewSize := PackArray(FSelection, FSelectionCount);
  24224. if NewSize > -1 then
  24225. begin
  24226. FSelectionCount := NewSize;
  24227. SetLength(FSelection, FSelectionCount);
  24228. end;
  24229. InvalidateCache;
  24230. ValidateCache;
  24231. if HandleAllocated then
  24232. UpdateScrollBars(False);
  24233. if tsStructureChangePending in FStates then
  24234. DoStructureChange(FLastStructureChangeNode, FLastStructureChangeReason);
  24235. try
  24236. if tsChangePending in FStates then
  24237. DoChange(FLastChangedNode);
  24238. finally
  24239. if toAutoSort in FOptions.FAutoOptions then
  24240. SortTree(FHeader.FSortColumn, FHeader.FSortDirection, True);
  24241. SetUpdateState(False);
  24242. if HandleAllocated then
  24243. Invalidate;
  24244. UpdateDesigner;
  24245. end;
  24246. end;
  24247. if FUpdateCount = 0 then begin
  24248. DoUpdating(usEnd);
  24249. EnsureNodeSelected();
  24250. end
  24251. else
  24252. DoUpdating(usUpdate);
  24253. end;
  24254. end;
  24255. //----------------------------------------------------------------------------------------------------------------------
  24256. function TBaseVirtualTree.ExecuteAction(Action: TBasicAction): Boolean;
  24257. // Some support for standard actions.
  24258. begin
  24259. Result := inherited ExecuteAction(Action);
  24260. if not Result then
  24261. begin
  24262. Result := Action is TEditSelectAll;
  24263. if Result then
  24264. SelectAll(False)
  24265. else
  24266. begin
  24267. Result := Action is TEditCopy;
  24268. if Result then
  24269. CopyToClipboard
  24270. else
  24271. if not (toReadOnly in FOptions.FMiscOptions) then
  24272. begin
  24273. Result := Action is TEditCut;
  24274. if Result then
  24275. CutToClipboard
  24276. else
  24277. begin
  24278. Result := Action is TEditPaste;
  24279. if Result then
  24280. PasteFromClipboard
  24281. else
  24282. begin
  24283. Result := Action is TEditDelete;
  24284. if Result then
  24285. DeleteSelectedNodes;
  24286. end;
  24287. end;
  24288. end;
  24289. end;
  24290. end;
  24291. end;
  24292. //----------------------------------------------------------------------------------------------------------------------
  24293. procedure TBaseVirtualTree.FinishCutOrCopy;
  24294. // Deletes nodes which are marked as being cutted.
  24295. var
  24296. Run: PVirtualNode;
  24297. begin
  24298. if tsCutPending in FStates then
  24299. begin
  24300. Run := FRoot.FirstChild;
  24301. while Assigned(Run) do
  24302. begin
  24303. if vsCutOrCopy in Run.States then
  24304. DeleteNode(Run);
  24305. Run := GetNextNoInit(Run);
  24306. end;
  24307. DoStateChange([], [tsCutPending]);
  24308. end;
  24309. end;
  24310. //----------------------------------------------------------------------------------------------------------------------
  24311. procedure TBaseVirtualTree.FlushClipboard;
  24312. // Used to render the data which is currently on the clipboard (finishes delayed rendering).
  24313. begin
  24314. if ClipboardStates * FStates <> [] then
  24315. begin
  24316. DoStateChange([tsClipboardFlushing]);
  24317. OleFlushClipboard;
  24318. CancelCutOrCopy;
  24319. DoStateChange([], [tsClipboardFlushing]);
  24320. end;
  24321. end;
  24322. //----------------------------------------------------------------------------------------------------------------------
  24323. procedure TBaseVirtualTree.FullCollapse(Node: PVirtualNode = nil);
  24324. // This routine collapses all expanded nodes in the subtree given by Node or the whole tree if Node is FRoot or nil.
  24325. // Only nodes which are expanded will be collapsed. This excludes uninitialized nodes but nodes marked as visible
  24326. // will still be collapsed if they are expanded.
  24327. var
  24328. Stop: PVirtualNode;
  24329. begin
  24330. if FRoot.TotalCount > 1 then
  24331. begin
  24332. if Node = FRoot then
  24333. Node := nil;
  24334. DoStateChange([tsCollapsing]);
  24335. BeginUpdate;
  24336. try
  24337. Stop := Node;
  24338. Node := GetLastVisibleNoInit(Node, True);
  24339. if Assigned(Node) then
  24340. begin
  24341. repeat
  24342. if [vsHasChildren, vsExpanded] * Node.States = [vsHasChildren, vsExpanded] then
  24343. ToggleNode(Node);
  24344. Node := GetPreviousNoInit(Node, True);
  24345. until (Node = Stop) or not Assigned(Node);
  24346. // Collapse the start node too.
  24347. if Assigned(Stop) and ([vsHasChildren, vsExpanded] * Stop.States = [vsHasChildren, vsExpanded]) then
  24348. ToggleNode(Stop);
  24349. end;
  24350. finally
  24351. EndUpdate;
  24352. DoStateChange([], [tsCollapsing]);
  24353. end;
  24354. end;
  24355. end;
  24356. //----------------------------------------------------------------------------------------------------------------------
  24357. procedure TBaseVirtualTree.FullExpand(Node: PVirtualNode = nil);
  24358. // This routine expands all collapsed nodes in the subtree given by Node or the whole tree if Node is FRoot or nil.
  24359. // All nodes on the way down are initialized so this procedure might take a long time.
  24360. // Since all nodes are validated, the tree cannot make use of optimatizations. Hence it is counter productive and you
  24361. // should consider avoiding its use.
  24362. var
  24363. Stop: PVirtualNode;
  24364. begin
  24365. if FRoot.TotalCount > 1 then
  24366. begin
  24367. DoStateChange([tsExpanding]);
  24368. BeginUpdate;
  24369. try
  24370. if Node = nil then
  24371. begin
  24372. Node := FRoot.FirstChild;
  24373. Stop := nil;
  24374. end
  24375. else
  24376. begin
  24377. Stop := Node.NextSibling;
  24378. if Stop = nil then
  24379. begin
  24380. Stop := Node;
  24381. repeat
  24382. Stop := Stop.Parent;
  24383. until (Stop = FRoot) or Assigned(Stop.NextSibling);
  24384. if Stop = FRoot then
  24385. Stop := nil
  24386. else
  24387. Stop := Stop.NextSibling;
  24388. end;
  24389. end;
  24390. // Initialize the start node. Others will be initialized in GetNext.
  24391. if not (vsInitialized in Node.States) then
  24392. InitNode(Node);
  24393. repeat
  24394. if not (vsExpanded in Node.States) then
  24395. ToggleNode(Node);
  24396. Node := GetNext(Node);
  24397. until Node = Stop;
  24398. finally
  24399. EndUpdate;
  24400. DoStateChange([], [tsExpanding]);
  24401. end;
  24402. end;
  24403. end;
  24404. //----------------------------------------------------------------------------------------------------------------------
  24405. function TBaseVirtualTree.GetControlsAlignment: TAlignment;
  24406. begin
  24407. Result := FAlignment;
  24408. end;
  24409. //----------------------------------------------------------------------------------------------------------------------
  24410. function TBaseVirtualTree.GetDisplayRect(Node: PVirtualNode; Column: TColumnIndex; TextOnly: Boolean;
  24411. Unclipped: Boolean = False; ApplyCellContentMargin: Boolean = False): TRect;
  24412. // Determines the client coordinates the given node covers, depending on scrolling, expand state etc.
  24413. // If the given node cannot be found (because one of its parents is collapsed or it is invisible) then an empty
  24414. // rectangle is returned.
  24415. // If TextOnly is True then only the text bounds are returned, that is, the resulting rectangle's left and right border
  24416. // are updated according to bidi mode, alignment and text width of the node.
  24417. // If Unclipped is True (which only makes sense if also TextOnly is True) then the calculated text rectangle is
  24418. // not clipped if the text does not entirely fit into the text space. This is special handling needed for hints.
  24419. // If ApplyCellContentMargin is True (which only makes sense if also TextOnly is True) then the calculated text
  24420. // rectangle respects the cell content margin.
  24421. // If Column is -1 then the entire client width is used before determining the node's width otherwise the bounds of the
  24422. // particular column are used.
  24423. // Note: Column must be a valid column and is used independent of whether the header is visible or not.
  24424. var
  24425. Temp: PVirtualNode;
  24426. Offset: Cardinal;
  24427. CacheIsAvailable: Boolean;
  24428. Indent,
  24429. TextWidth: Integer;
  24430. MainColumnHit: Boolean;
  24431. CurrentBidiMode: TBidiMode;
  24432. CurrentAlignment: TAlignment;
  24433. MaxUnclippedHeight: Integer;
  24434. TM: TTextMetric;
  24435. ExtraVerticalMargin: Integer;
  24436. begin
  24437. Assert(Assigned(Node), 'Node must not be nil.');
  24438. Assert(Node <> FRoot, 'Node must not be the hidden root node.');
  24439. MainColumnHit := (Column + 1) in [0, FHeader.MainColumn + 1];
  24440. if not (vsInitialized in Node.States) then
  24441. InitNode(Node);
  24442. Result := Rect(0, 0, 0, 0);
  24443. // Check whether the node is visible (determine indentation level btw.).
  24444. if not IsEffectivelyVisible[Node] then
  24445. Exit;
  24446. Temp := Node;
  24447. Indent := 0;
  24448. if not (toFixedIndent in FOptions.FPaintOptions) then
  24449. begin
  24450. while Temp <> FRoot do
  24451. begin
  24452. if not (vsVisible in Temp.States) or not (vsExpanded in Temp.Parent.States) then
  24453. Exit;
  24454. Temp := Temp.Parent;
  24455. if MainColumnHit and (Temp <> FRoot) then
  24456. Inc(Indent, FIndent);
  24457. end;
  24458. end;//if not toFixedIndent
  24459. // Here we know the node is visible.
  24460. Offset := 0;
  24461. CacheIsAvailable := False;
  24462. if tsUseCache in FStates then
  24463. begin
  24464. // If we can use the position cache then do a binary search to find a cached node which is as close as possible
  24465. // to the current node. Iterate then through all following and visible nodes and sum up their heights.
  24466. Temp := FindInPositionCache(Node, Offset);
  24467. CacheIsAvailable := Assigned(Temp);
  24468. while Assigned(Temp) and (Temp <> Node) do
  24469. begin
  24470. Inc(Offset, NodeHeight[Temp]);
  24471. Temp := GetNextVisibleNoInit(Temp, True);
  24472. end;
  24473. end;
  24474. if not CacheIsAvailable then
  24475. begin
  24476. // If the cache is not available then go straight through all nodes up to the root and sum up their heights.
  24477. Temp := Node;
  24478. repeat
  24479. Temp := GetPreviousVisibleNoInit(Temp, True);
  24480. if Temp = nil then
  24481. Break;
  24482. Inc(Offset, NodeHeight[Temp]);
  24483. until False;
  24484. end;
  24485. Result := Rect(0, Offset, Max(FRangeX, ClientWidth), Offset + NodeHeight[Node]);
  24486. // Limit left and right bounds to the given column (if any) and move bounds according to current scroll state.
  24487. if Column > NoColumn then
  24488. begin
  24489. FHeader.FColumns.GetColumnBounds(Column, Result.Left, Result.Right);
  24490. // The right column border is not part of this cell.
  24491. Dec(Result.Right);
  24492. OffsetRect(Result, 0, FOffsetY);
  24493. end
  24494. else
  24495. OffsetRect(Result, -FEffectiveOffsetX, FOffsetY);
  24496. // Limit left and right bounds further if only the text area is required.
  24497. if TextOnly then
  24498. begin
  24499. // Start with the offset of the text in the column and consider the indentation level too.
  24500. Offset := FMargin + Indent;
  24501. // If the text of a node is involved then we have to consider directionality and alignment too.
  24502. if Column <= NoColumn then
  24503. begin
  24504. CurrentBidiMode := BidiMode;
  24505. CurrentAlignment := Alignment;
  24506. end
  24507. else
  24508. begin
  24509. CurrentBidiMode := FHeader.FColumns[Column].BidiMode;
  24510. CurrentAlignment := FHeader.FColumns[Column].Alignment;
  24511. end;
  24512. if MainColumnHit then
  24513. begin
  24514. if toShowRoot in FOptions.FPaintOptions then
  24515. Inc(Offset, FIndent);
  24516. if (toCheckSupport in FOptions.FMiscOptions) and Assigned(FCheckImages) and (Node.CheckType <> ctNone) then
  24517. Inc(Offset, FCheckImages.Width + 2);
  24518. end;
  24519. // Consider associated images.
  24520. if Assigned(FStateImages) and HasImage(Node, ikState, Column) then
  24521. Inc(Offset, FStateImages.Width + 2);
  24522. if Assigned(FImages) and HasImage(Node, ikNormal, Column) then
  24523. Inc(Offset, GetNodeImageSize(Node).cx + 2);
  24524. // Offset contains now the distance from the left or right border of the rectangle (depending on bidi mode).
  24525. // Now consider the alignment too and calculate the final result.
  24526. if CurrentBidiMode = bdLeftToRight then
  24527. begin
  24528. Inc(Result.Left, Offset);
  24529. // Left-to-right reading does not need any special adjustment of the alignment.
  24530. end
  24531. else
  24532. begin
  24533. Dec(Result.Right, Offset);
  24534. // Consider bidi mode here. In RTL context does left alignment actually mean right alignment and vice versa.
  24535. ChangeBiDiModeAlignment(CurrentAlignment);
  24536. end;
  24537. TextWidth := DoGetNodeWidth(Node, Column);
  24538. // Keep cell height before applying cell content margin in order to increase cell height if text does not fit
  24539. // and Unclipped it true (see below).
  24540. MaxUnclippedHeight := Result.Bottom - Result.Top;
  24541. if ApplyCellContentMargin then
  24542. DoBeforeCellPaint(Self.Canvas, Node, Column, cpmGetContentMargin, Result, Result);
  24543. if Unclipped then
  24544. begin
  24545. // The caller requested the text coordinates unclipped. This means they must be calculated so as would
  24546. // there be enough space, regardless of column bounds etc.
  24547. // The layout still depends on the available space too, because this determines the position
  24548. // of the unclipped text rectangle.
  24549. if Result.Right - Result.Left < TextWidth - 1 then
  24550. if CurrentBidiMode = bdLeftToRight then
  24551. CurrentAlignment := taLeftJustify
  24552. else
  24553. CurrentAlignment := taRightJustify;
  24554. // Increase cell height (up to MaxUnclippedHeight determined above) if text does not fit.
  24555. GetTextMetrics(Self.Canvas.Handle, TM);
  24556. ExtraVerticalMargin := Math.Min(TM.tmHeight, MaxUnclippedHeight) - (Result.Bottom - Result.Top);
  24557. if ExtraVerticalMargin > 0 then
  24558. InflateRect(Result, 0, (ExtraVerticalMargin + 1) div 2);
  24559. case CurrentAlignment of
  24560. taCenter:
  24561. begin
  24562. Result.Left := (Result.Left + Result.Right - TextWidth) div 2;
  24563. Result.Right := Result.Left + TextWidth;
  24564. end;
  24565. taRightJustify:
  24566. Result.Left := Result.Right - TextWidth;
  24567. else // taLeftJustify
  24568. Result.Right := Result.Left + TextWidth - 1;
  24569. end;
  24570. end
  24571. else
  24572. // Modify rectangle only if the text fits entirely into the given room.
  24573. if Result.Right - Result.Left > TextWidth then
  24574. case CurrentAlignment of
  24575. taCenter:
  24576. begin
  24577. Result.Left := (Result.Left + Result.Right - TextWidth) div 2;
  24578. Result.Right := Result.Left + TextWidth;
  24579. end;
  24580. taRightJustify:
  24581. Result.Left := Result.Right - TextWidth;
  24582. else // taLeftJustify
  24583. Result.Right := Result.Left + TextWidth;
  24584. end;
  24585. end;
  24586. end;
  24587. //----------------------------------------------------------------------------------------------------------------------
  24588. function TBaseVirtualTree.GetEffectivelyFiltered(Node: PVirtualNode): Boolean;
  24589. // Checks if a node is effectively filtered out. This depends on the nodes state and the paint options.
  24590. begin
  24591. if Assigned(Node) then
  24592. Result := (vsFiltered in Node.States) and not (toShowFilteredNodes in FOptions.FPaintOptions)
  24593. else
  24594. Result := False;
  24595. end;
  24596. //----------------------------------------------------------------------------------------------------------------------
  24597. function TBaseVirtualTree.GetEffectivelyVisible(Node: PVirtualNode): Boolean;
  24598. begin
  24599. Result := (vsVisible in Node.States) and not IsEffectivelyFiltered[Node];
  24600. end;
  24601. //----------------------------------------------------------------------------------------------------------------------
  24602. function TBaseVirtualTree.GetFirst(ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  24603. // Returns the first node in the tree while optionally considering toChildrenAbove.
  24604. begin
  24605. if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then
  24606. begin
  24607. if vsHasChildren in FRoot.States then
  24608. begin
  24609. Result := FRoot;
  24610. // Child nodes are the first choice if possible.
  24611. if Assigned(Result.FirstChild) then
  24612. begin
  24613. while Assigned(Result.FirstChild) do
  24614. begin
  24615. Result := Result.FirstChild;
  24616. if not (vsInitialized in Result.States) then
  24617. InitNode(Result);
  24618. if (vsHasChildren in Result.States) and (Result.ChildCount = 0) then
  24619. InitChildren(Result);
  24620. end;
  24621. end
  24622. else
  24623. Result := nil;
  24624. end
  24625. else
  24626. Result := nil;
  24627. end
  24628. else
  24629. Result := FRoot.FirstChild;
  24630. if Assigned(Result) and not (vsInitialized in Result.States) then
  24631. InitNode(Result);
  24632. end;
  24633. //----------------------------------------------------------------------------------------------------------------------
  24634. function TBaseVirtualTree.GetFirstChecked(State: TCheckState = csCheckedNormal;
  24635. ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  24636. // Returns the first node in the tree with the given check state.
  24637. begin
  24638. Result := GetNextChecked(nil, State, ConsiderChildrenAbove);
  24639. end;
  24640. //----------------------------------------------------------------------------------------------------------------------
  24641. function TBaseVirtualTree.GetFirstChild(Node: PVirtualNode): PVirtualNode;
  24642. // Returns the first child of the given node. The result node is initialized before exit.
  24643. begin
  24644. if (Node = nil) or (Node = FRoot) then
  24645. Result := FRoot.FirstChild
  24646. else
  24647. begin
  24648. if not (vsInitialized in Node.States) then
  24649. InitNode(Node);
  24650. if vsHasChildren in Node.States then
  24651. begin
  24652. if Node.ChildCount = 0 then
  24653. InitChildren(Node);
  24654. Result := Node.FirstChild;
  24655. end
  24656. else
  24657. Result := nil;
  24658. end;
  24659. if Assigned(Result) and not (vsInitialized in Result.States) then
  24660. InitNode(Result);
  24661. end;
  24662. //----------------------------------------------------------------------------------------------------------------------
  24663. function TBaseVirtualTree.GetFirstChildNoInit(Node: PVirtualNode): PVirtualNode;
  24664. // Determines the first child of the given node but does not initialize it.
  24665. begin
  24666. if (Node = nil) or (Node = FRoot) then
  24667. Result := FRoot.FirstChild
  24668. else
  24669. begin
  24670. if vsHasChildren in Node.States then
  24671. Result := Node.FirstChild
  24672. else
  24673. Result := nil;
  24674. end;
  24675. end;
  24676. //----------------------------------------------------------------------------------------------------------------------
  24677. function TBaseVirtualTree.GetFirstCutCopy(ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  24678. // Returns the first node in the tree which is currently marked for a clipboard operation.
  24679. // See also GetNextCutCopy for comments on initialization.
  24680. begin
  24681. Result := GetNextCutCopy(nil, ConsiderChildrenAbove);
  24682. end;
  24683. //----------------------------------------------------------------------------------------------------------------------
  24684. function TBaseVirtualTree.GetFirstInitialized(ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  24685. // Returns the first node which is already initialized.
  24686. begin
  24687. Result := GetFirstNoInit(ConsiderChildrenAbove);
  24688. if Assigned(Result) and not (vsInitialized in Result.States) then
  24689. Result := GetNextInitialized(Result, ConsiderChildrenAbove);
  24690. end;
  24691. //----------------------------------------------------------------------------------------------------------------------
  24692. function TBaseVirtualTree.GetFirstLeaf: PVirtualNode;
  24693. // Returns the first node in the tree which has currently no children.
  24694. // The result is initialized if necessary.
  24695. begin
  24696. Result := GetNextLeaf(nil);
  24697. end;
  24698. //----------------------------------------------------------------------------------------------------------------------
  24699. function TBaseVirtualTree.GetFirstLevel(NodeLevel: Cardinal): PVirtualNode;
  24700. // Returns the first node in the tree on a specific level.
  24701. // The result is initialized if necessary.
  24702. begin
  24703. Result := GetFirstNoInit(True);
  24704. while Assigned(Result) and (GetNodeLevel(Result) <> NodeLevel) do
  24705. Result := GetNextNoInit(Result, True);
  24706. if Assigned(Result) and (GetNodeLevel(Result) <> NodeLevel) then // i.e. there is no node with the desired level in the tree
  24707. Result := nil;
  24708. if Assigned(Result) and not (vsInitialized in Result.States) then
  24709. InitNode(Result);
  24710. end;
  24711. //----------------------------------------------------------------------------------------------------------------------
  24712. function TBaseVirtualTree.GetFirstNoInit(ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  24713. // Returns the first node in the tree while optionally considering toChildrenAbove.
  24714. // No initialization is performed.
  24715. begin
  24716. if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then
  24717. begin
  24718. if vsHasChildren in FRoot.States then
  24719. begin
  24720. Result := FRoot;
  24721. // Child nodes are the first choice if possible.
  24722. if Assigned(Result.FirstChild) then
  24723. begin
  24724. while Assigned(Result.FirstChild) do
  24725. Result := Result.FirstChild;
  24726. end
  24727. else
  24728. Result := nil;
  24729. end
  24730. else
  24731. Result := nil;
  24732. end
  24733. else
  24734. Result := FRoot.FirstChild;
  24735. end;
  24736. //----------------------------------------------------------------------------------------------------------------------
  24737. function TBaseVirtualTree.GetFirstSelected(ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  24738. // Returns the first node in the current selection while optionally considering toChildrenAbove.
  24739. begin
  24740. Result := GetNextSelected(nil, ConsiderChildrenAbove);
  24741. end;
  24742. //----------------------------------------------------------------------------------------------------------------------
  24743. function TBaseVirtualTree.GetFirstVisible(Node: PVirtualNode = nil; ConsiderChildrenAbove: Boolean = True;
  24744. IncludeFiltered: Boolean = False): PVirtualNode;
  24745. // Returns the first visible node in the tree while optionally considering toChildrenAbove.
  24746. // If necessary nodes are initialized on demand.
  24747. begin
  24748. Result := Node;
  24749. if not Assigned(Result) then
  24750. Result := FRoot;
  24751. if vsHasChildren in Result.States then
  24752. begin
  24753. if Result.ChildCount = 0 then
  24754. InitChildren(Result);
  24755. // Child nodes are the first choice if possible.
  24756. if Assigned(Result.FirstChild) then
  24757. begin
  24758. Result := GetFirstChild(Result);
  24759. if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then
  24760. begin
  24761. repeat
  24762. // Search the first visible sibling.
  24763. while Assigned(Result.NextSibling) and not (vsVisible in Result.States) do
  24764. begin
  24765. Result := Result.NextSibling;
  24766. // Init node on demand as this might change the visibility.
  24767. if not (vsInitialized in Result.States) then
  24768. InitNode(Result);
  24769. end;
  24770. // If there are no visible siblings take the parent.
  24771. if not (vsVisible in Result.States) then
  24772. begin
  24773. Result := Result.Parent;
  24774. if Result = FRoot then
  24775. Result := nil;
  24776. Break;
  24777. end
  24778. else
  24779. begin
  24780. if (vsHasChildren in Result.States) and (Result.ChildCount = 0) then
  24781. InitChildren(Result);
  24782. if (not Assigned(Result.FirstChild)) or (not (vsExpanded in Result.States)) then
  24783. Break;
  24784. end;
  24785. Result := Result.FirstChild;
  24786. if not (vsInitialized in Result.States) then
  24787. InitNode(Result);
  24788. until False;
  24789. end
  24790. else
  24791. begin
  24792. // If there are no children or the first child is not visible then search the sibling nodes or traverse parents.
  24793. if not (vsVisible in Result.States) then
  24794. begin
  24795. repeat
  24796. // Is there a next sibling?
  24797. if Assigned(Result.NextSibling) then
  24798. begin
  24799. Result := Result.NextSibling;
  24800. // The visible state can be removed during initialization so init the node first.
  24801. if not (vsInitialized in Result.States) then
  24802. InitNode(Result);
  24803. if vsVisible in Result.States then
  24804. Break;
  24805. end
  24806. else
  24807. begin
  24808. // No sibling anymore, so use the parent's next sibling.
  24809. if Result.Parent <> FRoot then
  24810. Result := Result.Parent
  24811. else
  24812. begin
  24813. // There are no further nodes to examine, hence there is no further visible node.
  24814. Result := nil;
  24815. Break;
  24816. end;
  24817. end;
  24818. until False;
  24819. end;
  24820. end;
  24821. end
  24822. else
  24823. Result := nil;
  24824. end
  24825. else
  24826. Result := nil;
  24827. if Assigned(Result) and not IncludeFiltered and IsEffectivelyFiltered[Result] then
  24828. Result := GetNextVisible(Result);
  24829. end;
  24830. //----------------------------------------------------------------------------------------------------------------------
  24831. function TBaseVirtualTree.GetFirstVisibleChild(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode;
  24832. // Returns the first visible child node of Node. If necessary nodes are initialized on demand.
  24833. begin
  24834. if Node = nil then
  24835. Node := FRoot;
  24836. Result := GetFirstChild(Node);
  24837. if Assigned(Result) and (not (vsVisible in Result.States) or
  24838. (not IncludeFiltered and IsEffectivelyFiltered[Node])) then
  24839. Result := GetNextVisibleSibling(Result, IncludeFiltered);
  24840. end;
  24841. //----------------------------------------------------------------------------------------------------------------------
  24842. function TBaseVirtualTree.GetFirstVisibleChildNoInit(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode;
  24843. // Returns the first visible child node of Node.
  24844. begin
  24845. if Node = nil then
  24846. Node := FRoot;
  24847. Result := Node.FirstChild;
  24848. if Assigned(Result) and (not (vsVisible in Result.States) or
  24849. (not IncludeFiltered and IsEffectivelyFiltered[Node])) then
  24850. Result := GetNextVisibleSiblingNoInit(Result, IncludeFiltered);
  24851. end;
  24852. //----------------------------------------------------------------------------------------------------------------------
  24853. function TBaseVirtualTree.GetFirstVisibleNoInit(Node: PVirtualNode = nil;
  24854. ConsiderChildrenAbove: Boolean = True; IncludeFiltered: Boolean = False): PVirtualNode;
  24855. // Returns the first visible node in the tree or given subtree while optionally considering toChildrenAbove.
  24856. // No initialization is performed.
  24857. begin
  24858. Result := Node;
  24859. if not Assigned(Result) then
  24860. Result := FRoot;
  24861. if vsHasChildren in Result.States then
  24862. begin
  24863. // Child nodes are the first choice if possible.
  24864. if Assigned(Result.FirstChild) then
  24865. begin
  24866. Result := Result.FirstChild;
  24867. if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then
  24868. begin
  24869. repeat
  24870. // Search the first visible sibling.
  24871. while Assigned(Result.NextSibling) and not (vsVisible in Result.States) do
  24872. Result := Result.NextSibling;
  24873. // If there a no visible siblings take the parent.
  24874. if not (vsVisible in Result.States) then
  24875. begin
  24876. Result := Result.Parent;
  24877. if Result = FRoot then
  24878. Result := nil;
  24879. Break;
  24880. end
  24881. else
  24882. if (not Assigned(Result.FirstChild)) or (not (vsExpanded in Result.States))then
  24883. Break;
  24884. Result := Result.FirstChild;
  24885. until False;
  24886. end
  24887. else
  24888. begin
  24889. // If there are no children or the first child is not visible then search the sibling nodes or traverse parents.
  24890. if not (vsVisible in Result.States) then
  24891. begin
  24892. repeat
  24893. // Is there a next sibling?
  24894. if Assigned(Result.NextSibling) then
  24895. begin
  24896. Result := Result.NextSibling;
  24897. if vsVisible in Result.States then
  24898. Break;
  24899. end
  24900. else
  24901. begin
  24902. // No sibling anymore, so use the parent's next sibling.
  24903. if Result.Parent <> FRoot then
  24904. Result := Result.Parent
  24905. else
  24906. begin
  24907. // There are no further nodes to examine, hence there is no further visible node.
  24908. Result := nil;
  24909. Break;
  24910. end;
  24911. end;
  24912. until False;
  24913. end;
  24914. end;
  24915. end
  24916. else
  24917. Result := nil;
  24918. end
  24919. else
  24920. Result := nil;
  24921. if Assigned(Result) and not IncludeFiltered and IsEffectivelyFiltered[Result] then
  24922. Result := GetNextVisibleNoInit(Result);
  24923. end;
  24924. //----------------------------------------------------------------------------------------------------------------------
  24925. procedure TBaseVirtualTree.GetHitTestInfoAt(X, Y: Integer; Relative: Boolean; var HitInfo: THitInfo);
  24926. // Determines the node that occupies the specified point or nil if there's none. The parameter Relative determines
  24927. // whether to consider X and Y as being client coordinates (if True) or as being absolute tree coordinates.
  24928. // HitInfo is filled with flags describing the hit further.
  24929. var
  24930. ColLeft,
  24931. ColRight: Integer;
  24932. NodeTop: Integer;
  24933. InitialColumn,
  24934. NextColumn: TColumnIndex;
  24935. CurrentBidiMode: TBidiMode;
  24936. CurrentAlignment: TAlignment;
  24937. NodeRect: TRect;
  24938. begin
  24939. HitInfo.HitNode := nil;
  24940. HitInfo.HitPositions := [];
  24941. HitInfo.HitColumn := NoColumn;
  24942. // Determine if point lies in the tree's client area.
  24943. if X < 0 then
  24944. Include(HitInfo.HitPositions, hiToLeft)
  24945. else
  24946. if X > Max(FRangeX, ClientWidth) then
  24947. Include(HitInfo.HitPositions, hiToRight);
  24948. if Y < 0 then
  24949. Include(HitInfo.HitPositions, hiAbove)
  24950. else
  24951. if Y > Max(FRangeY, ClientHeight) then
  24952. Include(HitInfo.HitPositions, hiBelow);
  24953. // Convert position into absolute coordinate if necessary.
  24954. if Relative then
  24955. begin
  24956. if X >= Header.Columns.GetVisibleFixedWidth then
  24957. Inc(X, FEffectiveOffsetX);
  24958. Inc(Y, -FOffsetY);
  24959. end;
  24960. HitInfo.HitPoint.X := X;
  24961. HitInfo.HitPoint.Y := Y;
  24962. // If the point is in the tree area then check the nodes.
  24963. if HitInfo.HitPositions = [] then
  24964. begin
  24965. HitInfo.HitNode := GetNodeAt(X, Y, False, NodeTop);
  24966. if HitInfo.HitNode = nil then
  24967. Include(HitInfo.HitPositions, hiNowhere)
  24968. else
  24969. begin
  24970. // At this point we need some info about the node, so it must be initialized.
  24971. if not (vsInitialized in HitInfo.HitNode.States) then
  24972. InitNode(HitInfo.HitNode);
  24973. if FHeader.UseColumns then
  24974. begin
  24975. HitInfo.HitColumn := FHeader.Columns.GetColumnAndBounds(Point(X, Y), ColLeft, ColRight, False);
  24976. // If auto column spanning is enabled then look for the last non empty column.
  24977. if toAutoSpanColumns in FOptions.FAutoOptions then
  24978. begin
  24979. InitialColumn := HitInfo.HitColumn;
  24980. // Search to the left of the hit column for empty columns.
  24981. while (HitInfo.HitColumn > NoColumn) and ColumnIsEmpty(HitInfo.HitNode, HitInfo.HitColumn) do
  24982. begin
  24983. NextColumn := FHeader.FColumns.GetPreviousVisibleColumn(HitInfo.HitColumn);
  24984. if NextColumn = InvalidColumn then
  24985. Break;
  24986. HitInfo.HitColumn := NextColumn;
  24987. Dec(ColLeft, FHeader.FColumns[NextColumn].Width);
  24988. end;
  24989. // Search to the right of the hit column for empty columns.
  24990. repeat
  24991. InitialColumn := FHeader.FColumns.GetNextVisibleColumn(InitialColumn);
  24992. if (InitialColumn = InvalidColumn) or not ColumnIsEmpty(HitInfo.HitNode, InitialColumn) then
  24993. Break;
  24994. Inc(ColRight, FHeader.FColumns[InitialColumn].Width);
  24995. until False;
  24996. end;
  24997. // Make the X position and the right border relative to the start of the column.
  24998. Dec(X, ColLeft);
  24999. Dec(ColRight, ColLeft);
  25000. end
  25001. else
  25002. begin
  25003. HitInfo.HitColumn := NoColumn;
  25004. ColRight := Max(FRangeX, ClientWidth);
  25005. end;
  25006. ColLeft := 0;
  25007. if HitInfo.HitColumn = InvalidColumn then
  25008. Include(HitInfo.HitPositions, hiNowhere)
  25009. else
  25010. begin
  25011. // From now on X is in "column" coordinates (relative to the left column border).
  25012. HitInfo.HitPositions := [hiOnItem];
  25013. // Avoid getting the display rect if this is not necessary.
  25014. if toNodeHeightResize in FOptions.FMiscOptions then
  25015. begin
  25016. NodeRect := GetDisplayRect(HitInfo.HitNode, HitInfo.HitColumn, False);
  25017. if Y <= (NodeRect.Top - FOffsetY + 1) then
  25018. Include(HitInfo.HitPositions, hiUpperSplitter)
  25019. else
  25020. if Y >= (NodeRect.Bottom - FOffsetY - 3) then
  25021. Include(HitInfo.HitPositions, hiLowerSplitter);
  25022. end;
  25023. if HitInfo.HitColumn <= NoColumn then
  25024. begin
  25025. CurrentBidiMode := BidiMode;
  25026. CurrentAlignment := Alignment;
  25027. end
  25028. else
  25029. begin
  25030. CurrentBidiMode := FHeader.FColumns[HitInfo.HitColumn].BidiMode;
  25031. CurrentAlignment := FHeader.FColumns[HitInfo.HitColumn].Alignment;
  25032. end;
  25033. if CurrentBidiMode = bdLeftToRight then
  25034. DetermineHitPositionLTR(HitInfo, X, ColRight, CurrentAlignment)
  25035. else
  25036. DetermineHitPositionRTL(HitInfo, X, ColRight, CurrentAlignment);
  25037. end;
  25038. end;
  25039. end;
  25040. end;
  25041. //----------------------------------------------------------------------------------------------------------------------
  25042. function TBaseVirtualTree.GetLast(Node: PVirtualNode = nil; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  25043. // Returns the very last node in the tree branch given by Node and initializes the nodes all the way down including the
  25044. // result. toChildrenAbove is optionally considered. By using Node = nil the very last node in the tree is returned.
  25045. var
  25046. Next: PVirtualNode;
  25047. begin
  25048. Result := GetLastChild(Node);
  25049. if not ConsiderChildrenAbove or not (toChildrenAbove in FOptions.FPaintOptions) then
  25050. while Assigned(Result) do
  25051. begin
  25052. // Test if there is a next last child. If not keep the node from the last run.
  25053. // Otherwise use the next last child.
  25054. Next := GetLastChild(Result);
  25055. if Next = nil then
  25056. Break;
  25057. Result := Next;
  25058. end;
  25059. end;
  25060. //----------------------------------------------------------------------------------------------------------------------
  25061. function TBaseVirtualTree.GetLastInitialized(Node: PVirtualNode = nil;
  25062. ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  25063. // Returns the very last initialized child node in the tree branch given by Node.
  25064. begin
  25065. Result := GetLastNoInit(Node, ConsiderChildrenAbove);
  25066. if Assigned(Result) and not (vsInitialized in Result.States) then
  25067. Result := GetPreviousInitialized(Result, ConsiderChildrenAbove);
  25068. end;
  25069. //----------------------------------------------------------------------------------------------------------------------
  25070. function TBaseVirtualTree.GetLastNoInit(Node: PVirtualNode = nil; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  25071. // Returns the very last node in the tree branch given by Node without initialization.
  25072. var
  25073. Next: PVirtualNode;
  25074. begin
  25075. Result := GetLastChildNoInit(Node);
  25076. if not ConsiderChildrenAbove or not (toChildrenAbove in FOptions.FPaintOptions) then
  25077. while Assigned(Result) do
  25078. begin
  25079. // Test if there is a next last child. If not keep the node from the last run.
  25080. // Otherwise use the next last child.
  25081. Next := GetLastChildNoInit(Result);
  25082. if Next = nil then
  25083. Break;
  25084. Result := Next;
  25085. end;
  25086. end;
  25087. //----------------------------------------------------------------------------------------------------------------------
  25088. function TBaseVirtualTree.GetLastChild(Node: PVirtualNode): PVirtualNode;
  25089. // Determines the last child of the given node and initializes it if there is one.
  25090. begin
  25091. if (Node = nil) or (Node = FRoot) then
  25092. Result := FRoot.LastChild
  25093. else
  25094. begin
  25095. if not (vsInitialized in Node.States) then
  25096. InitNode(Node);
  25097. if vsHasChildren in Node.States then
  25098. begin
  25099. if Node.ChildCount = 0 then
  25100. InitChildren(Node);
  25101. Result := Node.LastChild;
  25102. end
  25103. else
  25104. Result := nil;
  25105. end;
  25106. if Assigned(Result) and not (vsInitialized in Result.States) then
  25107. InitNode(Result);
  25108. end;
  25109. //----------------------------------------------------------------------------------------------------------------------
  25110. function TBaseVirtualTree.GetLastChildNoInit(Node: PVirtualNode): PVirtualNode;
  25111. // Determines the last child of the given node but does not initialize it.
  25112. begin
  25113. if (Node = nil) or (Node = FRoot) then
  25114. Result := FRoot.LastChild
  25115. else
  25116. begin
  25117. if vsHasChildren in Node.States then
  25118. Result := Node.LastChild
  25119. else
  25120. Result := nil;
  25121. end;
  25122. end;
  25123. //----------------------------------------------------------------------------------------------------------------------
  25124. function TBaseVirtualTree.GetLastVisible(Node: PVirtualNode = nil; ConsiderChildrenAbove: Boolean = True;
  25125. IncludeFiltered: Boolean = False): PVirtualNode;
  25126. // Returns the very last visible node in the tree while optionally considering toChildrenAbove.
  25127. // The nodes are intialized all the way up including the result node.
  25128. var
  25129. Run: PVirtualNode;
  25130. begin
  25131. Result := GetLastVisibleNoInit(Node, ConsiderChildrenAbove);
  25132. Run := Result;
  25133. while Assigned(Run) and (Run <> Node) and (Run <> RootNode) do
  25134. begin
  25135. if not (vsInitialized in Run.States) then
  25136. InitNode(Run);
  25137. Run := Run.Parent;
  25138. end;
  25139. end;
  25140. //----------------------------------------------------------------------------------------------------------------------
  25141. function TBaseVirtualTree.GetLastVisibleChild(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode;
  25142. // Determines the last visible child of the given node and initializes it if necessary.
  25143. begin
  25144. if (Node = nil) or (Node = FRoot) then
  25145. Result := GetLastChild(FRoot)
  25146. else
  25147. if FullyVisible[Node] and (vsExpanded in Node.States) then
  25148. Result := GetLastChild(Node)
  25149. else
  25150. Result := nil;
  25151. if Assigned(Result) and (not (vsVisible in Result.States) or
  25152. (not IncludeFiltered and IsEffectivelyFiltered[Node])) then
  25153. Result := GetPreviousVisibleSibling(Result, IncludeFiltered);
  25154. if Assigned(Result) and not (vsInitialized in Result.States) then
  25155. InitNode(Result);
  25156. end;
  25157. //----------------------------------------------------------------------------------------------------------------------
  25158. function TBaseVirtualTree.GetLastVisibleChildNoInit(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode;
  25159. // Determines the last visible child of the given node without initialization.
  25160. begin
  25161. if (Node = nil) or (Node = FRoot) then
  25162. Result := GetLastChildNoInit(FRoot)
  25163. else
  25164. if FullyVisible[Node] and (vsExpanded in Node.States) then
  25165. Result := GetLastChildNoInit(Node)
  25166. else
  25167. Result := nil;
  25168. if Assigned(Result) and (not (vsVisible in Result.States) or
  25169. (not IncludeFiltered and IsEffectivelyFiltered[Node])) then
  25170. Result := GetPreviousVisibleSiblingNoInit(Result, IncludeFiltered);
  25171. end;
  25172. //----------------------------------------------------------------------------------------------------------------------
  25173. function TBaseVirtualTree.GetLastVisibleNoInit(Node: PVirtualNode = nil;
  25174. ConsiderChildrenAbove: Boolean = True; IncludeFiltered: Boolean = False): PVirtualNode;
  25175. // Returns the very last visible node in the tree while optionally considering toChildrenAbove.
  25176. // No initialization is performed.
  25177. begin
  25178. Result := GetLastNoInit(Node, ConsiderChildrenAbove);
  25179. while Assigned(Result) and (Result <> Node) do
  25180. begin
  25181. if FullyVisible[Result] and
  25182. (IncludeFiltered or not IsEffectivelyFiltered[Result]) then
  25183. Break;
  25184. Result := GetPreviousNoInit(Result, ConsiderChildrenAbove);
  25185. end;
  25186. if (Result = Node) then // i.e. there is no visible node
  25187. Result := nil;
  25188. end;
  25189. //----------------------------------------------------------------------------------------------------------------------
  25190. function TBaseVirtualTree.GetMaxColumnWidth(Column: TColumnIndex; UseSmartColumnWidth: Boolean = False): Integer;
  25191. // This method determines the width of the largest node in the given column.
  25192. // If UseSmartColumnWidth is True then only the visible nodes which are in view will be considered
  25193. // Note: If UseSmartColumnWidth is False then every visible node in the tree will be initialized contradicting so
  25194. // the virtual paradigm.
  25195. var
  25196. Run,
  25197. LastNode,
  25198. NextNode: PVirtualNode;
  25199. NodeLeft,
  25200. TextLeft,
  25201. CurrentWidth: Integer;
  25202. AssumeImage: Boolean;
  25203. WithCheck,
  25204. WithStateImages: Boolean;
  25205. CheckOffset,
  25206. StateImageOffset: Integer;
  25207. begin
  25208. if OperationCanceled then
  25209. begin
  25210. // Behave non-destructive.
  25211. Result := FHeader.FColumns[Column].Width;
  25212. Exit;
  25213. end
  25214. else
  25215. Result := 0;
  25216. StartOperation(okGetMaxColumnWidth);
  25217. try
  25218. if Assigned(FOnBeforeGetMaxColumnWidth) then
  25219. FOnBeforeGetMaxColumnWidth(FHeader, Column, UseSmartColumnWidth);
  25220. WithStateImages := Assigned(FStateImages);
  25221. if WithStateImages then
  25222. StateImageOffset := FStateImages.Width + 2
  25223. else
  25224. StateImageOffset := 0;
  25225. if Assigned(FCheckImages) then
  25226. CheckOffset := FCheckImages.Width + 2
  25227. else
  25228. CheckOffset := 0;
  25229. if UseSmartColumnWidth then // Get first visible node which is in view.
  25230. Run := GetTopNode
  25231. else
  25232. Run := GetFirstVisible(nil, True);
  25233. if Column = FHeader.MainColumn then
  25234. begin
  25235. if toFixedIndent in FOptions.FPaintOptions then
  25236. NodeLeft := FIndent
  25237. else
  25238. begin
  25239. if toShowRoot in FOptions.FPaintOptions then
  25240. NodeLeft := Integer((GetNodeLevel(Run) + 1) * FIndent)
  25241. else
  25242. NodeLeft := Integer(GetNodeLevel(Run) * FIndent);
  25243. end;
  25244. WithCheck := (toCheckSupport in FOptions.FMiscOptions) and Assigned(FCheckImages);
  25245. end
  25246. else
  25247. begin
  25248. NodeLeft := 0;
  25249. WithCheck := False;
  25250. end;
  25251. // Consider node margin at the left of the nodes.
  25252. Inc(NodeLeft, FMargin);
  25253. // Decide where to stop.
  25254. if UseSmartColumnWidth then
  25255. LastNode := GetNextVisible(BottomNode)
  25256. else
  25257. LastNode := nil;
  25258. AssumeImage := False;
  25259. while Assigned(Run) and not OperationCanceled do
  25260. begin
  25261. TextLeft := NodeLeft;
  25262. if WithCheck and (Run.CheckType <> ctNone) then
  25263. Inc(TextLeft, CheckOffset);
  25264. if Assigned(FImages) and (AssumeImage or HasImage(Run, ikNormal, Column)) then
  25265. begin
  25266. TextLeft := TextLeft + GetNodeImageSize(Run).cx + 2;
  25267. AssumeImage := True;// From now on, assume that the nodes do ave an image
  25268. end;
  25269. if WithStateImages and HasImage(Run, ikState, Column) then
  25270. Inc(TextLeft, StateImageOffset);
  25271. CurrentWidth := DoGetNodeWidth(Run, Column);
  25272. Inc(CurrentWidth, DoGetNodeExtraWidth(Run, Column));
  25273. Inc(CurrentWidth, DoGetCellContentMargin(Run, Column).X);
  25274. if Result < (TextLeft + CurrentWidth) then
  25275. Result := TextLeft + CurrentWidth;
  25276. // Get next visible node and update left node position if needed.
  25277. NextNode := GetNextVisible(Run, True);
  25278. if NextNode = LastNode then
  25279. Break;
  25280. if (Column = Header.MainColumn) and not (toFixedIndent in FOptions.FPaintOptions) then
  25281. Inc(NodeLeft, CountLevelDifference(Run, NextNode) * Integer(FIndent));
  25282. Run := NextNode;
  25283. end;
  25284. if toShowVertGridLines in FOptions.FPaintOptions then
  25285. Inc(Result);
  25286. if Assigned(FOnAfterGetMaxColumnWidth) then
  25287. FOnAfterGetMaxColumnWidth(FHeader, Column, Result);
  25288. finally
  25289. EndOperation(okGetMaxColumnWidth);
  25290. end;
  25291. end;
  25292. //----------------------------------------------------------------------------------------------------------------------
  25293. function TBaseVirtualTree.GetNext(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  25294. // Returns next node in tree while optionally considering toChildrenAbove. The Result will be initialized if needed.
  25295. begin
  25296. Result := Node;
  25297. if Assigned(Result) then
  25298. begin
  25299. Assert(Result <> FRoot, 'Node must not be the hidden root node.');
  25300. if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then
  25301. begin
  25302. // If this node has no siblings use the parent.
  25303. if not Assigned(Result.NextSibling) then
  25304. begin
  25305. Result := Result.Parent;
  25306. if Result = FRoot then
  25307. begin
  25308. Result := nil;
  25309. end;
  25310. end
  25311. else
  25312. begin
  25313. // There is at least one sibling so take it.
  25314. Result := Result.NextSibling;
  25315. // Has this node got children? Initialize them if necessary.
  25316. if (vsHasChildren in Result.States) and (Result.ChildCount = 0) then
  25317. InitChildren(Result);
  25318. // Now take a look at the children.
  25319. while Assigned(Result.FirstChild) do
  25320. begin
  25321. Result := Result.FirstChild;
  25322. if (vsHasChildren in Result.States) and (Result.ChildCount = 0) then
  25323. InitChildren(Result);
  25324. end;
  25325. end;
  25326. end
  25327. else
  25328. begin
  25329. // Has this node got children?
  25330. if vsHasChildren in Result.States then
  25331. begin
  25332. // Yes, there are child nodes. Initialize them if necessary.
  25333. if Result.ChildCount = 0 then
  25334. InitChildren(Result);
  25335. end;
  25336. // if there is no child node try siblings
  25337. if Assigned(Result.FirstChild) then
  25338. Result := Result.FirstChild
  25339. else
  25340. begin
  25341. repeat
  25342. // Is there a next sibling?
  25343. if Assigned(Result.NextSibling) then
  25344. begin
  25345. Result := Result.NextSibling;
  25346. Break;
  25347. end
  25348. else
  25349. begin
  25350. // No sibling anymore, so use the parent's next sibling.
  25351. if Result.Parent <> FRoot then
  25352. Result := Result.Parent
  25353. else
  25354. begin
  25355. // There are no further nodes to examine, hence there is no further visible node.
  25356. Result := nil;
  25357. Break;
  25358. end;
  25359. end;
  25360. until False;
  25361. end;
  25362. end;
  25363. end;
  25364. if Assigned(Result) and not (vsInitialized in Result.States) then
  25365. InitNode(Result);
  25366. end;
  25367. //----------------------------------------------------------------------------------------------------------------------
  25368. function TBaseVirtualTree.GetNextChecked(Node: PVirtualNode; State: TCheckState = csCheckedNormal;
  25369. ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  25370. begin
  25371. if (Node = nil) or (Node = FRoot) then
  25372. Result := GetFirstNoInit(ConsiderChildrenAbove)
  25373. else
  25374. Result := GetNextNoInit(Node, ConsiderChildrenAbove);
  25375. while Assigned(Result) and (Result.CheckState <> State) do
  25376. Result := GetNextNoInit(Result, ConsiderChildrenAbove);
  25377. if Assigned(Result) and not (vsInitialized in Result.States) then
  25378. InitNode(Result);
  25379. end;
  25380. //----------------------------------------------------------------------------------------------------------------------
  25381. function TBaseVirtualTree.GetNextChecked(Node: PVirtualNode; ConsiderChildrenAbove: Boolean): PVirtualNode;
  25382. begin
  25383. Result := Self.GetNextChecked(Node, csCheckedNormal, ConsiderChildrenAbove);
  25384. end;
  25385. //----------------------------------------------------------------------------------------------------------------------
  25386. function TBaseVirtualTree.GetNextCutCopy(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  25387. // Returns the next node in the tree which is currently marked for a clipboard operation. Since only visible nodes can
  25388. // be marked (or they are hidden after they have been marked) it is not necessary to initialize nodes to check for
  25389. // child nodes. The result, however, is initialized if necessary.
  25390. begin
  25391. if ClipboardStates * FStates <> [] then
  25392. begin
  25393. if (Node = nil) or (Node = FRoot) then
  25394. Result := GetFirstNoInit(ConsiderChildrenAbove)
  25395. else
  25396. Result := GetNextNoInit(Node, ConsiderChildrenAbove);
  25397. while Assigned(Result) and not (vsCutOrCopy in Result.States) do
  25398. Result := GetNextNoInit(Result, ConsiderChildrenAbove);
  25399. if Assigned(Result) and not (vsInitialized in Result.States) then
  25400. InitNode(Result);
  25401. end
  25402. else
  25403. Result := nil;
  25404. end;
  25405. //----------------------------------------------------------------------------------------------------------------------
  25406. function TBaseVirtualTree.GetNextInitialized(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  25407. // Returns the next node in tree which is initialized.
  25408. begin
  25409. Result := Node;
  25410. repeat
  25411. Result := GetNextNoInit(Result, ConsiderChildrenAbove);
  25412. until (Result = nil) or (vsInitialized in Result.States);
  25413. end;
  25414. //----------------------------------------------------------------------------------------------------------------------
  25415. function TBaseVirtualTree.GetNextLeaf(Node: PVirtualNode): PVirtualNode;
  25416. // Returns the next node in the tree which has currently no children.
  25417. // The result is initialized if necessary.
  25418. begin
  25419. if (Node = nil) or (Node = FRoot) then
  25420. Result := FRoot.FirstChild
  25421. else
  25422. Result := GetNext(Node);
  25423. while Assigned(Result) and (vsHasChildren in Result.States) do
  25424. Result := GetNext(Result);
  25425. if Assigned(Result) and not (vsInitialized in Result.States) then
  25426. InitNode(Result);
  25427. end;
  25428. //----------------------------------------------------------------------------------------------------------------------
  25429. function TBaseVirtualTree.GetNextLevel(Node: PVirtualNode; NodeLevel: Cardinal): PVirtualNode;
  25430. // Returns the next node in the tree on a specific level.
  25431. // The result is initialized if necessary.
  25432. var
  25433. StartNodeLevel: Cardinal;
  25434. begin
  25435. Result := nil;
  25436. if Assigned(Node) and (Node <> FRoot) then
  25437. begin
  25438. StartNodeLevel := GetNodeLevel(Node);
  25439. if StartNodeLevel < NodeLevel then
  25440. begin
  25441. Result := GetNext(Node);
  25442. if Assigned(Result) and (GetNodeLevel(Result) <> NodeLevel) then
  25443. Result := GetNextLevel(Result, NodeLevel);
  25444. end
  25445. else
  25446. if StartNodeLevel = NodeLevel then
  25447. begin
  25448. Result := Node.NextSibling;
  25449. if not Assigned(Result) then // i.e. start node was a last sibling
  25450. begin
  25451. Result := Node.Parent;
  25452. if Assigned(Result) then
  25453. begin
  25454. // go to next anchestor of the start node which has a next sibling (if exists)
  25455. while Assigned(Result) and not Assigned(Result.NextSibling) do
  25456. Result := Result.Parent;
  25457. if Assigned(Result) then
  25458. Result := GetNextLevel(Result.NextSibling, NodeLevel);
  25459. end;
  25460. end;
  25461. end
  25462. else
  25463. // i.e. StartNodeLevel > NodeLevel
  25464. Result := GetNextLevel(Node.Parent, NodeLevel);
  25465. end;
  25466. if Assigned(Result) and not (vsInitialized in Result.States) then
  25467. InitNode(Result);
  25468. end;
  25469. //----------------------------------------------------------------------------------------------------------------------
  25470. function TBaseVirtualTree.GetNextNoInit(Node: PVirtualNode; ConsiderChildrenAbove: Boolean): PVirtualNode;
  25471. // Optimized version of GetNext performing no initialization, but optionally considering toChildrenAbove.
  25472. begin
  25473. Result := Node;
  25474. if Assigned(Result) then
  25475. begin
  25476. Assert(Result <> FRoot, 'Node must not be the hidden root node.');
  25477. if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then
  25478. begin
  25479. // If this node has no siblings use the parent.
  25480. if not Assigned(Result.NextSibling) then
  25481. begin
  25482. Result := Result.Parent;
  25483. if Result = FRoot then
  25484. begin
  25485. Result := nil;
  25486. end;
  25487. end
  25488. else
  25489. begin
  25490. // There is at least one sibling so take it.
  25491. Result := Result.NextSibling;
  25492. // Now take a look at the children.
  25493. while Assigned(Result.FirstChild) do
  25494. begin
  25495. Result := Result.FirstChild;
  25496. end;
  25497. end;
  25498. end
  25499. else
  25500. begin
  25501. // If there is no child node try siblings.
  25502. if Assigned(Result.FirstChild) then
  25503. Result := Result.FirstChild
  25504. else
  25505. begin
  25506. repeat
  25507. // Is there a next sibling?
  25508. if Assigned(Result.NextSibling) then
  25509. begin
  25510. Result := Result.NextSibling;
  25511. Break;
  25512. end
  25513. else
  25514. begin
  25515. // No sibling anymore, so use the parent's next sibling.
  25516. if Result.Parent <> FRoot then
  25517. Result := Result.Parent
  25518. else
  25519. begin
  25520. // There are no further nodes to examine, hence there is no further visible node.
  25521. Result := nil;
  25522. Break;
  25523. end;
  25524. end;
  25525. until False;
  25526. end;
  25527. end;
  25528. end;
  25529. end;
  25530. //----------------------------------------------------------------------------------------------------------------------
  25531. function TBaseVirtualTree.GetNextSelected(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  25532. // Returns the next node in the tree which is currently selected. Since children of unitialized nodes cannot be
  25533. // in the current selection (because they simply do not exist yet) it is not necessary to initialize nodes here.
  25534. // The result however is initialized if necessary.
  25535. begin
  25536. if FSelectionCount > 0 then
  25537. begin
  25538. if (Node = nil) or (Node = FRoot) then
  25539. Result := GetFirstNoInit(ConsiderChildrenAbove)
  25540. else
  25541. Result := GetNextNoInit(Node, ConsiderChildrenAbove);
  25542. while Assigned(Result) and not (vsSelected in Result.States) do
  25543. Result := GetNextNoInit(Result, ConsiderChildrenAbove);
  25544. if Assigned(Result) and not (vsInitialized in Result.States) then
  25545. InitNode(Result);
  25546. end
  25547. else
  25548. Result := nil;
  25549. end;
  25550. //----------------------------------------------------------------------------------------------------------------------
  25551. function TBaseVirtualTree.GetNextSibling(Node: PVirtualNode): PVirtualNode;
  25552. // Returns the next sibling of Node and initializes it if necessary.
  25553. begin
  25554. Result := Node;
  25555. if Assigned(Result) then
  25556. begin
  25557. Assert(Result <> FRoot, 'Node must not be the hidden root node.');
  25558. Result := Result.NextSibling;
  25559. if Assigned(Result) and not (vsInitialized in Result.States) then
  25560. InitNode(Result);
  25561. end;
  25562. end;
  25563. function TBaseVirtualTree.GetNextSiblingNoInit(Node: PVirtualNode): PVirtualNode;
  25564. // Returns the next sibling of Node.
  25565. begin
  25566. Result := Node;
  25567. if Assigned(Result) then
  25568. begin
  25569. Assert(Result <> FRoot, 'Node must not be the hidden root node.');
  25570. Result := Result.NextSibling;
  25571. end;
  25572. end;
  25573. //----------------------------------------------------------------------------------------------------------------------
  25574. function TBaseVirtualTree.GetNextVisible(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = True): PVirtualNode;
  25575. // Returns next node in tree, with regard to Node, which is visible.
  25576. // Nodes which need an initialization (including the result) are initialized.
  25577. // toChildrenAbove is optionally considered which is the default here.
  25578. var
  25579. ForceSearch: Boolean;
  25580. begin
  25581. Result := Node;
  25582. if Assigned(Result) then
  25583. begin
  25584. Assert(Result <> FRoot, 'Node must not be the hidden root node.');
  25585. repeat
  25586. // If the given node is not visible then look for a parent node which is visible, otherwise we will
  25587. // likely go unnecessarily through a whole bunch of invisible nodes.
  25588. if not FullyVisible[Result] then
  25589. Result := GetVisibleParent(Result, True);
  25590. if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then
  25591. begin
  25592. repeat
  25593. // If there a no siblings anymore, go up one level.
  25594. if not Assigned(Result.NextSibling) then
  25595. begin
  25596. Result := Result.Parent;
  25597. if Result = FRoot then
  25598. begin
  25599. Result := nil;
  25600. Break;
  25601. end;
  25602. if not (vsInitialized in Result.States) then
  25603. InitNode(Result);
  25604. if vsVisible in Result.States then
  25605. Break;
  25606. end
  25607. else
  25608. begin
  25609. // There is at least one sibling so take it.
  25610. Result := Result.NextSibling;
  25611. if not (vsInitialized in Result.States) then
  25612. InitNode(Result);
  25613. if not (vsVisible in Result.States) then
  25614. Continue;
  25615. // Now take a look at the children.
  25616. // As the children are initialized while toggling, we don't need to do this here.
  25617. while (vsExpanded in Result.States) and Assigned(Result.FirstChild) do
  25618. begin
  25619. Result := Result.FirstChild;
  25620. if not (vsInitialized in Result.States) then
  25621. InitNode(Result);
  25622. if not (vsVisible in Result.States) then
  25623. Break;
  25624. end;
  25625. // If we found a visible node we don't need to search any longer.
  25626. if vsVisible in Result.States then
  25627. Break;
  25628. end;
  25629. until False;
  25630. end
  25631. else
  25632. begin
  25633. // Has this node got children?
  25634. if [vsHasChildren, vsExpanded] * Result.States = [vsHasChildren, vsExpanded] then
  25635. begin
  25636. // Yes, there are child nodes. Initialize them if necessary.
  25637. if Result.ChildCount = 0 then
  25638. InitChildren(Result);
  25639. end;
  25640. // Child nodes are the first choice if possible.
  25641. if (vsExpanded in Result.States) and Assigned(Result.FirstChild) then
  25642. begin
  25643. Result := GetFirstChild(Result);
  25644. ForceSearch := False;
  25645. end
  25646. else
  25647. ForceSearch := True;
  25648. // If there are no children or the first child is not visible then search the sibling nodes or traverse parents.
  25649. if Assigned(Result) and (ForceSearch or not (vsVisible in Result.States)) then
  25650. begin
  25651. repeat
  25652. // Is there a next sibling?
  25653. if Assigned(Result.NextSibling) then
  25654. begin
  25655. Result := Result.NextSibling;
  25656. if not (vsInitialized in Result.States) then
  25657. InitNode(Result);
  25658. if vsVisible in Result.States then
  25659. Break;
  25660. end
  25661. else
  25662. begin
  25663. // No sibling anymore, so use the parent's next sibling.
  25664. if Result.Parent <> FRoot then
  25665. Result := Result.Parent
  25666. else
  25667. begin
  25668. // There are no further nodes to examine, hence there is no further visible node.
  25669. Result := nil;
  25670. Break;
  25671. end;
  25672. end;
  25673. until False;
  25674. end;
  25675. end;
  25676. until not Assigned(Result) or IsEffectivelyVisible[Result];
  25677. end;
  25678. end;
  25679. //----------------------------------------------------------------------------------------------------------------------
  25680. function TBaseVirtualTree.GetNextVisibleNoInit(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = True): PVirtualNode;
  25681. // Returns the next node in tree, with regard to Node, which is visible.
  25682. // toChildrenAbove is optionally considered (which is the default). No initialization is done.
  25683. var
  25684. ForceSearch: Boolean;
  25685. begin
  25686. Result := Node;
  25687. if Assigned(Result) then
  25688. begin
  25689. Assert(Result <> FRoot, 'Node must not be the hidden root node.');
  25690. repeat
  25691. if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then
  25692. begin
  25693. repeat
  25694. // If there a no siblings anymore, go up one level.
  25695. if not Assigned(Result.NextSibling) then
  25696. begin
  25697. Result := Result.Parent;
  25698. if Result = FRoot then
  25699. begin
  25700. Result := nil;
  25701. Break;
  25702. end;
  25703. if vsVisible in Result.States then
  25704. Break;
  25705. end
  25706. else
  25707. begin
  25708. // There is at least one sibling so take it.
  25709. Result := Result.NextSibling;
  25710. if not (vsVisible in Result.States) then
  25711. Continue;
  25712. // Now take a look at the children.
  25713. while (vsExpanded in Result.States) and Assigned(Result.FirstChild) do
  25714. begin
  25715. Result := Result.FirstChild;
  25716. if not (vsVisible in Result.States) then
  25717. Break;
  25718. end;
  25719. // If we found a visible node we don't need to search any longer.
  25720. if vsVisible in Result.States then
  25721. Break;
  25722. end;
  25723. until False;
  25724. end
  25725. else
  25726. begin
  25727. // If the given node is not visible then look for a parent node which is visible, otherwise we will
  25728. // likely go unnecessarily through a whole bunch of invisible nodes.
  25729. if not FullyVisible[Result] then
  25730. Result := GetVisibleParent(Result, True);
  25731. // Child nodes are the first choice if possible.
  25732. if (vsExpanded in Result.States) and Assigned(Result.FirstChild) then
  25733. begin
  25734. Result := Result.FirstChild;
  25735. ForceSearch := False;
  25736. end
  25737. else
  25738. ForceSearch := True;
  25739. // If there are no children or the first child is not visible then search the sibling nodes or traverse parents.
  25740. if ForceSearch or not (vsVisible in Result.States) then
  25741. begin
  25742. repeat
  25743. // Is there a next sibling?
  25744. if Assigned(Result.NextSibling) then
  25745. begin
  25746. Result := Result.NextSibling;
  25747. if vsVisible in Result.States then
  25748. Break;
  25749. end
  25750. else
  25751. begin
  25752. // No sibling anymore, so use the parent's next sibling.
  25753. if Result.Parent <> FRoot then
  25754. Result := Result.Parent
  25755. else
  25756. begin
  25757. // There are no further nodes to examine, hence there is no further visible node.
  25758. Result := nil;
  25759. Break;
  25760. end;
  25761. end;
  25762. until False;
  25763. end;
  25764. end;
  25765. until not Assigned(Result) or IsEffectivelyVisible[Result];
  25766. end;
  25767. end;
  25768. //----------------------------------------------------------------------------------------------------------------------
  25769. function TBaseVirtualTree.GetNextVisibleSibling(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode;
  25770. // Returns the next visible sibling after Node. Initialization is done implicitly.
  25771. begin
  25772. Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameter.');
  25773. Result := Node;
  25774. repeat
  25775. Result := GetNextSibling(Result);
  25776. until not Assigned(Result) or ((vsVisible in Result.States) and
  25777. (IncludeFiltered or not IsEffectivelyFiltered[Result]));
  25778. end;
  25779. //----------------------------------------------------------------------------------------------------------------------
  25780. function TBaseVirtualTree.GetNextVisibleSiblingNoInit(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode;
  25781. // Returns the next visible sibling after Node.
  25782. begin
  25783. Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameter.');
  25784. Result := Node;
  25785. repeat
  25786. Result := Result.NextSibling;
  25787. until not Assigned(Result) or ((vsVisible in Result.States) and
  25788. (IncludeFiltered or not IsEffectivelyFiltered[Result]));
  25789. end;
  25790. //----------------------------------------------------------------------------------------------------------------------
  25791. function TBaseVirtualTree.GetNodeAt(X, Y: Integer): PVirtualNode;
  25792. // Overloaded variant of GetNodeAt to easy life of application developers which do not need to have the exact
  25793. // top position returned and always use client coordinates.
  25794. var
  25795. Dummy: Integer;
  25796. begin
  25797. Result := GetNodeAt(X, Y, True, Dummy);
  25798. end;
  25799. function TBaseVirtualTree.GetNodeAt(const P: TPoint): PVirtualNode;
  25800. begin
  25801. Result := GetNodeAt(P.X, P.Y);
  25802. end;
  25803. //----------------------------------------------------------------------------------------------------------------------
  25804. function TBaseVirtualTree.GetNodeAt(X, Y: Integer; Relative: Boolean; var NodeTop: Integer): PVirtualNode;
  25805. // This method returns the node that occupies the specified point, or nil if there's none.
  25806. // If Releative is True then X and Y are given in client coordinates otherwise they are considered as being
  25807. // absolute values into the virtual tree image (regardless of the current offsets in the tree window).
  25808. // NodeTop gets the absolute or relative top position of the node returned or is untouched if no node
  25809. // could be found.
  25810. var
  25811. AbsolutePos,
  25812. CurrentPos: Cardinal;
  25813. begin
  25814. if Y < 0 then
  25815. Y := 0;
  25816. AbsolutePos := Y;
  25817. if Relative then
  25818. Inc(AbsolutePos, -FOffsetY);
  25819. // CurrentPos tracks a running term of the current position to test for.
  25820. // It corresponds always to the top position of the currently considered node.
  25821. CurrentPos := 0;
  25822. // If the cache is available then use it.
  25823. if tsUseCache in FStates then
  25824. Result := FindInPositionCache(AbsolutePos, CurrentPos)
  25825. else
  25826. Result := GetFirstVisibleNoInit(nil, True);
  25827. // Determine node, of which position and height corresponds to the scroll position most closely.
  25828. while Assigned(Result) and (Result <> FRoot) do
  25829. begin
  25830. if AbsolutePos < (CurrentPos + NodeHeight[Result]) then
  25831. Break;
  25832. Inc(CurrentPos, NodeHeight[Result]);
  25833. Result := GetNextVisibleNoInit(Result, True);
  25834. end;
  25835. if Result = FRoot then
  25836. Result := nil;
  25837. // Since the given vertical position is likely not the same as the top position
  25838. // of the found node this top position is returned.
  25839. if Assigned(Result) then
  25840. begin
  25841. NodeTop := CurrentPos;
  25842. if Relative then
  25843. Inc(NodeTop, FOffsetY);
  25844. end;
  25845. end;
  25846. //----------------------------------------------------------------------------------------------------------------------
  25847. function TBaseVirtualTree.GetNodeData(Node: PVirtualNode): Pointer;
  25848. // Returns the address of the user defined data area in the node.
  25849. begin
  25850. Assert(FNodeDataSize > 0, 'NodeDataSize not initialized.');
  25851. if (FNodeDataSize <= 0) or (Node = nil) or (Node = FRoot) then
  25852. Result := nil
  25853. else
  25854. begin
  25855. Result := PByte(@Node.Data) + FTotalInternalDataSize;
  25856. Include(Node.States, vsOnFreeNodeCallRequired); // We now need to call OnFreeNode, see bug #323
  25857. end;
  25858. end;
  25859. //----------------------------------------------------------------------------------------------------------------------
  25860. function TBaseVirtualTree.GetNodeLevel(Node: PVirtualNode): Cardinal;
  25861. // returns the level of the given node
  25862. var
  25863. Run: PVirtualNode;
  25864. begin
  25865. Result := 0;
  25866. if Assigned(Node) and (Node <> FRoot) then
  25867. begin
  25868. Run := Node.Parent;
  25869. while Run <> FRoot do
  25870. begin
  25871. Run := Run.Parent;
  25872. Inc(Result);
  25873. end;
  25874. end;
  25875. end;
  25876. //----------------------------------------------------------------------------------------------------------------------
  25877. function TBaseVirtualTree.GetPrevious(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  25878. // Returns previous node in tree. If ConsiderChildrenAbove is True the function considers
  25879. // whether toChildrenAbove is currently set, otherwise the result will always be the previous
  25880. // node in top-down order regardless of the current PaintOptions.
  25881. // The Result will be initialized if needed.
  25882. var
  25883. Run: PVirtualNode;
  25884. begin
  25885. Result := Node;
  25886. if Assigned(Result) then
  25887. begin
  25888. Assert(Result <> FRoot, 'Node must not be the hidden root node.');
  25889. if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then
  25890. begin
  25891. // Has this node got children? Initialize them if necessary.
  25892. if (vsHasChildren in Result.States) and (Result.ChildCount = 0) then
  25893. InitChildren(Result);
  25894. // If there is a last child, take it; if not try the previous sibling.
  25895. if Assigned(Result.LastChild) then
  25896. Result := Result.LastChild
  25897. else
  25898. if Assigned(Result.PrevSibling) then
  25899. Result := Result.PrevSibling
  25900. else
  25901. begin
  25902. // If neither a last child nor a previous sibling exist, go the tree upwards and
  25903. // look, wether one of the parent nodes have a previous sibling. If not the result
  25904. // will ne nil.
  25905. repeat
  25906. Result := Result.Parent;
  25907. Run := nil;
  25908. if Result <> FRoot then
  25909. Run := Result.PrevSibling
  25910. else
  25911. Result := nil;
  25912. until Assigned(Run) or (Result = nil);
  25913. if Assigned(Run) then
  25914. Result := Run;
  25915. end;
  25916. end
  25917. else
  25918. begin
  25919. // Is there a previous sibling?
  25920. if Assigned(Node.PrevSibling) then
  25921. begin
  25922. // Go down and find the last child node.
  25923. Result := GetLast(Node.PrevSibling);
  25924. if Result = nil then
  25925. Result := Node.PrevSibling;
  25926. end
  25927. else
  25928. // no previous sibling so the parent of the node is the previous visible node
  25929. if Node.Parent <> FRoot then
  25930. Result := Node.Parent
  25931. else
  25932. Result := nil;
  25933. end;
  25934. end;
  25935. if Assigned(Result) and not (vsInitialized in Result.States) then
  25936. InitNode(Result);
  25937. end;
  25938. //----------------------------------------------------------------------------------------------------------------------
  25939. function TBaseVirtualTree.GetPreviousChecked(Node: PVirtualNode; State: TCheckState = csCheckedNormal;
  25940. ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  25941. begin
  25942. if (Node = nil) or (Node = FRoot) then
  25943. Result := GetLastNoInit(nil, ConsiderChildrenAbove)
  25944. else
  25945. Result := GetPreviousNoInit(Node, ConsiderChildrenAbove);
  25946. while Assigned(Result) and (Result.CheckState <> State) do
  25947. Result := GetPreviousNoInit(Result, ConsiderChildrenAbove);
  25948. if Assigned(Result) and not (vsInitialized in Result.States) then
  25949. InitNode(Result);
  25950. end;
  25951. //----------------------------------------------------------------------------------------------------------------------
  25952. function TBaseVirtualTree.GetPreviousCutCopy(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  25953. // Returns the previous node in the tree which is currently marked for a clipboard operation. Since only visible nodes can
  25954. // be marked (or they are hidden after they have been marked) it is not necessary to initialize nodes to check for
  25955. // child nodes. The result, however, is initialized if necessary.
  25956. begin
  25957. if ClipboardStates * FStates <> [] then
  25958. begin
  25959. if (Node = nil) or (Node = FRoot) then
  25960. Result := GetLastNoInit(nil, ConsiderChildrenAbove)
  25961. else
  25962. Result := GetPreviousNoInit(Node, ConsiderChildrenAbove);
  25963. while Assigned(Result) and not (vsCutOrCopy in Result.States) do
  25964. Result := GetPreviousNoInit(Result, ConsiderChildrenAbove);
  25965. if Assigned(Result) and not (vsInitialized in Result.States) then
  25966. InitNode(Result);
  25967. end
  25968. else
  25969. Result := nil;
  25970. end;
  25971. //----------------------------------------------------------------------------------------------------------------------
  25972. function TBaseVirtualTree.GetPreviousInitialized(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  25973. // Returns the previous node in tree which is initialized.
  25974. begin
  25975. Result := Node;
  25976. repeat
  25977. Result := GetPreviousNoInit(Result, ConsiderChildrenAbove);
  25978. until (Result = nil) or (vsInitialized in Result.States);
  25979. end;
  25980. //----------------------------------------------------------------------------------------------------------------------
  25981. function TBaseVirtualTree.GetPreviousLeaf(Node: PVirtualNode): PVirtualNode;
  25982. // Returns the previous node in the tree which has currently no children.
  25983. // The result is initialized if necessary.
  25984. begin
  25985. if (Node = nil) or (Node = FRoot) then
  25986. Result := FRoot.LastChild
  25987. else
  25988. Result := GetPrevious(Node);
  25989. while Assigned(Result) and (vsHasChildren in Result.States) do
  25990. Result := GetPrevious(Result);
  25991. if Assigned(Result) and not (vsInitialized in Result.States) then
  25992. InitNode(Result);
  25993. end;
  25994. //----------------------------------------------------------------------------------------------------------------------
  25995. function TBaseVirtualTree.GetPreviousLevel(Node: PVirtualNode; NodeLevel: Cardinal): PVirtualNode;
  25996. // Returns the previous node in the tree on a specific level.
  25997. // The result is initialized if necessary.
  25998. var
  25999. StartNodeLevel: Cardinal;
  26000. Run: PVirtualNode;
  26001. begin
  26002. Result := nil;
  26003. if Assigned(Node) and (Node <> FRoot) then
  26004. begin
  26005. StartNodeLevel := GetNodeLevel(Node);
  26006. if StartNodeLevel < NodeLevel then
  26007. begin
  26008. Result := Node.PrevSibling;
  26009. if Assigned(Result) then
  26010. begin
  26011. // go to last descendant of previous sibling with desired node level (if exists)
  26012. Run := Result;
  26013. while Assigned(Run) and (GetNodeLevel(Run) < NodeLevel) do
  26014. begin
  26015. Result := Run;
  26016. Run := GetLastChild(Run);
  26017. end;
  26018. if Assigned(Run) and (GetNodeLevel(Run) = NodeLevel) then
  26019. Result := Run
  26020. else
  26021. begin
  26022. if Assigned(Result.PrevSibling) then
  26023. Result := GetPreviousLevel(Result, NodeLevel)
  26024. else
  26025. if Assigned(Result) and (Result.Parent <> FRoot) then
  26026. Result := GetPreviousLevel(Result.Parent, NodeLevel)
  26027. else
  26028. Result := nil;
  26029. end;
  26030. end
  26031. else
  26032. Result := GetPreviousLevel(Node.Parent, NodeLevel);
  26033. end
  26034. else
  26035. if StartNodeLevel = NodeLevel then
  26036. begin
  26037. Result := Node.PrevSibling;
  26038. if not Assigned(Result) then // i.e. start node was a first sibling
  26039. begin
  26040. Result := Node.Parent;
  26041. if Assigned(Result) then
  26042. Result := GetPreviousLevel(Result, NodeLevel);
  26043. end;
  26044. end
  26045. else // i.e. StartNodeLevel > NodeLevel
  26046. Result := GetPreviousLevel(Node.Parent, NodeLevel);
  26047. end;
  26048. if Assigned(Result) and not (vsInitialized in Result.States) then
  26049. InitNode(Result);
  26050. end;
  26051. //----------------------------------------------------------------------------------------------------------------------
  26052. function TBaseVirtualTree.GetPreviousNoInit(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  26053. // Returns previous node in tree, optionally considering toChildrenAbove. No initialization is performed.
  26054. var
  26055. Run: PVirtualNode;
  26056. begin
  26057. Result := Node;
  26058. if Assigned(Result) then
  26059. begin
  26060. Assert(Result <> FRoot, 'Node must not be the hidden root node.');
  26061. if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then
  26062. begin
  26063. // If there is a last child, take it; if not try the previous sibling.
  26064. if Assigned(Result.LastChild) then
  26065. Result := Result.LastChild
  26066. else
  26067. if Assigned(Result.PrevSibling) then
  26068. Result := Result.PrevSibling
  26069. else
  26070. begin
  26071. // If neither a last child nor a previous sibling exist, go the tree upwards and
  26072. // look, wether one of the parent nodes have a previous sibling. If not the result
  26073. // will ne nil.
  26074. repeat
  26075. Result := Result.Parent;
  26076. Run := nil;
  26077. if Result <> FRoot then
  26078. Run := Result.PrevSibling
  26079. else
  26080. Result := nil;
  26081. until Assigned(Run) or (Result = nil);
  26082. if Assigned(Run) then
  26083. Result := Run;
  26084. end;
  26085. end
  26086. else
  26087. begin
  26088. // Is there a previous sibling?
  26089. if Assigned(Node.PrevSibling) then
  26090. begin
  26091. // Go down and find the last child node.
  26092. Result := GetLastNoInit(Node.PrevSibling);
  26093. if Result = nil then
  26094. Result := Node.PrevSibling;
  26095. end
  26096. else
  26097. // No previous sibling so the parent of the node is the previous node.
  26098. if Node.Parent <> FRoot then
  26099. Result := Node.Parent
  26100. else
  26101. Result := nil;
  26102. end;
  26103. end;
  26104. end;
  26105. //----------------------------------------------------------------------------------------------------------------------
  26106. function TBaseVirtualTree.GetPreviousSelected(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode;
  26107. // Returns the previous node in the tree which is currently selected. Since children of unitialized nodes cannot be
  26108. // in the current selection (because they simply do not exist yet) it is not necessary to initialize nodes here.
  26109. // The result however is initialized if necessary.
  26110. begin
  26111. if FSelectionCount > 0 then
  26112. begin
  26113. if (Node = nil) or (Node = FRoot) then
  26114. Result := FRoot.LastChild
  26115. else
  26116. Result := GetPreviousNoInit(Node, ConsiderChildrenAbove);
  26117. while Assigned(Result) and not (vsSelected in Result.States) do
  26118. Result := GetPreviousNoInit(Result, ConsiderChildrenAbove);
  26119. if Assigned(Result) and not (vsInitialized in Result.States) then
  26120. InitNode(Result);
  26121. end
  26122. else
  26123. Result := nil;
  26124. end;
  26125. //----------------------------------------------------------------------------------------------------------------------
  26126. function TBaseVirtualTree.GetPreviousSibling(Node: PVirtualNode): PVirtualNode;
  26127. // Returns the previous sibling of Node and initializes it if necessary.
  26128. begin
  26129. Result := Node;
  26130. if Assigned(Result) then
  26131. begin
  26132. Assert(Result <> FRoot, 'Node must not be the hidden root node.');
  26133. Result := Result.PrevSibling;
  26134. if Assigned(Result) and not (vsInitialized in Result.States) then
  26135. InitNode(Result);
  26136. end;
  26137. end;
  26138. function TBaseVirtualTree.GetPreviousSiblingNoInit(Node: PVirtualNode): PVirtualNode;
  26139. // Returns the previous sibling of Node
  26140. begin
  26141. Result := Node;
  26142. if Assigned(Result) then
  26143. begin
  26144. Assert(Result <> FRoot, 'Node must not be the hidden root node.');
  26145. Result := Result.PrevSibling;
  26146. end;
  26147. end;
  26148. //----------------------------------------------------------------------------------------------------------------------
  26149. function TBaseVirtualTree.GetPreviousVisible(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = True): PVirtualNode;
  26150. // Returns the previous node in tree, with regard to Node, which is visible.
  26151. // Nodes which need an initialization (including the result) are initialized.
  26152. // toChildrenAbove is optionally considered which is the default here.
  26153. var
  26154. Marker: PVirtualNode;
  26155. begin
  26156. Result := Node;
  26157. if Assigned(Result) then
  26158. begin
  26159. Assert(Result <> FRoot, 'Node must not be the hidden root node.');
  26160. repeat
  26161. // If the given node is not visible then look for a parent node which is visible and use its last visible
  26162. // child or the parent node (if there is no visible child) as result.
  26163. if not FullyVisible[Result] then
  26164. begin
  26165. Result := GetVisibleParent(Result, True);
  26166. if Result = FRoot then
  26167. Result := nil;
  26168. Marker := GetLastVisible(Result, True);
  26169. if Assigned(Marker) then
  26170. Result := Marker;
  26171. end
  26172. else
  26173. begin
  26174. if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then
  26175. begin
  26176. repeat
  26177. if Assigned(Result.LastChild) and (vsExpanded in Result.States) then
  26178. begin
  26179. Result := Result.LastChild;
  26180. if not (vsInitialized in Result.States) then
  26181. InitNode(Result);
  26182. if vsVisible in Result.States then
  26183. Break;
  26184. end
  26185. else
  26186. if Assigned(Result.PrevSibling) then
  26187. begin
  26188. if not (vsInitialized in Result.PrevSibling.States) then
  26189. InitNode(Result.PrevSibling);
  26190. if vsVisible in Result.PrevSibling.States then
  26191. begin
  26192. Result := Result.PrevSibling;
  26193. Break;
  26194. end;
  26195. end
  26196. else
  26197. begin
  26198. Marker := nil;
  26199. repeat
  26200. Result := Result.Parent;
  26201. if Result <> FRoot then
  26202. Marker := GetPreviousVisibleSibling(Result, True)
  26203. else
  26204. Result := nil;
  26205. until Assigned(Marker) or (Result = nil);
  26206. if Assigned(Marker) then
  26207. Result := Marker;
  26208. Break;
  26209. end;
  26210. until False;
  26211. end
  26212. else
  26213. begin
  26214. repeat
  26215. // Is there a previous sibling node?
  26216. if Assigned(Result.PrevSibling) then
  26217. begin
  26218. Result := Result.PrevSibling;
  26219. // Initialize the new node and check its visibility.
  26220. if not (vsInitialized in Result.States) then
  26221. InitNode(Result);
  26222. if vsVisible in Result.States then
  26223. begin
  26224. // If there are visible child nodes then use the last one.
  26225. Marker := GetLastVisible(Result, True, True);
  26226. if Assigned(Marker) then
  26227. Result := Marker;
  26228. Break;
  26229. end;
  26230. end
  26231. else
  26232. begin
  26233. // No previous sibling there so the parent node is the nearest previous node.
  26234. Result := Result.Parent;
  26235. if Result = FRoot then
  26236. Result := nil;
  26237. Break;
  26238. end;
  26239. until False;
  26240. end;
  26241. if Assigned(Result) and not (vsInitialized in Result.States) then
  26242. InitNode(Result);
  26243. end;
  26244. until not Assigned(Result) or IsEffectivelyVisible[Result];
  26245. end;
  26246. end;
  26247. //----------------------------------------------------------------------------------------------------------------------
  26248. function TBaseVirtualTree.GetPreviousVisibleNoInit(Node: PVirtualNode;
  26249. ConsiderChildrenAbove: Boolean = True): PVirtualNode;
  26250. // Returns the previous node in tree, with regard to Node, which is visible.
  26251. // toChildrenAbove is optionally considered which is the default here.
  26252. var
  26253. Marker: PVirtualNode;
  26254. begin
  26255. Result := Node;
  26256. if Assigned(Result) then
  26257. begin
  26258. Assert(Result <> FRoot, 'Node must not be the hidden root node.');
  26259. repeat
  26260. // If the given node is not visible then look for a parent node which is visible and use its last visible
  26261. // child or the parent node (if there is no visible child) as result.
  26262. if not FullyVisible[Result] then
  26263. begin
  26264. Result := GetVisibleParent(Result, True);
  26265. if Result = FRoot then
  26266. Result := nil;
  26267. Marker := GetLastVisibleNoInit(Result, True);
  26268. if Assigned(Marker) then
  26269. Result := Marker;
  26270. end
  26271. else
  26272. begin
  26273. if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then
  26274. begin
  26275. repeat
  26276. // Is the current node expanded and has children?
  26277. if (vsExpanded in Result.States) and Assigned(Result.LastChild) then
  26278. begin
  26279. Result := Result.LastChild;
  26280. if vsVisible in Result.States then
  26281. Break;
  26282. end
  26283. else
  26284. if Assigned(Result.PrevSibling) then
  26285. begin
  26286. // No children anymore, so take the previous sibling.
  26287. if vsVisible in Result.PrevSibling.States then
  26288. begin
  26289. Result := Result.PrevSibling;
  26290. Break;
  26291. end;
  26292. end
  26293. else
  26294. begin
  26295. // No children and no previous siblings, so walk up the tree and look wether
  26296. // a parent has a previous visible sibling. If that is the case take it,
  26297. // otherwise there is no previous visible node.
  26298. Marker := nil;
  26299. repeat
  26300. Result := Result.Parent;
  26301. if Result <> FRoot then
  26302. Marker := GetPreviousVisibleSiblingNoInit(Result, True)
  26303. else
  26304. Result := nil;
  26305. until Assigned(Marker) or (Result = nil);
  26306. if Assigned(Marker) then
  26307. Result := Marker;
  26308. Break;
  26309. end;
  26310. until False;
  26311. end
  26312. else
  26313. begin
  26314. repeat
  26315. // Is there a previous sibling node?
  26316. if Assigned(Result.PrevSibling) then
  26317. begin
  26318. Result := Result.PrevSibling;
  26319. if vsVisible in Result.States then
  26320. begin
  26321. // If there are visible child nodes then use the last one.
  26322. Marker := GetLastVisibleNoInit(Result, True, True);
  26323. if Assigned(Marker) then
  26324. Result := Marker;
  26325. Break;
  26326. end;
  26327. end
  26328. else
  26329. begin
  26330. // No previous sibling there so the parent node is the nearest previous node.
  26331. Result := Result.Parent;
  26332. if Result = FRoot then
  26333. Result := nil;
  26334. Break;
  26335. end;
  26336. until False;
  26337. end;
  26338. end;
  26339. until not Assigned(Result) or IsEffectivelyVisible[Result];
  26340. end;
  26341. end;
  26342. //----------------------------------------------------------------------------------------------------------------------
  26343. function TBaseVirtualTree.GetPreviousVisibleSibling(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode;
  26344. // Returns the previous visible sibling before Node. Initialization is done implicitly.
  26345. begin
  26346. Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameter.');
  26347. Result := Node;
  26348. repeat
  26349. Result := GetPreviousSibling(Result);
  26350. until not Assigned(Result) or ((vsVisible in Result.States) and
  26351. (IncludeFiltered or not IsEffectivelyFiltered[Result]));
  26352. end;
  26353. //----------------------------------------------------------------------------------------------------------------------
  26354. function TBaseVirtualTree.GetPreviousVisibleSiblingNoInit(Node: PVirtualNode;
  26355. IncludeFiltered: Boolean = False): PVirtualNode;
  26356. // Returns the previous visible sibling before Node.
  26357. begin
  26358. Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameter.');
  26359. Result := Node;
  26360. repeat
  26361. Result := Result.PrevSibling;
  26362. until not Assigned(Result) or ((vsVisible in Result.States) and
  26363. (IncludeFiltered or not IsEffectivelyFiltered[Result]));
  26364. end;
  26365. //----------------------------------------------------------------------------------------------------------------------
  26366. function TBaseVirtualTree.Nodes(ConsiderChildrenAbove: Boolean): TVTVirtualNodeEnumeration;
  26367. // Enumeration for all nodes
  26368. begin
  26369. Result.FMode := vneAll;
  26370. Result.FTree := Self;
  26371. Result.FConsiderChildrenAbove := ConsiderChildrenAbove;
  26372. end;
  26373. //----------------------------------------------------------------------------------------------------------------------
  26374. function TBaseVirtualTree.CheckedNodes(State: TCheckState; ConsiderChildrenAbove: Boolean): TVTVirtualNodeEnumeration;
  26375. // Enumeration for all checked nodes
  26376. begin
  26377. Result.FMode := vneChecked;
  26378. Result.FTree := Self;
  26379. Result.FState := State;
  26380. Result.FConsiderChildrenAbove := ConsiderChildrenAbove;
  26381. end;
  26382. //----------------------------------------------------------------------------------------------------------------------
  26383. function TBaseVirtualTree.ChildNodes(Node: PVirtualNode): TVTVirtualNodeEnumeration;
  26384. // Enumeration for child nodes
  26385. begin
  26386. Result.FMode := vneChild;
  26387. Result.FTree := Self;
  26388. Result.FNode := Node;
  26389. end;
  26390. //----------------------------------------------------------------------------------------------------------------------
  26391. function TBaseVirtualTree.CutCopyNodes(ConsiderChildrenAbove: Boolean): TVTVirtualNodeEnumeration;
  26392. // Enumeration for cut copy node
  26393. begin
  26394. Result.FMode := vneCutCopy;
  26395. Result.FTree := Self;
  26396. Result.FConsiderChildrenAbove := ConsiderChildrenAbove;
  26397. end;
  26398. //----------------------------------------------------------------------------------------------------------------------
  26399. function TBaseVirtualTree.InitializedNodes(ConsiderChildrenAbove: Boolean): TVTVirtualNodeEnumeration;
  26400. // Enumeration for initialized nodes
  26401. begin
  26402. Result.FMode := vneInitialized;
  26403. Result.FTree := Self;
  26404. Result.FConsiderChildrenAbove := ConsiderChildrenAbove;
  26405. end;
  26406. //----------------------------------------------------------------------------------------------------------------------
  26407. function TBaseVirtualTree.LeafNodes: TVTVirtualNodeEnumeration;
  26408. // Enumeration for leaf nodes
  26409. begin
  26410. Result.FMode := vneLeaf;
  26411. Result.FTree := Self;
  26412. end;
  26413. //----------------------------------------------------------------------------------------------------------------------
  26414. function TBaseVirtualTree.LevelNodes(NodeLevel: Cardinal): TVTVirtualNodeEnumeration;
  26415. // Enumeration for level nodes
  26416. begin
  26417. Result.FMode := vneLevel;
  26418. Result.FTree := Self;
  26419. Result.FNodeLevel := NodeLevel;
  26420. end;
  26421. //----------------------------------------------------------------------------------------------------------------------
  26422. function TBaseVirtualTree.NoInitNodes(ConsiderChildrenAbove: Boolean): TVTVirtualNodeEnumeration;
  26423. // Enumeration for no init nodes
  26424. begin
  26425. Result.FMode := vneNoInit;
  26426. Result.FTree := Self;
  26427. Result.FConsiderChildrenAbove := ConsiderChildrenAbove;
  26428. end;
  26429. //----------------------------------------------------------------------------------------------------------------------
  26430. function TBaseVirtualTree.SelectedNodes(ConsiderChildrenAbove: Boolean): TVTVirtualNodeEnumeration;
  26431. // Enumeration for selected nodes
  26432. begin
  26433. Result.FMode := vneSelected;
  26434. Result.FTree := Self;
  26435. Result.FConsiderChildrenAbove := ConsiderChildrenAbove;
  26436. end;
  26437. //----------------------------------------------------------------------------------------------------------------------
  26438. function TBaseVirtualTree.VisibleNodes(Node: PVirtualNode; ConsiderChildrenAbove: Boolean;
  26439. IncludeFiltered: Boolean): TVTVirtualNodeEnumeration;
  26440. // Enumeration for visible nodes
  26441. begin
  26442. Result.FMode := vneVisible;
  26443. Result.FTree := Self;
  26444. Result.FNode := Node;
  26445. Result.FConsiderChildrenAbove := ConsiderChildrenAbove;
  26446. Result.FIncludeFiltered := IncludeFiltered;
  26447. end;
  26448. //----------------------------------------------------------------------------------------------------------------------
  26449. function TBaseVirtualTree.VisibleChildNodes(Node: PVirtualNode; IncludeFiltered: Boolean): TVTVirtualNodeEnumeration;
  26450. // Enumeration for visible child nodes
  26451. begin
  26452. Result.FMode := vneVisibleChild;
  26453. Result.FTree := Self;
  26454. Result.FNode := Node;
  26455. Result.FIncludeFiltered := IncludeFiltered;
  26456. end;
  26457. //----------------------------------------------------------------------------------------------------------------------
  26458. function TBaseVirtualTree.VisibleChildNoInitNodes(Node: PVirtualNode; IncludeFiltered: Boolean): TVTVirtualNodeEnumeration;
  26459. // Enumeration for visible child no init nodes
  26460. begin
  26461. Result.FMode := vneVisibleNoInitChild;
  26462. Result.FTree := Self;
  26463. Result.FNode := Node;
  26464. Result.FIncludeFiltered := IncludeFiltered;
  26465. end;
  26466. //----------------------------------------------------------------------------------------------------------------------
  26467. function TBaseVirtualTree.VisibleNoInitNodes(Node: PVirtualNode; ConsiderChildrenAbove: Boolean;
  26468. IncludeFiltered: Boolean): TVTVirtualNodeEnumeration;
  26469. // Enumeration for visible no init nodes
  26470. begin
  26471. Result.FMode := vneVisibleNoInit;
  26472. Result.FTree := Self;
  26473. Result.FNode := Node;
  26474. Result.FConsiderChildrenAbove := ConsiderChildrenAbove;
  26475. Result.FIncludeFiltered := IncludeFiltered;
  26476. end;
  26477. //----------------------------------------------------------------------------------------------------------------------
  26478. function TBaseVirtualTree.GetSortedCutCopySet(Resolve: Boolean): TNodeArray;
  26479. // Same as GetSortedSelection but with nodes marked as being part in the current cut/copy set (e.g. for clipboard).
  26480. var
  26481. Run: PVirtualNode;
  26482. Counter: Cardinal;
  26483. //--------------- local function --------------------------------------------
  26484. procedure IncludeThisNode(Node: PVirtualNode);
  26485. // adds the given node to the result
  26486. var
  26487. Len: Cardinal;
  26488. begin
  26489. Len := Length(Result);
  26490. if Counter = Len then
  26491. begin
  26492. if Len < 100 then
  26493. Len := 100
  26494. else
  26495. Len := Len + Len div 10;
  26496. SetLength(Result, Len);
  26497. end;
  26498. Result[Counter] := Node;
  26499. Inc(Counter);
  26500. end;
  26501. //--------------- end local function ----------------------------------------
  26502. begin
  26503. Run := FRoot.FirstChild;
  26504. Counter := 0;
  26505. if Resolve then
  26506. begin
  26507. // Resolving is actually easy: just find the first cutted node in logical order
  26508. // and then never go deeper in level than this node as long as there's a sibling node.
  26509. // Restart the search for a cutted node (at any level) if there are no further siblings.
  26510. while Assigned(Run) do
  26511. begin
  26512. if vsCutOrCopy in Run.States then
  26513. begin
  26514. IncludeThisNode(Run);
  26515. if Assigned(Run.NextSibling) then
  26516. Run := Run.NextSibling
  26517. else
  26518. begin
  26519. // If there are no further siblings then go up one or more levels until a node is
  26520. // found or all nodes have been processed. Although we consider here only initialized
  26521. // nodes we don't need to make any special checks as only initialized nodes can also be selected.
  26522. repeat
  26523. Run := Run.Parent;
  26524. until (Run = FRoot) or Assigned(Run.NextSibling);
  26525. if Run = FRoot then
  26526. Break
  26527. else
  26528. Run := Run.NextSibling;
  26529. end;
  26530. end
  26531. else
  26532. Run := GetNextNoInit(Run);
  26533. end;
  26534. end
  26535. else
  26536. while Assigned(Run) do
  26537. begin
  26538. if vsCutOrCopy in Run.States then
  26539. IncludeThisNode(Run);
  26540. Run := GetNextNoInit(Run);
  26541. end;
  26542. // set the resulting array to its real length
  26543. SetLength(Result, Counter);
  26544. end;
  26545. //----------------------------------------------------------------------------------------------------------------------
  26546. function TBaseVirtualTree.GetSortedSelection(Resolve: Boolean): TNodeArray;
  26547. // Returns a list of selected nodes sorted in logical order, that is, as they appear in the tree.
  26548. // If Resolve is True then nodes which are children of other selected nodes are not put into the new array.
  26549. // This feature is in particuar important when doing drag'n drop as in this case all selected node plus their children
  26550. // need to be considered. A selected node which is child (grand child etc.) of another selected node is then
  26551. // automatically included and doesn't need to be explicitely mentioned in the returned selection array.
  26552. //
  26553. // Note: The caller is responsible for freeing the array. Allocation is done here. Usually, though, freeing the array
  26554. // doesn't need additional attention as it is automatically freed by Delphi when it gets out of scope.
  26555. var
  26556. Run: PVirtualNode;
  26557. Counter: Cardinal;
  26558. begin
  26559. SetLength(Result, FSelectionCount);
  26560. if FSelectionCount > 0 then
  26561. begin
  26562. Run := FRoot.FirstChild;
  26563. Counter := 0;
  26564. if Resolve then
  26565. begin
  26566. // Resolving is actually easy: just find the first selected node in logical order
  26567. // and then never go deeper in level than this node as long as there's a sibling node.
  26568. // Restart the search for a selected node (at any level) if there are no further siblings.
  26569. while Assigned(Run) do
  26570. begin
  26571. if vsSelected in Run.States then
  26572. begin
  26573. Result[Counter] := Run;
  26574. Inc(Counter);
  26575. if Assigned(Run.NextSibling) then
  26576. Run := Run.NextSibling
  26577. else
  26578. begin
  26579. // If there are no further siblings then go up one or more levels until a node is
  26580. // found or all nodes have been processed. Although we consider here only initialized
  26581. // nodes we don't need to make any special checks as only initialized nodes can also be selected.
  26582. repeat
  26583. Run := Run.Parent;
  26584. until (Run = FRoot) or Assigned(Run.NextSibling);
  26585. if Run = FRoot then
  26586. Break
  26587. else
  26588. Run := Run.NextSibling;
  26589. end;
  26590. end
  26591. else
  26592. Run := GetNextNoInit(Run);
  26593. end;
  26594. end
  26595. else
  26596. while Assigned(Run) do
  26597. begin
  26598. if vsSelected in Run.States then
  26599. begin
  26600. Result[Counter] := Run;
  26601. Inc(Counter);
  26602. end;
  26603. Run := GetNextNoInit(Run);
  26604. end;
  26605. // Since we may have skipped some nodes the result array is likely to be smaller than the
  26606. // selection array, hence shorten the result to true length.
  26607. if Integer(Counter) < Length(Result) then
  26608. SetLength(Result, Counter);
  26609. end;
  26610. end;
  26611. //----------------------------------------------------------------------------------------------------------------------
  26612. procedure TBaseVirtualTree.GetTextInfo(Node: PVirtualNode; Column: TColumnIndex; const AFont: TFont; var R: TRect;
  26613. var Text: UnicodeString);
  26614. // Generic base method for editors, hint windows etc. to get some info about a node.
  26615. begin
  26616. R := Rect(0, 0, 0, 0);
  26617. Text := '';
  26618. AFont.Assign(Font);
  26619. end;
  26620. //----------------------------------------------------------------------------------------------------------------------
  26621. function TBaseVirtualTree.GetTreeRect: TRect;
  26622. // Returns the true size of the tree in pixels. This size is at least ClientHeight x ClientWidth and depends on
  26623. // the expand state, header size etc.
  26624. // Note: if no columns are used then the width of the tree is determined by the largest node which is currently in the
  26625. // client area. This might however not be the largest node in the entire tree.
  26626. begin
  26627. Result := Rect(0, 0, Max(FRangeX, ClientWidth), Max(FRangeY, ClientHeight));
  26628. end;
  26629. //----------------------------------------------------------------------------------------------------------------------
  26630. function TBaseVirtualTree.GetVisibleParent(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode;
  26631. // Returns the first (nearest) parent node of Node which is visible.
  26632. // This method is one of the seldom cases where the hidden root node could be returned.
  26633. begin
  26634. Assert(Assigned(Node), 'Node must not be nil.');
  26635. Assert(Node <> FRoot, 'Node must not be the hidden root node.');
  26636. Result := Node.Parent;
  26637. while (Result <> FRoot) and (not FullyVisible[Result] or (not IncludeFiltered and IsEffectivelyFiltered[Result])) do
  26638. Result := Result.Parent;
  26639. end;
  26640. //----------------------------------------------------------------------------------------------------------------------
  26641. function TBaseVirtualTree.HasAsParent(Node, PotentialParent: PVirtualNode): Boolean;
  26642. // Determines whether Node has got PotentialParent as one of its parents.
  26643. var
  26644. Run: PVirtualNode;
  26645. begin
  26646. Result := Assigned(Node) and Assigned(PotentialParent) and (Node <> PotentialParent);
  26647. if Result then
  26648. begin
  26649. Run := Node;
  26650. while (Run <> FRoot) and (Run <> PotentialParent) do
  26651. Run := Run.Parent;
  26652. Result := Run = PotentialParent;
  26653. end;
  26654. end;
  26655. //----------------------------------------------------------------------------------------------------------------------
  26656. function TBaseVirtualTree.InsertNode(Node: PVirtualNode; Mode: TVTNodeAttachMode; UserData: Pointer = nil): PVirtualNode;
  26657. // Adds a new node relative to Node. The final position is determined by Mode.
  26658. // UserData can be used to set the first SizeOf(Pointer) bytes of the user data area to an initial value which can be used
  26659. // in OnInitNode and will also cause to trigger the OnFreeNode event (if <> nil) even if the node is not yet
  26660. // "officially" initialized.
  26661. // InsertNode is a compatibility method and will implicitly validate the given node if the new node
  26662. // is to be added as child node. This is however against the virtual paradigm and hence I dissuade from its usage.
  26663. var
  26664. NodeData: ^Pointer;
  26665. begin
  26666. if Mode <> amNoWhere then
  26667. begin
  26668. CancelEditNode;
  26669. if Node = nil then
  26670. Node := FRoot;
  26671. // we need a new node...
  26672. Result := MakeNewNode;
  26673. // avoid erronous attach modes
  26674. if Node = FRoot then
  26675. begin
  26676. case Mode of
  26677. amInsertBefore:
  26678. Mode := amAddChildFirst;
  26679. amInsertAfter:
  26680. Mode := amAddChildLast;
  26681. end;
  26682. end;
  26683. // Validate given node in case the new node becomes its child.
  26684. if (Mode in [amAddChildFirst, amAddChildLast]) and not (vsInitialized in Node.States) then
  26685. InitNode(Node);
  26686. InternalConnectNode(Result, Node, Self, Mode);
  26687. // Check if there is initial user data and there is also enough user data space allocated.
  26688. if Assigned(UserData) then
  26689. if FNodeDataSize >= SizeOf(Pointer) then
  26690. begin
  26691. NodeData := Pointer(PByte(@Result.Data) + FTotalInternalDataSize);
  26692. NodeData^ := UserData;
  26693. Include(Result.States, vsOnFreeNodeCallRequired);
  26694. end
  26695. else
  26696. ShowError(SCannotSetUserData, hcTFCannotSetUserData);
  26697. if FUpdateCount = 0 then
  26698. begin
  26699. // If auto sort is enabled then sort the node or its parent (depending on the insert mode).
  26700. if (toAutoSort in FOptions.FAutoOptions) and (FHeader.FSortColumn > InvalidColumn) then
  26701. case Mode of
  26702. amInsertBefore,
  26703. amInsertAfter:
  26704. // Here no initialization is necessary because *if* a node has already got children then it
  26705. // must also be initialized.
  26706. // Note: Node can never be FRoot at this point.
  26707. Sort(Node.Parent, FHeader.FSortColumn, FHeader.FSortDirection, True);
  26708. amAddChildFirst,
  26709. amAddChildLast:
  26710. Sort(Node, FHeader.FSortColumn, FHeader.FSortDirection, True);
  26711. end;
  26712. UpdateScrollBars(True);
  26713. if Mode = amInsertBefore then
  26714. InvalidateToBottom(Result)
  26715. else
  26716. InvalidateToBottom(Node);
  26717. end;
  26718. StructureChange(Result, crNodeAdded);
  26719. end
  26720. else
  26721. Result := nil;
  26722. end;
  26723. //----------------------------------------------------------------------------------------------------------------------
  26724. procedure TBaseVirtualTree.InvalidateChildren(Node: PVirtualNode; Recursive: Boolean);
  26725. // Invalidates Node and its immediate children.
  26726. // If Recursive is True then all grandchildren are invalidated as well.
  26727. // The node itself is initialized if necessary and its child nodes are created (and initialized too if
  26728. // Recursive is True).
  26729. var
  26730. Run: PVirtualNode;
  26731. begin
  26732. if Assigned(Node) then
  26733. begin
  26734. if not (vsInitialized in Node.States) then
  26735. InitNode(Node);
  26736. InvalidateNode(Node);
  26737. if (vsHasChildren in Node.States) and (Node.ChildCount = 0) then
  26738. InitChildren(Node);
  26739. Run := Node.FirstChild;
  26740. end
  26741. else
  26742. Run := FRoot.FirstChild;
  26743. while Assigned(Run) do
  26744. begin
  26745. InvalidateNode(Run);
  26746. if Recursive then
  26747. InvalidateChildren(Run, True);
  26748. Run := Run.NextSibling;
  26749. end;
  26750. end;
  26751. //----------------------------------------------------------------------------------------------------------------------
  26752. procedure TBaseVirtualTree.InvalidateColumn(Column: TColumnIndex);
  26753. // Invalidates the client area part of a column.
  26754. var
  26755. R: TRect;
  26756. begin
  26757. if (FUpdateCount = 0) and HandleAllocated and FHeader.FColumns.IsValidColumn(Column) then
  26758. begin
  26759. R := ClientRect;
  26760. FHeader.Columns.GetColumnBounds(Column, R.Left, R.Right);
  26761. InvalidateRect(Handle, @R, False);
  26762. end;
  26763. end;
  26764. //----------------------------------------------------------------------------------------------------------------------
  26765. function TBaseVirtualTree.InvalidateNode(Node: PVirtualNode): TRect;
  26766. // Initiates repaint of the given node and returns the just invalidated rectangle.
  26767. begin
  26768. if (FUpdateCount = 0) and HandleAllocated then
  26769. begin
  26770. Result := GetDisplayRect(Node, NoColumn, False);
  26771. InvalidateRect(Handle, @Result, False);
  26772. end;
  26773. end;
  26774. //----------------------------------------------------------------------------------------------------------------------
  26775. procedure TBaseVirtualTree.InvalidateToBottom(Node: PVirtualNode);
  26776. // Initiates repaint of client area starting at given node. If this node is not visible or not yet initialized
  26777. // then nothing happens.
  26778. var
  26779. R: TRect;
  26780. begin
  26781. if (FUpdateCount = 0) and HandleAllocated then
  26782. begin
  26783. if (Node = nil) or (Node = FRoot) then
  26784. Invalidate
  26785. else
  26786. if (vsInitialized in Node.States) and IsEffectivelyVisible[Node] then
  26787. begin
  26788. R := GetDisplayRect(Node, -1, False);
  26789. if R.Top < ClientHeight then
  26790. begin
  26791. if (toChildrenAbove in FOptions.FPaintOptions) and (vsExpanded in Node.States) then
  26792. Dec(R.Top, Node.TotalHeight + NodeHeight[Node]);
  26793. R.Bottom := ClientHeight;
  26794. InvalidateRect(Handle, @R, False);
  26795. end;
  26796. end;
  26797. end;
  26798. end;
  26799. //----------------------------------------------------------------------------------------------------------------------
  26800. procedure TBaseVirtualTree.InvertSelection(VisibleOnly: Boolean);
  26801. // Inverts the current selection (so nodes which are selected become unselected and vice versa).
  26802. // If VisibleOnly is True then only visible nodes are considered.
  26803. var
  26804. Run: PVirtualNode;
  26805. NewSize: Integer;
  26806. NextFunction: TGetNextNodeProc;
  26807. TriggerChange: Boolean;
  26808. begin
  26809. if not FSelectionLocked and (toMultiSelect in FOptions.FSelectionOptions) then
  26810. begin
  26811. Run := FRoot.FirstChild;
  26812. ClearTempCache;
  26813. if VisibleOnly then
  26814. NextFunction := GetNextVisibleNoInit
  26815. else
  26816. NextFunction := GetNextNoInit;
  26817. while Assigned(Run) do
  26818. begin
  26819. if vsSelected in Run.States then
  26820. InternalRemoveFromSelection(Run)
  26821. else
  26822. InternalCacheNode(Run);
  26823. Run := NextFunction(Run);
  26824. end;
  26825. // do some housekeeping
  26826. // Need to trigger the OnChange event from here if nodes were only deleted but not added.
  26827. TriggerChange := False;
  26828. NewSize := PackArray(FSelection, FSelectionCount);
  26829. if NewSize > -1 then
  26830. begin
  26831. FSelectionCount := NewSize;
  26832. SetLength(FSelection, FSelectionCount);
  26833. TriggerChange := True;
  26834. end;
  26835. if FTempNodeCount > 0 then
  26836. begin
  26837. AddToSelection(FTempNodeCache, FTempNodeCount);
  26838. ClearTempCache;
  26839. TriggerChange := False;
  26840. end;
  26841. Invalidate;
  26842. if TriggerChange then
  26843. Change(nil);
  26844. if Self.SelectedCount = 0 then
  26845. FNextNodeToSelect := nil;//Ensure that no other node is selected now
  26846. end;
  26847. end;
  26848. //----------------------------------------------------------------------------------------------------------------------
  26849. function TBaseVirtualTree.IsEditing: Boolean;
  26850. begin
  26851. Result := tsEditing in FStates;
  26852. end;
  26853. //----------------------------------------------------------------------------------------------------------------------
  26854. function TBaseVirtualTree.IsMouseSelecting: Boolean;
  26855. begin
  26856. Result := (tsDrawSelPending in FStates) or (tsDrawSelecting in FStates);
  26857. end;
  26858. //----------------------------------------------------------------------------------------------------------------------
  26859. function TBaseVirtualTree.IterateSubtree(Node: PVirtualNode; Callback: TVTGetNodeProc; Data: Pointer;
  26860. Filter: TVirtualNodeStates = []; DoInit: Boolean = False; ChildNodesOnly: Boolean = False): PVirtualNode;
  26861. // Iterates through the all children and grandchildren etc. of Node (or the entire tree if Node = nil)
  26862. // and calls for each node the provided callback method (which must not be empty).
  26863. // Filter determines which nodes to consider (an empty set denotes all nodes).
  26864. // If DoInit is True then nodes which aren't initialized yet will be initialized.
  26865. // Note: During execution of the callback the application can set Abort to True. In this case the iteration is stopped
  26866. // and the last accessed node (the one on which the callback set Abort to True) is returned to the caller.
  26867. // Otherwise (no abort) nil is returned.
  26868. var
  26869. Stop: PVirtualNode;
  26870. Abort: Boolean;
  26871. GetNextNode: TGetNextNodeProc;
  26872. WasIterating: Boolean;
  26873. begin
  26874. Assert(Node <> FRoot, 'Node must not be the hidden root node.');
  26875. WasIterating := tsIterating in FStates;
  26876. DoStateChange([tsIterating]);
  26877. try
  26878. // prepare function to be used when advancing
  26879. if DoInit then
  26880. GetNextNode := GetNext
  26881. else
  26882. GetNextNode := GetNextNoInit;
  26883. Abort := False;
  26884. if Node = nil then
  26885. Stop := nil
  26886. else
  26887. begin
  26888. if not (vsInitialized in Node.States) and DoInit then
  26889. InitNode(Node);
  26890. // The stopper does not need to be initialized since it is not taken into the enumeration.
  26891. Stop := Node.NextSibling;
  26892. if Stop = nil then
  26893. begin
  26894. Stop := Node;
  26895. repeat
  26896. Stop := Stop.Parent;
  26897. until (Stop = FRoot) or Assigned(Stop.NextSibling);
  26898. if Stop = FRoot then
  26899. Stop := nil
  26900. else
  26901. Stop := Stop.NextSibling;
  26902. end;
  26903. end;
  26904. // Use first node if we start with the root.
  26905. if Node = nil then
  26906. Node := GetFirstNoInit;
  26907. if Assigned(Node) then
  26908. begin
  26909. if not (vsInitialized in Node.States) and DoInit then
  26910. InitNode(Node);
  26911. // Skip given node if only the child nodes are requested.
  26912. if ChildNodesOnly then
  26913. begin
  26914. if Node.ChildCount = 0 then
  26915. Node := nil
  26916. else
  26917. Node := GetNextNode(Node);
  26918. end;
  26919. if Filter = [] then
  26920. begin
  26921. // unfiltered loop
  26922. while Assigned(Node) and (Node <> Stop) do
  26923. begin
  26924. Callback(Self, Node, Data, Abort);
  26925. if Abort then
  26926. Break;
  26927. Node := GetNextNode(Node);
  26928. end;
  26929. end
  26930. else
  26931. begin
  26932. // filtered loop
  26933. while Assigned(Node) and (Node <> Stop) do
  26934. begin
  26935. if Node.States * Filter = Filter then
  26936. Callback(Self, Node, Data, Abort);
  26937. if Abort then
  26938. Break;
  26939. Node := GetNextNode(Node);
  26940. end;
  26941. end;
  26942. end;
  26943. if Abort then
  26944. Result := Node
  26945. else
  26946. Result := nil;
  26947. finally
  26948. if not WasIterating then
  26949. DoStateChange([], [tsIterating]);
  26950. end;
  26951. end;
  26952. //----------------------------------------------------------------------------------------------------------------------
  26953. procedure TBaseVirtualTree.LoadFromFile(const FileName: TFileName);
  26954. var
  26955. FileStream: TFileStream;
  26956. begin
  26957. FileStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  26958. try
  26959. LoadFromStream(FileStream);
  26960. finally
  26961. FileStream.Free;
  26962. end;
  26963. end;
  26964. //----------------------------------------------------------------------------------------------------------------------
  26965. procedure TBaseVirtualTree.LoadFromStream(Stream: TStream);
  26966. // Clears the current content of the tree and loads a new structure from the given stream.
  26967. var
  26968. ThisID: TMagicID;
  26969. Version,
  26970. Count: Cardinal;
  26971. Node: PVirtualNode;
  26972. begin
  26973. if not (toReadOnly in FOptions.FMiscOptions) then
  26974. begin
  26975. Clear;
  26976. // Check first whether this is a stream we can read.
  26977. if Stream.Read(ThisID, SizeOf(TMagicID)) < SizeOf(TMagicID) then
  26978. ShowError(SStreamTooSmall, hcTFStreamTooSmall);
  26979. if (ThisID[0] = MagicID[0]) and
  26980. (ThisID[1] = MagicID[1]) and
  26981. (ThisID[2] = MagicID[2]) and
  26982. (ThisID[5] = MagicID[5]) then
  26983. begin
  26984. Version := Word(ThisID[3]);
  26985. if Version <= VTTreeStreamVersion then
  26986. begin
  26987. BeginUpdate;
  26988. try
  26989. if Version < 2 then
  26990. Count := MaxInt
  26991. else
  26992. Stream.ReadBuffer(Count, SizeOf(Count));
  26993. while (Stream.Position < Stream.Size) and (Count > 0) do
  26994. begin
  26995. Dec(Count);
  26996. Node := MakeNewNode;
  26997. InternalConnectNode(Node, FRoot, Self, amAddChildLast);
  26998. InternalAddFromStream(Stream, Version, Node);
  26999. end;
  27000. DoNodeCopied(nil);
  27001. if Assigned(FOnLoadTree) then
  27002. FOnLoadTree(Self, Stream);
  27003. finally
  27004. EndUpdate;
  27005. end;
  27006. end
  27007. else
  27008. ShowError(SWrongStreamVersion, hcTFWrongStreamVersion);
  27009. end
  27010. else
  27011. ShowError(SWrongStreamFormat, hcTFWrongStreamFormat);
  27012. end;
  27013. end;
  27014. //----------------------------------------------------------------------------------------------------------------------
  27015. procedure TBaseVirtualTree.MeasureItemHeight(const Canvas: TCanvas; Node: PVirtualNode);
  27016. // If the height of the given node has not yet been measured then do it now.
  27017. var
  27018. NewNodeHeight: Integer;
  27019. begin
  27020. if not (vsHeightMeasured in Node.States) {$if CompilerVersion < 20}and (MainThreadId = GetCurrentThreadId){$ifend} then
  27021. begin
  27022. Include(Node.States, vsHeightMeasured);
  27023. if (toVariableNodeHeight in FOptions.FMiscOptions) then
  27024. begin
  27025. NewNodeHeight := Node.NodeHeight;
  27026. {$if CompilerVersion >= 20} // Anonymous methods help to make this thread safe easily. In Delphi 2007 and lower developers must take care themselves about thread synchronization when consuming the OnMeasureItemHeight event
  27027. if (MainThreadId <> GetCurrentThreadId) then
  27028. TThread.Synchronize(nil,
  27029. procedure
  27030. begin
  27031. DoMeasureItem(Canvas, Node, NewNodeHeight);
  27032. SetNodeHeight(Node, NewNodeHeight);
  27033. end
  27034. )
  27035. else
  27036. {$ifend}
  27037. begin
  27038. DoMeasureItem(Canvas, Node, NewNodeHeight);
  27039. SetNodeHeight(Node, NewNodeHeight);
  27040. end;
  27041. end;
  27042. end;
  27043. end;
  27044. //----------------------------------------------------------------------------------------------------------------------
  27045. procedure TBaseVirtualTree.MoveTo(Node: PVirtualNode; Tree: TBaseVirtualTree; Mode: TVTNodeAttachMode;
  27046. ChildrenOnly: Boolean);
  27047. // A simplified method to allow to move nodes to the root of another tree.
  27048. begin
  27049. MoveTo(Node, Tree.FRoot, Mode, ChildrenOnly);
  27050. end;
  27051. //----------------------------------------------------------------------------------------------------------------------
  27052. procedure TBaseVirtualTree.MoveTo(Source, Target: PVirtualNode; Mode: TVTNodeAttachMode; ChildrenOnly: Boolean);
  27053. // Moves the given node (and all its children) to Target. Source must belong to the tree instance which calls this
  27054. // MoveTo method. Mode determines how to connect Source to Target.
  27055. // This method might involve a change of the tree if Target belongs to a different tree than Source.
  27056. var
  27057. TargetTree: TBaseVirtualTree;
  27058. Allowed: Boolean;
  27059. NewNode: PVirtualNode;
  27060. Stream: TMemoryStream;
  27061. begin
  27062. Assert(TreeFromNode(Source) = Self, 'The source tree must contain the source node.');
  27063. // When moving nodes then source and target must not be the same node unless only the source's children are
  27064. // moved and they are inserted before or after the node itself.
  27065. Allowed := (Source <> Target) or ((Mode in [amInsertBefore, amInsertAfter]) and ChildrenOnly);
  27066. if Allowed and (Mode <> amNoWhere) and Assigned(Source) and (Source <> FRoot) and
  27067. not (toReadOnly in FOptions.FMiscOptions) then
  27068. begin
  27069. // Assume that an empty destination means the root in this (the source) tree.
  27070. if Target = nil then
  27071. begin
  27072. TargetTree := Self;
  27073. Target := FRoot;
  27074. Mode := amAddChildFirst;
  27075. end
  27076. else
  27077. TargetTree := TreeFromNode(Target);
  27078. if Target = TargetTree.FRoot then
  27079. begin
  27080. case Mode of
  27081. amInsertBefore:
  27082. Mode := amAddChildFirst;
  27083. amInsertAfter:
  27084. Mode := amAddChildLast;
  27085. end;
  27086. end;
  27087. // Make sure the target node is initialized.
  27088. if not (vsInitialized in Target.States) then
  27089. TargetTree.InitNode(Target)
  27090. else
  27091. if (vsHasChildren in Target.States) and (Target.ChildCount = 0) then
  27092. TargetTree.InitChildren(Target);
  27093. if TargetTree = Self then
  27094. begin
  27095. // Simple case: move node(s) within the same tree.
  27096. if Target = FRoot then
  27097. Allowed := DoNodeMoving(Source, nil)
  27098. else
  27099. Allowed := DoNodeMoving(Source, Target);
  27100. if Allowed then
  27101. begin
  27102. // Check first that Source is not added as new child to a target node which
  27103. // is already a child of Source.
  27104. // Consider the case Source and Target are the same node, but only child nodes are moved.
  27105. if (Source <> Target) and HasAsParent(Target, Source) then
  27106. ShowError(SWrongMoveError, hcTFWrongMoveError);
  27107. if not ChildrenOnly then
  27108. begin
  27109. // Disconnect from old location.
  27110. InternalDisconnectNode(Source, True);
  27111. // Connect to new location.
  27112. InternalConnectNode(Source, Target, Self, Mode);
  27113. DoNodeMoved(Source);
  27114. end
  27115. else
  27116. begin
  27117. // Only child nodes should be moved. Insertion order depends on move mode.
  27118. if Mode = amAddChildFirst then
  27119. begin
  27120. Source := Source.LastChild;
  27121. while Assigned(Source) do
  27122. begin
  27123. NewNode := Source.PrevSibling;
  27124. // Disconnect from old location.
  27125. InternalDisconnectNode(Source, True, False);
  27126. // Connect to new location.
  27127. InternalConnectNode(Source, Target, Self, Mode);
  27128. DoNodeMoved(Source);
  27129. Source := NewNode;
  27130. end;
  27131. end
  27132. else
  27133. begin
  27134. Source := Source.FirstChild;
  27135. while Assigned(Source) do
  27136. begin
  27137. NewNode := Source.NextSibling;
  27138. // Disconnect from old location.
  27139. InternalDisconnectNode(Source, True, False);
  27140. // Connect to new location.
  27141. InternalConnectNode(Source, Target, Self, Mode);
  27142. DoNodeMoved(Source);
  27143. Source := NewNode;
  27144. end;
  27145. end;
  27146. end;
  27147. end;
  27148. end
  27149. else
  27150. begin
  27151. // Difficult case: move node(s) to another tree.
  27152. // In opposition to node copying we ask only once if moving is allowed because
  27153. // we cannot take back a move once done.
  27154. if Target = TargetTree.FRoot then
  27155. Allowed := DoNodeMoving(Source, nil)
  27156. else
  27157. Allowed := DoNodeMoving(Source, Target);
  27158. if Allowed then
  27159. begin
  27160. Stream := TMemoryStream.Create;
  27161. try
  27162. // Write all nodes into a temporary stream depending on the ChildrenOnly flag.
  27163. if not ChildrenOnly then
  27164. WriteNode(Stream, Source)
  27165. else
  27166. begin
  27167. Source := Source.FirstChild;
  27168. while Assigned(Source) do
  27169. begin
  27170. WriteNode(Stream, Source);
  27171. Source := Source.NextSibling;
  27172. end;
  27173. end;
  27174. // Now load the serialized nodes into the target node (tree).
  27175. TargetTree.BeginUpdate;
  27176. try
  27177. Stream.Position := 0;
  27178. while Stream.Position < Stream.Size do
  27179. begin
  27180. NewNode := TargetTree.MakeNewNode;
  27181. InternalConnectNode(NewNode, Target, TargetTree, Mode);
  27182. TargetTree.InternalAddFromStream(Stream, VTTreeStreamVersion, NewNode);
  27183. DoNodeMoved(NewNode);
  27184. end;
  27185. finally
  27186. TargetTree.EndUpdate;
  27187. end;
  27188. finally
  27189. Stream.Free;
  27190. end;
  27191. // finally delete original nodes
  27192. BeginUpdate;
  27193. try
  27194. if ChildrenOnly then
  27195. DeleteChildren(Source)
  27196. else
  27197. DeleteNode(Source);
  27198. finally
  27199. EndUpdate;
  27200. end;
  27201. end;
  27202. end;
  27203. InvalidateCache;
  27204. if (FUpdateCount = 0) and Allowed then
  27205. begin
  27206. ValidateCache;
  27207. UpdateScrollBars(True);
  27208. Invalidate;
  27209. if TargetTree <> Self then
  27210. TargetTree.Invalidate;
  27211. end;
  27212. StructureChange(Source, crNodeMoved);
  27213. end;
  27214. end;
  27215. //----------------------------------------------------------------------------------------------------------------------
  27216. procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Target: TPoint;
  27217. PaintOptions: TVTInternalPaintOptions; PixelFormat: TPixelFormat);
  27218. // This is the core paint routine of the tree. It is responsible for maintaining the paint cycles per node as well
  27219. // as coordinating drawing of the various parts of the tree image.
  27220. // TargetCanvas is the canvas to which to draw the tree image. This is usually the tree window itself but could well
  27221. // be a bitmap or printer canvas.
  27222. // Window determines which part of the entire tree image to draw. The full size of the virtual image is determined
  27223. // by GetTreeRect.
  27224. // Target is the position in TargetCanvas where to draw the tree part specified by Window.
  27225. // PaintOptions determines what of the tree to draw. For different tasks usually different parts need to be drawn, with
  27226. // a full image in the window, selected only nodes for a drag image etc.
  27227. const
  27228. ImageKind: array[Boolean] of TVTImageKind = (ikNormal, ikSelected);
  27229. var
  27230. DrawSelectionRect,
  27231. UseBackground,
  27232. ShowImages,
  27233. ShowStateImages,
  27234. ShowCheckImages,
  27235. UseColumns,
  27236. IsMainColumn: Boolean;
  27237. VAlign,
  27238. IndentSize,
  27239. ButtonX,
  27240. ButtonY: Integer;
  27241. LineImage: TLineImage;
  27242. PaintInfo: TVTPaintInfo; // all necessary information about a node to pass to the paint routines
  27243. R, // the area of an entire node in its local coordinate
  27244. TargetRect, // the area of a node (part) in the target canvas
  27245. SelectionRect, // ordered rectangle used for drawing the selection focus rect
  27246. ClipRect: TRect; // area to which the canvas will be clipped when painting a node's content
  27247. NextColumn: TColumnIndex;
  27248. BaseOffset: Integer; // top position of the top node to draw given in absolute tree coordinates
  27249. NodeBitmap: TBitmap; // small buffer to draw flicker free
  27250. MaximumRight, // maximum horizontal target position
  27251. MaximumBottom: Integer; // maximum vertical target position
  27252. SelectLevel: Integer; // > 0 if current node is selected or child/grandchild etc. of a selected node
  27253. FirstColumn: TColumnIndex; // index of first column which is at least partially visible in the given window
  27254. MaxRight,
  27255. ColLeft,
  27256. ColRight: Integer;
  27257. SavedTargetDC: Integer;
  27258. PaintWidth: Integer;
  27259. CurrentNodeHeight: Integer;
  27260. lUseSelectedBkColor: Boolean; // determines if the dotted grid lines need to be painted in selection color of background color
  27261. CellIsTouchingClientRight: Boolean;
  27262. CellIsInLastColumn: Boolean;
  27263. ColumnIsFixed: Boolean;
  27264. begin
  27265. if not (tsPainting in FStates) then
  27266. begin
  27267. DoStateChange([tsPainting]);
  27268. try
  27269. DoBeforePaint(TargetCanvas);
  27270. if poUnbuffered in PaintOptions then
  27271. SavedTargetDC := SaveDC(TargetCanvas.Handle)
  27272. else
  27273. SavedTargetDC := 0;
  27274. // Prepare paint info structure.
  27275. ZeroMemory(@PaintInfo, SizeOf(PaintInfo));
  27276. PaintWidth := Window.Right - Window.Left;
  27277. if not (poUnbuffered in PaintOptions) then
  27278. begin
  27279. // Create small bitmaps and initialize default values.
  27280. // The bitmaps are used to paint one node at a time and to draw the result to the target (e.g. screen) in one step,
  27281. // to prevent flickering.
  27282. NodeBitmap := TBitmap.Create;
  27283. // For alpha blending we need the 32 bit pixel format. For other targets there might be a need for a certain
  27284. // pixel format (e.g. printing).
  27285. if MMXAvailable and ((FDrawSelectionMode = smBlendedRectangle) or (tsUseThemes in FStates) or
  27286. (toUseBlendedSelection in FOptions.PaintOptions)) then
  27287. NodeBitmap.PixelFormat := pf32Bit
  27288. else
  27289. NodeBitmap.PixelFormat := PixelFormat;
  27290. NodeBitmap.Width := PaintWidth;
  27291. // Make sure the buffer bitmap and target bitmap use the same transformation mode.
  27292. SetMapMode(NodeBitmap.Canvas.Handle, GetMapMode(TargetCanvas.Handle));
  27293. PaintInfo.Canvas := NodeBitmap.Canvas;
  27294. end
  27295. else
  27296. begin
  27297. PaintInfo.Canvas := TargetCanvas;
  27298. NodeBitmap := nil;
  27299. end;
  27300. // Lock the canvas to avoid that it gets freed on the way.
  27301. PaintInfo.Canvas.Lock;
  27302. try
  27303. // Prepare the current selection rectangle once. The corner points are absolute tree coordinates.
  27304. SelectionRect := OrderRect(FNewSelRect);
  27305. DrawSelectionRect := IsMouseSelecting and not IsRectEmpty(SelectionRect) and (GetKeyState(VK_LBUTTON) < 0);
  27306. // R represents an entire node (all columns), but is a bit unprecise when it comes to
  27307. // trees without any column defined, because FRangeX only represents the maximum width of all
  27308. // nodes in the client area (not all defined nodes). There might be, however, wider nodes somewhere. Without full
  27309. // validation I cannot better determine the width, though. By using at least the control's width it is ensured
  27310. // that the tree is fully displayed on screen.
  27311. R := Rect(0, 0, Max(FRangeX, ClientWidth), 0);
  27312. // For quick checks some intermediate variables are used.
  27313. UseBackground := (toShowBackground in FOptions.FPaintOptions) and (FBackground.Graphic is TBitmap) and
  27314. (poBackground in PaintOptions);
  27315. ShowImages := Assigned(FImages);
  27316. ShowStateImages := Assigned(FStateImages);
  27317. ShowCheckImages := Assigned(FCheckImages) and (toCheckSupport in FOptions.FMiscOptions);
  27318. UseColumns := FHeader.UseColumns;
  27319. // Adjust paint options to tree settings. Hide selection if told so or the tree is unfocused.
  27320. if (toAlwaysHideSelection in FOptions.FPaintOptions) or
  27321. (not Focused and (toHideSelection in FOptions.FPaintOptions)) then
  27322. Exclude(PaintOptions, poDrawSelection);
  27323. if toHideFocusRect in FOptions.FPaintOptions then
  27324. Exclude(PaintOptions, poDrawFocusRect);
  27325. // Determine node to start drawing with.
  27326. BaseOffset := 0;
  27327. PaintInfo.Node := GetNodeAt(0, Window.Top, False, BaseOffset);
  27328. if PaintInfo.Node = nil then
  27329. BaseOffset := Window.Top;
  27330. // Transform selection rectangle into node bitmap coordinates.
  27331. if DrawSelectionRect then
  27332. OffsetRect(SelectionRect, 0, -BaseOffset);
  27333. // The target rectangle holds the coordinates of the exact area to blit in target canvas coordinates.
  27334. // It is usually smaller than an entire node and wanders while the paint loop advances.
  27335. MaximumRight := Target.X + (Window.Right - Window.Left);
  27336. MaximumBottom := Target.Y + (Window.Bottom - Window.Top);
  27337. TargetRect := Rect(Target.X, Target.Y - (Window.Top - BaseOffset), MaximumRight, 0);
  27338. TargetRect.Bottom := TargetRect.Top;
  27339. TargetCanvas.Font := Self.Font;
  27340. // This marker gets the index of the first column which is visible in the given window.
  27341. // This is needed for column based background colors.
  27342. FirstColumn := InvalidColumn;
  27343. if Assigned(PaintInfo.Node) then
  27344. begin
  27345. ButtonX := Round((Integer(FIndent) - FPlusBM.Width) / 2) + 1;
  27346. // ----- main node paint loop
  27347. while Assigned(PaintInfo.Node) do
  27348. begin
  27349. // Determine LineImage, SelectionLevel and IndentSize
  27350. SelectLevel := DetermineLineImageAndSelectLevel(PaintInfo.Node, LineImage);
  27351. IndentSize := Length(LineImage);
  27352. if not (toFixedIndent in FOptions.FPaintOptions) then
  27353. ButtonX := (IndentSize - 1) * Integer(FIndent) + Round((Integer(FIndent) - FPlusBM.Width) / 2) + 1;
  27354. // Initialize node if not already done.
  27355. if not (vsInitialized in PaintInfo.Node.States) then
  27356. InitNode(PaintInfo.Node);
  27357. if (vsSelected in PaintInfo.Node.States) and not (toChildrenAbove in FOptions.FPaintOptions) then
  27358. Inc(SelectLevel);
  27359. // Ensure the node's height is determined.
  27360. MeasureItemHeight(PaintInfo.Canvas, PaintInfo.Node);
  27361. // Adjust the brush origin for dotted lines depending on the current source position.
  27362. // It is applied some lines later, as the canvas might get reallocated, when changing the node bitmap.
  27363. PaintInfo.BrushOrigin := Point(Window.Left and 1, BaseOffset and 1);
  27364. Inc(BaseOffset, PaintInfo.Node.NodeHeight);
  27365. TargetRect.Bottom := TargetRect.Top + PaintInfo.Node.NodeHeight;
  27366. // If poSelectedOnly is active then do the following stuff only for selected nodes or nodes
  27367. // which are children of selected nodes.
  27368. if (SelectLevel > 0) or not (poSelectedOnly in PaintOptions) then
  27369. begin
  27370. if not (poUnbuffered in PaintOptions) then
  27371. begin
  27372. // Adjust height of temporary node bitmap.
  27373. with NodeBitmap do
  27374. begin
  27375. if Height <> PaintInfo.Node.NodeHeight then
  27376. begin
  27377. // Avoid that the VCL copies the bitmap while changing its height.
  27378. Height := 0;
  27379. Height := PaintInfo.Node.NodeHeight;
  27380. SetCanvasOrigin(Canvas, Window.Left, 0);
  27381. end;
  27382. end;
  27383. end
  27384. else
  27385. begin
  27386. SetCanvasOrigin(PaintInfo.Canvas, -TargetRect.Left + Window.Left, -TargetRect.Top);
  27387. ClipCanvas(PaintInfo.Canvas, Rect(TargetRect.Left, TargetRect.Top, TargetRect.Right,
  27388. Min(TargetRect.Bottom, MaximumBottom)));
  27389. end;
  27390. // Set the origin of the canvas' brush. This depends on the node heights.
  27391. with PaintInfo do
  27392. SetBrushOrigin(Canvas, BrushOrigin.X, BrushOrigin.Y);
  27393. CurrentNodeHeight := PaintInfo.Node.NodeHeight;
  27394. R.Bottom := CurrentNodeHeight;
  27395. CalculateVerticalAlignments(ShowImages, ShowStateImages, PaintInfo.Node, VAlign, ButtonY);
  27396. // Let application decide whether the node should normally be drawn or by the application itself.
  27397. if not DoBeforeItemPaint(PaintInfo.Canvas, PaintInfo.Node, R) then
  27398. begin
  27399. // Init paint options for the background painting.
  27400. PaintInfo.PaintOptions := PaintOptions;
  27401. // The node background can contain a single color, a bitmap or can be drawn by the application.
  27402. ClearNodeBackground(PaintInfo, UseBackground, True, Rect(Window.Left, TargetRect.Top, Window.Right,
  27403. TargetRect.Bottom));
  27404. // Prepare column, position and node clipping rectangle.
  27405. PaintInfo.CellRect := R;
  27406. if UseColumns then
  27407. InitializeFirstColumnValues(PaintInfo);
  27408. // Now go through all visible columns (there's still one run if columns aren't used).
  27409. with FHeader.FColumns do
  27410. begin
  27411. while ((PaintInfo.Column > InvalidColumn) or not UseColumns)
  27412. and (PaintInfo.CellRect.Left < Window.Right) do
  27413. begin
  27414. if UseColumns then
  27415. begin
  27416. PaintInfo.Column := FPositionToIndex[PaintInfo.Position];
  27417. if FirstColumn = InvalidColumn then
  27418. FirstColumn := PaintInfo.Column;
  27419. PaintInfo.BidiMode := Items[PaintInfo.Column].FBiDiMode;
  27420. PaintInfo.Alignment := Items[PaintInfo.Column].FAlignment;
  27421. end
  27422. else
  27423. begin
  27424. PaintInfo.Column := NoColumn;
  27425. PaintInfo.BidiMode := BidiMode;
  27426. PaintInfo.Alignment := FAlignment;
  27427. end;
  27428. PaintInfo.PaintOptions := PaintOptions;
  27429. with PaintInfo do
  27430. begin
  27431. if (tsEditing in FStates) and (Node = FFocusedNode) and
  27432. ((Column = FEditColumn) or not UseColumns) then
  27433. Exclude(PaintOptions, poDrawSelection);
  27434. if not UseColumns or
  27435. ((vsSelected in Node.States) and (toFullRowSelect in FOptions.FSelectionOptions) and
  27436. (poDrawSelection in PaintOptions)) or
  27437. (coParentColor in Items[PaintInfo.Column].Options) then
  27438. Exclude(PaintOptions, poColumnColor);
  27439. end;
  27440. IsMainColumn := PaintInfo.Column = FHeader.MainColumn;
  27441. // Consider bidi mode here. In RTL context means left alignment actually right alignment and vice versa.
  27442. if PaintInfo.BidiMode <> bdLeftToRight then
  27443. ChangeBiDiModeAlignment(PaintInfo.Alignment);
  27444. // Paint the current cell if it is marked as being visible or columns aren't used and
  27445. // if this cell belongs to the main column if only the main column should be drawn.
  27446. if (not UseColumns or (coVisible in Items[PaintInfo.Column].FOptions)) and
  27447. (not (poMainOnly in PaintOptions) or IsMainColumn) then
  27448. begin
  27449. AdjustPaintCellRect(PaintInfo, NextColumn);
  27450. // Paint the cell only if it is in the current window.
  27451. if PaintInfo.CellRect.Right > Window.Left then
  27452. begin
  27453. with PaintInfo do
  27454. begin
  27455. // Fill in remaining values in the paint info structure.
  27456. NodeWidth := DoGetNodeWidth(Node, Column, Canvas);
  27457. // Not the entire cell is covered by text. Hence we need a running rectangle to follow up.
  27458. ContentRect := CellRect;
  27459. // Set up the distance from column border (margin).
  27460. if BidiMode <> bdLeftToRight then
  27461. Dec(ContentRect.Right, FMargin)
  27462. else
  27463. Inc(ContentRect.Left, FMargin);
  27464. if ShowCheckImages and IsMainColumn then
  27465. begin
  27466. ImageInfo[iiCheck].Index := GetCheckImage(Node);
  27467. if ImageInfo[iiCheck].Index > -1 then
  27468. begin
  27469. AdjustImageBorder(FCheckImages, BidiMode, VAlign, ContentRect, ImageInfo[iiCheck]);
  27470. ImageInfo[iiCheck].Ghosted := False;
  27471. end;
  27472. end
  27473. else
  27474. ImageInfo[iiCheck].Index := -1;
  27475. if ShowStateImages then
  27476. begin
  27477. GetImageIndex(PaintInfo, ikState, iiState, FStateImages);
  27478. if ImageInfo[iiState].Index > -1 then
  27479. AdjustImageBorder(FStateImages, BidiMode, VAlign, ContentRect, ImageInfo[iiState]);
  27480. end
  27481. else
  27482. ImageInfo[iiState].Index := -1;
  27483. if ShowImages then
  27484. begin
  27485. GetImageIndex(PaintInfo, ImageKind[vsSelected in Node.States], iiNormal, FImages);
  27486. if ImageInfo[iiNormal].Index > -1 then
  27487. AdjustImageBorder(ImageInfo[iiNormal].Images, BidiMode, VAlign, ContentRect, ImageInfo[iiNormal]);
  27488. end
  27489. else
  27490. ImageInfo[iiNormal].Index := -1;
  27491. // Take the space for the tree lines into account.
  27492. if IsMainColumn then
  27493. AdjustCoordinatesByIndent(PaintInfo, IfThen(toFixedIndent in FOptions.FPaintOptions, 1, IndentSize));
  27494. if UseColumns then
  27495. begin
  27496. ClipRect := CellRect;
  27497. if poUnbuffered in PaintOptions then
  27498. begin
  27499. ClipRect.Left := Max(ClipRect.Left, Window.Left);
  27500. ClipRect.Right := Min(ClipRect.Right, Window.Right);
  27501. ClipRect.Top := Max(ClipRect.Top, Window.Top - (BaseOffset - CurrentNodeHeight));
  27502. ClipRect.Bottom := ClipRect.Bottom - Max(TargetRect.Bottom - MaximumBottom, 0);
  27503. end;
  27504. ClipCanvas(Canvas, ClipRect);
  27505. end;
  27506. // Paint the horizontal grid line.
  27507. if (poGridLines in PaintOptions) and (toShowHorzGridLines in FOptions.FPaintOptions) then
  27508. begin
  27509. Canvas.Font.Color := FColors.GridLineColor;
  27510. if IsMainColumn and (FLineMode = lmBands) then
  27511. begin
  27512. if BidiMode = bdLeftToRight then
  27513. begin
  27514. DrawDottedHLine(PaintInfo, CellRect.Left + IfThen(toFixedIndent in FOptions.FPaintOptions, 1, IndentSize) * Integer(FIndent), CellRect.Right - 1,
  27515. CellRect.Bottom - 1);
  27516. end
  27517. else
  27518. begin
  27519. DrawDottedHLine(PaintInfo, CellRect.Left, CellRect.Right - IfThen(toFixedIndent in FOptions.FPaintOptions, 1, IndentSize) * Integer(FIndent) - 1,
  27520. CellRect.Bottom - 1);
  27521. end;
  27522. end
  27523. else
  27524. DrawDottedHLine(PaintInfo, CellRect.Left, CellRect.Right, CellRect.Bottom - 1);
  27525. Dec(CellRect.Bottom);
  27526. Dec(ContentRect.Bottom);
  27527. end;
  27528. if UseColumns then
  27529. begin
  27530. // Paint vertical grid line.
  27531. if (poGridLines in PaintOptions) and (toShowVertGridLines in FOptions.FPaintOptions) then
  27532. begin
  27533. // These variables and the nested if conditions shall make the logic
  27534. // easier to understand.
  27535. CellIsTouchingClientRight := PaintInfo.CellRect.Right = Window.Right;
  27536. CellIsInLastColumn := Position = TColumnPosition(Count - 1);
  27537. ColumnIsFixed := coFixed in FHeader.FColumns[Column].Options;
  27538. // Don't draw if this is the last column and the header is in autosize mode.
  27539. if not ((hoAutoResize in FHeader.FOptions) and CellIsInLastColumn) then
  27540. begin
  27541. // We have to take spanned cells into account which we determine
  27542. // by checking if CellRect.Right equals the Window.Right.
  27543. // But since the PaintTree procedure is called twice in
  27544. // TBaseVirtualTree.Paint (i.e. for fixed columns and other columns.
  27545. // CellIsTouchingClientRight does not work for fixed columns.)
  27546. // we have to paint fixed column grid line anyway.
  27547. if not CellIsTouchingClientRight or ColumnIsFixed then
  27548. begin
  27549. if (BidiMode = bdLeftToRight) or not ColumnIsEmpty(Node, Column) then
  27550. begin
  27551. Canvas.Font.Color := FColors.GridLineColor;
  27552. lUseSelectedBkColor := (poDrawSelection in PaintOptions) and (toFullRowSelect in FOptions.FSelectionOptions) and
  27553. (vsSelected in Node.States) and not (toUseBlendedSelection in FOptions.PaintOptions) and not
  27554. (tsUseExplorerTheme in FStates);
  27555. DrawDottedVLine(PaintInfo, CellRect.Top, CellRect.Bottom, CellRect.Right - 1, lUseSelectedBkColor);
  27556. end;
  27557. Dec(CellRect.Right);
  27558. Dec(ContentRect.Right);
  27559. end;
  27560. end;
  27561. end;
  27562. end;
  27563. // Prepare background and focus rect for the current cell.
  27564. PrepareCell(PaintInfo, Window.Left, PaintWidth);
  27565. // Some parts are only drawn for the main column.
  27566. if IsMainColumn then
  27567. begin
  27568. if (toShowTreeLines in FOptions.FPaintOptions) and
  27569. (not (toHideTreeLinesIfThemed in FOptions.FPaintOptions) or
  27570. not (tsUseThemes in FStates)) then
  27571. PaintTreeLines(PaintInfo, VAlign, IfThen(toFixedIndent in FOptions.FPaintOptions, 1,
  27572. IndentSize), LineImage);
  27573. // Show node button if allowed, if there child nodes and at least one of the child
  27574. // nodes is visible or auto button hiding is disabled.
  27575. if (toShowButtons in FOptions.FPaintOptions) and (vsHasChildren in Node.States) and
  27576. not ((vsAllChildrenHidden in Node.States) and
  27577. (toAutoHideButtons in TreeOptions.FAutoOptions)) then
  27578. PaintNodeButton(Canvas, Node, Column, CellRect, ButtonX, ButtonY, BidiMode);
  27579. if ImageInfo[iiCheck].Index > -1 then
  27580. PaintCheckImage(Canvas, PaintInfo.ImageInfo[iiCheck], vsSelected in PaintInfo.Node.States);
  27581. end;
  27582. if ImageInfo[iiState].Index > -1 then
  27583. PaintImage(PaintInfo, iiState, False);
  27584. if ImageInfo[iiNormal].Index > -1 then
  27585. PaintImage(PaintInfo, iiNormal, True);
  27586. // Now let descendants or applications draw whatever they want,
  27587. // but don't draw the node if it is currently being edited.
  27588. if not ((tsEditing in FStates) and (Node = FFocusedNode) and
  27589. ((Column = FEditColumn) or not UseColumns)) then
  27590. DoPaintNode(PaintInfo);
  27591. DoAfterCellPaint(Canvas, Node, Column, CellRect);
  27592. end;
  27593. end;
  27594. // leave after first run if columns aren't used
  27595. if not UseColumns then
  27596. Break;
  27597. end
  27598. else
  27599. NextColumn := GetNextVisibleColumn(PaintInfo.Column);
  27600. SelectClipRgn(PaintInfo.Canvas.Handle, 0);
  27601. // Stop column loop if there are no further columns in the given window.
  27602. if (PaintInfo.CellRect.Left >= Window.Right) or (NextColumn = InvalidColumn) then
  27603. Break;
  27604. // Move on to next column which might not be the one immediately following the current one
  27605. // because of auto span feature.
  27606. PaintInfo.Position := Items[NextColumn].Position;
  27607. // Move clip rectangle and continue.
  27608. if coVisible in Items[NextColumn].FOptions then
  27609. with PaintInfo do
  27610. begin
  27611. Items[NextColumn].GetAbsoluteBounds(CellRect.Left, CellRect.Right);
  27612. CellRect.Bottom := Node.NodeHeight;
  27613. ContentRect.Bottom := Node.NodeHeight;
  27614. end;
  27615. end;
  27616. end;
  27617. // This node is finished, notify descendants/application.
  27618. with PaintInfo do
  27619. begin
  27620. DoAfterItemPaint(Canvas, Node, R);
  27621. // Final touch for this node: mark it if it is the current drop target node.
  27622. if (Node = FDropTargetNode) and (toShowDropmark in FOptions.FPaintOptions) and
  27623. (poDrawDropMark in PaintOptions) then
  27624. DoPaintDropMark(Canvas, Node, R);
  27625. end;
  27626. end;
  27627. with PaintInfo.Canvas do
  27628. begin
  27629. if DrawSelectionRect then
  27630. begin
  27631. PaintSelectionRectangle(PaintInfo.Canvas, Window.Left, SelectionRect, Rect(0, 0, PaintWidth,
  27632. CurrentNodeHeight));
  27633. end;
  27634. // Put the constructed node image onto the target canvas.
  27635. if not (poUnbuffered in PaintOptions) then
  27636. with TWithSafeRect(TargetRect), NodeBitmap do
  27637. BitBlt(TargetCanvas.Handle, Left, Top, Width, Height, Canvas.Handle, Window.Left, 0, SRCCOPY);
  27638. end;
  27639. end;
  27640. Inc(TargetRect.Top, PaintInfo.Node.NodeHeight);
  27641. if TargetRect.Top >= MaximumBottom then
  27642. Break;
  27643. // Keep selection rectangle coordinates in sync.
  27644. if DrawSelectionRect then
  27645. OffsetRect(SelectionRect, 0, -PaintInfo.Node.NodeHeight);
  27646. // Advance to next visible node.
  27647. PaintInfo.Node := GetNextVisible(PaintInfo.Node, True);
  27648. end;
  27649. end;
  27650. // Erase rest of window not covered by a node.
  27651. if TargetRect.Top < MaximumBottom then
  27652. begin
  27653. // Keep the horizontal target position to determine the selection rectangle offset later (if necessary).
  27654. BaseOffset := Target.X;
  27655. Target := TargetRect.TopLeft;
  27656. R := Rect(TargetRect.Left, 0, TargetRect.Left, MaximumBottom - Target.Y);
  27657. TargetRect := Rect(0, 0, MaximumRight - Target.X, MaximumBottom - Target.Y);
  27658. if not (poUnbuffered in PaintOptions) then
  27659. begin
  27660. // Avoid unnecessary copying of bitmap content. This will destroy the DC handle too.
  27661. NodeBitmap.Height := 0;
  27662. NodeBitmap.PixelFormat := pf32Bit;
  27663. NodeBitmap.Width := TargetRect.Right - TargetRect.Left;
  27664. NodeBitmap.Height := TargetRect.Bottom - TargetRect.Top;
  27665. end;
  27666. // Call back application/descendants whether they want to erase this area.
  27667. if not DoPaintBackground(PaintInfo.Canvas, TargetRect) then
  27668. begin
  27669. if UseBackground then
  27670. begin
  27671. SetCanvasOrigin(PaintInfo.Canvas, 0, 0);
  27672. if toStaticBackground in TreeOptions.PaintOptions then
  27673. StaticBackground(FBackground.Bitmap, PaintInfo.Canvas, Target, TargetRect)
  27674. else
  27675. TileBackground(FBackground.Bitmap, PaintInfo.Canvas, Target, TargetRect);
  27676. end
  27677. else
  27678. begin
  27679. // Consider here also colors of the columns.
  27680. SetCanvasOrigin(PaintInfo.Canvas, Target.X, 0); // This line caused issue #313 when it was placed above the if-statement
  27681. if UseColumns then
  27682. begin
  27683. with FHeader.FColumns do
  27684. begin
  27685. // If there is no content in the tree then the first column has not yet been determined.
  27686. if FirstColumn = InvalidColumn then
  27687. begin
  27688. FirstColumn := GetFirstVisibleColumn;
  27689. repeat
  27690. if FirstColumn <> InvalidColumn then
  27691. begin
  27692. R.Left := Items[FirstColumn].Left;
  27693. R.Right := R.Left + Items[FirstColumn].FWidth;
  27694. if R.Right > TargetRect.Left then
  27695. Break;
  27696. FirstColumn := GetNextVisibleColumn(FirstColumn);
  27697. end;
  27698. until FirstColumn = InvalidColumn;
  27699. end
  27700. else
  27701. begin
  27702. R.Left := Items[FirstColumn].Left;
  27703. R.Right := R.Left + Items[FirstColumn].FWidth;
  27704. end;
  27705. // Initialize MaxRight.
  27706. MaxRight := Target.X - 1;
  27707. PaintInfo.Canvas.Font.Color := FColors.GridLineColor;
  27708. while (FirstColumn <> InvalidColumn) and (MaxRight < TargetRect.Right + Target.X) do
  27709. begin
  27710. // Determine left and right coordinate of the current column
  27711. ColLeft := Items[FirstColumn].Left;
  27712. ColRight := (ColLeft + Items[FirstColumn].FWidth);
  27713. // Check wether this column needs to be painted at all.
  27714. if (ColRight >= MaxRight) then
  27715. begin
  27716. R.Left := MaxRight; // Continue where we left off
  27717. R.Right := ColRight; // Paint to the right of the column
  27718. MaxRight := ColRight; // And record were to start the next column.
  27719. if (poGridLines in PaintOptions) and
  27720. (toFullVertGridLines in FOptions.FPaintOptions) and
  27721. (toShowVertGridLines in FOptions.FPaintOptions) and
  27722. (not (hoAutoResize in FHeader.FOptions) or (Cardinal(FirstColumn) < TColumnPosition(Count - 1))) then
  27723. begin
  27724. DrawDottedVLine(PaintInfo, R.Top, R.Bottom, R.Right - 1);
  27725. Dec(R.Right);
  27726. end;
  27727. if not (coParentColor in Items[FirstColumn].FOptions) then
  27728. PaintInfo.Canvas.Brush.Color := Items[FirstColumn].FColor
  27729. else
  27730. PaintInfo.Canvas.Brush.Color := FColors.BackGroundColor;
  27731. PaintInfo.Canvas.FillRect(R);
  27732. end;
  27733. FirstColumn := GetNextVisibleColumn(FirstColumn);
  27734. end;
  27735. // Erase also the part of the tree not covert by a column.
  27736. if R.Right < TargetRect.Right + Target.X then
  27737. begin
  27738. R.Left := R.Right;
  27739. R.Right := TargetRect.Right + Target.X;
  27740. // Prevent erasing the last vertical grid line.
  27741. if (poGridLines in PaintOptions) and
  27742. (toFullVertGridLines in FOptions.FPaintOptions) and (toShowVertGridLines in FOptions.FPaintOptions) and
  27743. (not (hoAutoResize in FHeader.FOptions)) then
  27744. Inc(R.Left);
  27745. PaintInfo.Canvas.Brush.Color := FColors.BackGroundColor;
  27746. PaintInfo.Canvas.FillRect(R);
  27747. end;
  27748. end;
  27749. SetCanvasOrigin(PaintInfo.Canvas, 0, 0);
  27750. end
  27751. else
  27752. begin
  27753. // No columns nor bitmap background. Simply erase it with the tree color.
  27754. SetCanvasOrigin(PaintInfo.Canvas, 0, 0);
  27755. PaintInfo.Canvas.Brush.Color := FColors.BackGroundColor;
  27756. PaintInfo.Canvas.FillRect(TargetRect);
  27757. end;
  27758. end;
  27759. end;
  27760. SetCanvasOrigin(PaintInfo.Canvas, 0, 0);
  27761. if DrawSelectionRect then
  27762. begin
  27763. R := OrderRect(FNewSelRect);
  27764. // Remap the selection rectangle to the current window of the tree.
  27765. // Since Target has been used for other tasks BaseOffset got the left extent of the target position here.
  27766. OffsetRect(R, -Target.X + BaseOffset - Window.Left, -Target.Y + FOffsetY);
  27767. SetBrushOrigin(PaintInfo.Canvas, 0, Target.X and 1);
  27768. PaintSelectionRectangle(PaintInfo.Canvas, 0, R, TargetRect);
  27769. end;
  27770. if not (poUnBuffered in PaintOptions) then
  27771. with Target, NodeBitmap do
  27772. BitBlt(TargetCanvas.Handle, X, Y, Width, Height, Canvas.Handle, 0, 0, SRCCOPY);
  27773. end;
  27774. finally
  27775. PaintInfo.Canvas.Unlock;
  27776. if poUnbuffered in PaintOptions then
  27777. RestoreDC(TargetCanvas.Handle, SavedTargetDC)
  27778. else
  27779. NodeBitmap.Free;
  27780. end;
  27781. if (ChildCount[nil] = 0) and (FEmptyListMessage <> '') then
  27782. begin
  27783. // output a message if no items are to display
  27784. Canvas.Font := Self.Font;
  27785. SetBkMode(TargetCanvas.Handle, TRANSPARENT);
  27786. R.Left := OffSetX + 2;
  27787. R.Top := 2;
  27788. R.Right := R.Left + Width - 2;
  27789. R.Bottom := Height -2;
  27790. TargetCanvas.Font.Color := clGrayText;
  27791. {$if CompilerVersion >= 20}
  27792. TargetCanvas.TextRect(R, FEmptyListMessage, [tfNoClip, tfLeft, tfWordBreak]);
  27793. {$else}
  27794. TextOutW(TargetCanvas.Handle, 2 - Window.Left, 2 - Window.Top, PWideChar(FEmptyListMessage), Length(FEmptyListMessage));
  27795. {$ifend}
  27796. end;
  27797. DoAfterPaint(TargetCanvas);
  27798. finally
  27799. DoStateChange([], [tsPainting]);
  27800. end;
  27801. end;
  27802. end;
  27803. //----------------------------------------------------------------------------------------------------------------------
  27804. function TBaseVirtualTree.PasteFromClipboard: Boolean;
  27805. // Reads what is currently on the clipboard into the tree (if the format is supported).
  27806. // Note: If the application wants to have text or special formats to be inserted then it must implement
  27807. // its own code (OLE). Here only the native tree format is accepted.
  27808. var
  27809. Data: IDataObject;
  27810. Source: TBaseVirtualTree;
  27811. begin
  27812. Result := False;
  27813. if not (toReadOnly in FOptions.FMiscOptions) then
  27814. begin
  27815. if OleGetClipboard(Data) <> S_OK then
  27816. ShowError(SClipboardFailed, hcTFClipboardFailed)
  27817. else
  27818. begin
  27819. // Try to get the source tree of the operation to optimize the operation.
  27820. Source := GetTreeFromDataObject(Data);
  27821. Result := ProcessOLEData(Source, Data, FFocusedNode, FDefaultPasteMode, Assigned(Source) and
  27822. (tsCutPending in Source.FStates));
  27823. if Assigned(Source) then
  27824. begin
  27825. if Source <> Self then
  27826. Source.FinishCutOrCopy
  27827. else
  27828. DoStateChange([], [tsCutPending]);
  27829. end;
  27830. end;
  27831. end;
  27832. end;
  27833. //----------------------------------------------------------------------------------------------------------------------
  27834. procedure TBaseVirtualTree.PrepareDragImage(HotSpot: TPoint; const DataObject: IDataObject);
  27835. // Initiates an image drag operation. HotSpot is the position of the mouse in client coordinates.
  27836. var
  27837. PaintOptions: TVTInternalPaintOptions;
  27838. TreeRect,
  27839. PaintRect: TRect;
  27840. LocalSpot,
  27841. ImagePos,
  27842. PaintTarget: TPoint;
  27843. Image: TBitmap;
  27844. begin
  27845. if CanShowDragImage then
  27846. begin
  27847. // Determine the drag rectangle which is a square around the hot spot. Operate in virtual tree space.
  27848. LocalSpot := HotSpot;
  27849. Dec(LocalSpot.X, -FEffectiveOffsetX);
  27850. Dec(LocalSpot.Y, FOffsetY);
  27851. TreeRect := Rect(LocalSpot.X - FDragWidth div 2, LocalSpot.Y - FDragHeight div 2, LocalSpot.X + FDragWidth div 2,
  27852. LocalSpot.Y + FDragHeight div 2);
  27853. // Check that we have a valid rectangle.
  27854. PaintRect := TreeRect;
  27855. with TWithSafeRect(TreeRect) do
  27856. begin
  27857. if Left < 0 then
  27858. begin
  27859. PaintTarget.X := -Left;
  27860. PaintRect.Left := 0;
  27861. end
  27862. else
  27863. PaintTarget.X := 0;
  27864. if Top < 0 then
  27865. begin
  27866. PaintTarget.Y := -Top;
  27867. PaintRect.Top := 0;
  27868. end
  27869. else
  27870. PaintTarget.Y := 0;
  27871. end;
  27872. Image := TBitmap.Create;
  27873. with Image do
  27874. try
  27875. PixelFormat := pf32Bit;
  27876. Width := TreeRect.Right - TreeRect.Left;
  27877. Height := TreeRect.Bottom - TreeRect.Top;
  27878. // Erase the entire image with the color key value, for the case not everything
  27879. // in the image is covered by the tree image.
  27880. Canvas.Brush.Color := FColors.BackGroundColor;
  27881. Canvas.FillRect(Rect(0, 0, Width, Height));
  27882. PaintOptions := [poDrawSelection, poSelectedOnly];
  27883. if FDragImageKind = diMainColumnOnly then
  27884. Include(PaintOptions, poMainOnly);
  27885. PaintTree(Image.Canvas, PaintRect, PaintTarget, PaintOptions);
  27886. // Once we have got the drag image we can convert all necessary coordinates into screen space.
  27887. OffsetRect(TreeRect, -FEffectiveOffsetX, FOffsetY);
  27888. ImagePos := ClientToScreen(TreeRect.TopLeft);
  27889. HotSpot := ClientToScreen(HotSpot);
  27890. FDragImage.ColorKey := FColors.BackGroundColor;
  27891. FDragImage.PrepareDrag(Image, ImagePos, HotSpot, DataObject);
  27892. finally
  27893. Image.Free;
  27894. end;
  27895. end;
  27896. end;
  27897. //----------------------------------------------------------------------------------------------------------------------
  27898. procedure TBaseVirtualTree.Print(Printer: TPrinter; PrintHeader: Boolean);
  27899. var
  27900. SaveTreeFont: TFont; // Remembers the tree's current font.
  27901. SaveHeaderFont: TFont; // Remembers the header's current font.
  27902. ImgRect, // Describes the dimensions of Image.
  27903. TreeRect, // The total VTree dimensions.
  27904. DestRect, // Dimensions of PrinterImage.
  27905. SrcRect: TRect; // Clip dimensions from Image -> PrinterImage
  27906. P: TPoint; // Used by PaintTree.
  27907. Options: TVTInternalPaintOptions; // Used by PaintTree.
  27908. Image, // Complete Tree is drawn to this image.
  27909. PrinterImage: TBitmap; // This is the image that gets printed.
  27910. SaveColor: TColor; // Remembers the VTree Color.
  27911. pTxtHeight, // Height of font in the TPrinter.Canvas
  27912. vTxtHeight, // Height of font in the VTree Canvas
  27913. vPageWidth,
  27914. vPageHeight, // Printer height in VTree resolution
  27915. xPageNum, yPageNum, // # of pages (except the occasional last one)
  27916. xPage, yPage: Integer; // Loop counter
  27917. Scale: Extended; // Scale factor between Printer Canvas and VTree Canvas
  27918. LogFont: TLogFont;
  27919. begin
  27920. if Assigned(Printer) then
  27921. begin
  27922. BeginUpdate;
  27923. // Grid lines are the only parts which are desirable when printing.
  27924. Options := [poGridLines];
  27925. // Remember the tree font.
  27926. SaveTreeFont := TFont.Create;
  27927. SaveTreeFont.Assign(Font);
  27928. // Create a new font for printing which does not use clear type output (but is antialiased, if possible)
  27929. // and which has the highest possible quality.
  27930. GetObject(Font.Handle, SizeOf(TLogFont), @LogFont);
  27931. LogFont.lfQuality := ANTIALIASED_QUALITY;
  27932. Font.Handle := CreateFontIndirect(LogFont);
  27933. // Create an image that will hold the complete VTree
  27934. Image := TBitmap.Create;
  27935. Image.PixelFormat := pf32Bit;
  27936. PrinterImage := nil;
  27937. try
  27938. TreeRect := GetTreeRect;
  27939. Image.Width := TreeRect.Right - TreeRect.Left;
  27940. P := Point(0, 0);
  27941. if (hoVisible in FHeader.Options) and PrintHeader then
  27942. begin
  27943. Inc(TreeRect.Bottom, FHeader.Height);
  27944. Inc(P.Y, FHeader.Height);
  27945. end;
  27946. Image.Height := TreeRect.Bottom - TreeRect.Top;
  27947. ImgRect.Left := 0;
  27948. ImgRect.Top := 0;
  27949. ImgRect.Right := Image.Width;
  27950. // Force the background to white color during the rendering.
  27951. SaveColor := FColors.BackGroundColor;
  27952. Color := clWhite;
  27953. // Print header if it is visible.
  27954. if (hoVisible in FHeader.Options) and PrintHeader then
  27955. begin
  27956. SaveHeaderFont := TFont.Create;
  27957. try
  27958. SaveHeaderFont.Assign(FHeader.Font);
  27959. // Create a new font for printing which does not use clear type output (but is antialiased, if possible)
  27960. // and which has the highest possible quality.
  27961. GetObject(FHeader.Font.Handle, SizeOf(TLogFont), @LogFont);
  27962. LogFont.lfQuality := ANTIALIASED_QUALITY;
  27963. FHeader.Font.Handle := CreateFontIndirect(LogFont);
  27964. ImgRect.Bottom := FHeader.Height;
  27965. FHeader.FColumns.PaintHeader(Image.Canvas.Handle, ImgRect, 0);
  27966. FHeader.Font := SaveHeaderFont;
  27967. finally
  27968. SaveHeaderFont.Free;
  27969. end;
  27970. end;
  27971. // The image's height is already adjusted for the header if it is visible.
  27972. ImgRect.Bottom := Image.Height;
  27973. PaintTree(Image.Canvas, ImgRect, P, Options, pf32Bit);
  27974. Color := SaveColor;
  27975. // Activate the printer
  27976. Printer.BeginDoc;
  27977. Printer.Canvas.Font := Font;
  27978. // Now we can calculate the scaling :
  27979. pTxtHeight := Printer.Canvas.TextHeight('Tj');
  27980. vTxtHeight := Canvas.TextHeight('Tj');
  27981. Scale := pTxtHeight / vTxtHeight;
  27982. // Create an Image that has the same dimensions as the printer canvas but
  27983. // scaled to the VTree resolution:
  27984. PrinterImage := TBitmap.Create;
  27985. vPageHeight := Round(Printer.PageHeight / Scale);
  27986. vPageWidth := Round(Printer.PageWidth / Scale);
  27987. // We do a minumum of one page.
  27988. xPageNum := Trunc(Image.Width / vPageWidth);
  27989. yPageNum := Trunc(Image.Height / vPageHeight);
  27990. PrinterImage.Width := vPageWidth;
  27991. PrinterImage.Height := vPageHeight;
  27992. // Split vertically:
  27993. for yPage := 0 to yPageNum do
  27994. begin
  27995. DestRect.Left := 0;
  27996. DestRect.Top := 0;
  27997. DestRect.Right := PrinterImage.Width;
  27998. DestRect.Bottom := PrinterImage.Height;
  27999. // Split horizontally:
  28000. for xPage := 0 to xPageNum do
  28001. begin
  28002. SrcRect.Left := vPageWidth * xPage;
  28003. SrcRect.Top := vPageHeight * yPage;
  28004. SrcRect.Right := vPageWidth * xPage + PrinterImage.Width;
  28005. SrcRect.Bottom := SrcRect.Top + vPageHeight;
  28006. // Clear the image
  28007. PrinterImage.Canvas.Brush.Color := clWhite;
  28008. PrinterImage.Canvas.FillRect(Rect(0, 0, PrinterImage.Width, PrinterImage.Height));
  28009. PrinterImage.Canvas.CopyRect(DestRect, Image.Canvas, SrcRect);
  28010. PrtStretchDrawDIB(Printer.Canvas, Rect(0, 0, Printer.PageWidth, Printer.PageHeight - 1), PrinterImage);
  28011. if xPage <> xPageNum then
  28012. Printer.NewPage;
  28013. end;
  28014. if yPage <> yPageNum then
  28015. Printer.NewPage;
  28016. end;
  28017. // Restore tree font.
  28018. Font := SaveTreeFont;
  28019. SaveTreeFont.Free;
  28020. Printer.EndDoc;
  28021. finally
  28022. PrinterImage.Free;
  28023. Image.Free;
  28024. EndUpdate;
  28025. end;
  28026. end;
  28027. end;
  28028. //----------------------------------------------------------------------------------------------------------------------
  28029. function TBaseVirtualTree.ProcessDrop(DataObject: IDataObject; TargetNode: PVirtualNode; var Effect: Integer;
  28030. Mode: TVTNodeAttachMode): Boolean;
  28031. // Recreates the (sub) tree structure serialized into memory and provided by DataObject. The new nodes are attached to
  28032. // the passed node or FRoot if TargetNode is nil.
  28033. // Returns True on success, i.e. the CF_VIRTUALTREE format is supported by the data object and the structure could be
  28034. // recreated, otherwise False.
  28035. var
  28036. Source: TBaseVirtualTree;
  28037. begin
  28038. Result := False;
  28039. if Mode = amNoWhere then
  28040. Effect := DROPEFFECT_NONE
  28041. else
  28042. begin
  28043. BeginUpdate;
  28044. // try to get the source tree of the operation
  28045. Source := GetTreeFromDataObject(DataObject);
  28046. if Assigned(Source) then
  28047. Source.BeginUpdate;
  28048. try
  28049. try
  28050. // Before adding the new nodes try to optimize the operation if source and target tree reside in
  28051. // the same application and operation is a move.
  28052. if ((Effect and DROPEFFECT_MOVE) <> 0) and Assigned(Source) then
  28053. begin
  28054. // If both copy and move are specified then prefer a copy because this is not destructing.
  28055. Result := ProcessOLEData(Source, DataObject, TargetNode, Mode, (Effect and DROPEFFECT_COPY) = 0);
  28056. // Since we made an optimized move or a copy there's no reason to act further after DoDragging returns.
  28057. Effect := DROPEFFECT_NONE;
  28058. end
  28059. else
  28060. // Act only if move or copy operation is requested.
  28061. if (Effect and (DROPEFFECT_MOVE or DROPEFFECT_COPY)) <> 0 then
  28062. Result := ProcessOLEData(Source, DataObject, TargetNode, Mode, False)
  28063. else
  28064. Result := False;
  28065. except
  28066. Effect := DROPEFFECT_NONE;
  28067. end;
  28068. finally
  28069. if Assigned(Source) then
  28070. Source.EndUpdate;
  28071. EndUpdate;
  28072. end;
  28073. end;
  28074. end;
  28075. //----------------------------------------------------------------------------------------------------------------------
  28076. type
  28077. // needed to handle OLE global memory objects
  28078. TOLEMemoryStream = class(TCustomMemoryStream)
  28079. public
  28080. function Write(const Buffer; Count: Integer): Longint; override;
  28081. end;
  28082. //----------------------------------------------------------------------------------------------------------------------
  28083. function TOLEMemoryStream.Write(const Buffer; Count: Integer): Integer;
  28084. begin
  28085. raise EStreamError.CreateRes(PResStringRec(@SCantWriteResourceStreamError));
  28086. end;
  28087. //----------------- TBaseVirtualTree -----------------------------------------------------------------------------
  28088. procedure TBaseVirtualTree.DoDrawHint(Canvas: TCanvas; Node: PVirtualNode; R:
  28089. TRect; Column: TColumnIndex);
  28090. begin
  28091. if Assigned(FOnDrawHint) then
  28092. FOnDrawHint(Self, Canvas, Node, R, Column);
  28093. end;
  28094. //----------------------------------------------------------------------------------------------------------------------
  28095. procedure TBaseVirtualTree.DoGetHintSize(Node: PVirtualNode; Column:
  28096. TColumnIndex; var R: TRect);
  28097. begin
  28098. if Assigned(FOnGetHintSize) then
  28099. FOnGetHintSize(Self, Node, Column, R);
  28100. end;
  28101. //----------------------------------------------------------------------------------------------------------------------
  28102. procedure TBaseVirtualTree.DoGetHintKind(Node: PVirtualNode; Column:
  28103. TColumnIndex; var Kind: TVTHintKind);
  28104. begin
  28105. if Assigned(FOnGetHintKind) then
  28106. FOnGetHintKind(Self, Node, Column, Kind)
  28107. else
  28108. Kind := DefaultHintKind;
  28109. end;
  28110. function TBaseVirtualTree.GetDefaultHintKind: TVTHintKind;
  28111. begin
  28112. Result := vhkText;
  28113. end;
  28114. //----------------------------------------------------------------------------------------------------------------------
  28115. function TBaseVirtualTree.ProcessOLEData(Source: TBaseVirtualTree; DataObject: IDataObject; TargetNode: PVirtualNode;
  28116. Mode: TVTNodeAttachMode; Optimized: Boolean): Boolean;
  28117. // Recreates the (sub) tree structure serialized into memory and provided by DataObject. The new nodes are attached to
  28118. // the passed node or FRoot if TargetNode is nil according to Mode. Optimized can be set to True if the entire operation
  28119. // happens within the same process (i.e. sender and receiver of the OLE operation are located in the same process).
  28120. // Optimize = True makes only sense if the operation to carry out is a move hence it is also the indication of the
  28121. // operation to be done here. Source is the source of the OLE data and only of use (and usually assigned) when
  28122. // an OLE operation takes place in the same application.
  28123. // Returns True on success, i.e. the CF_VIRTUALTREE format is supported by the data object and the structure could be
  28124. // recreated, otherwise False.
  28125. var
  28126. Medium: TStgMedium;
  28127. Stream: TStream;
  28128. Data: Pointer;
  28129. Node: PVirtualNode;
  28130. Nodes: TNodeArray;
  28131. I: Integer;
  28132. Res: HRESULT;
  28133. ChangeReason: TChangeReason;
  28134. begin
  28135. Nodes := nil;
  28136. // Check the data format available by the data object.
  28137. with StandardOLEFormat do
  28138. begin
  28139. // Read best format.
  28140. cfFormat := CF_VIRTUALTREE;
  28141. end;
  28142. Result := DataObject.QueryGetData(StandardOLEFormat) = S_OK;
  28143. if Result and not (toReadOnly in FOptions.FMiscOptions) then
  28144. begin
  28145. BeginUpdate;
  28146. Result := False;
  28147. try
  28148. if TargetNode = nil then
  28149. TargetNode := FRoot;
  28150. if TargetNode = FRoot then
  28151. begin
  28152. case Mode of
  28153. amInsertBefore:
  28154. Mode := amAddChildFirst;
  28155. amInsertAfter:
  28156. Mode := amAddChildLast;
  28157. end;
  28158. end;
  28159. // Optimized means source is known and in the same process so we can access its pointers, which avoids duplicating
  28160. // the data while doing a serialization. Can only be used with cut'n paste and drag'n drop with move effect.
  28161. if Optimized then
  28162. begin
  28163. if tsOLEDragging in Source.FStates then
  28164. Nodes := Source.FDragSelection
  28165. else
  28166. Nodes := Source.GetSortedCutCopySet(True);
  28167. if Mode in [amInsertBefore,amAddChildLast] then
  28168. begin
  28169. for I := 0 to High(Nodes) do
  28170. if not HasAsParent(TargetNode, Nodes[I]) then
  28171. Source.MoveTo(Nodes[I], TargetNode, Mode, False);
  28172. end
  28173. else
  28174. begin
  28175. for I := High(Nodes) downto 0 do
  28176. if not HasAsParent(TargetNode, Nodes[I]) then
  28177. Source.MoveTo(Nodes[I], TargetNode, Mode, False);
  28178. end;
  28179. Result := True;
  28180. end
  28181. else
  28182. begin
  28183. if Source = Self then
  28184. ChangeReason := crNodeCopied
  28185. else
  28186. ChangeReason := crNodeAdded;
  28187. Res := DataObject.GetData(StandardOLEFormat, Medium);
  28188. if Res = S_OK then
  28189. begin
  28190. case Medium.tymed of
  28191. TYMED_ISTREAM, // IStream interface
  28192. TYMED_HGLOBAL: // global memory block
  28193. begin
  28194. Stream := nil;
  28195. if Medium.tymed = TYMED_ISTREAM then
  28196. Stream := TOLEStream.Create(IUnknown(Medium.stm) as IStream)
  28197. else
  28198. begin
  28199. Data := GlobalLock(Medium.hGlobal);
  28200. if Assigned(Data) then
  28201. begin
  28202. // Get the total size of data to retrieve.
  28203. I := PCardinal(Data)^;
  28204. Inc(PCardinal(Data));
  28205. Stream := TOLEMemoryStream.Create;
  28206. TOLEMemoryStream(Stream).SetPointer(Data, I);
  28207. end;
  28208. end;
  28209. if Assigned(Stream) then
  28210. try
  28211. while Stream.Position < Stream.Size do
  28212. begin
  28213. Node := MakeNewNode;
  28214. InternalConnectNode(Node, TargetNode, Self, Mode);
  28215. InternalAddFromStream(Stream, VTTreeStreamVersion, Node);
  28216. // This seems a bit strange because of the callback for granting to add the node
  28217. // which actually comes after the node has been added. The reason is that the node must
  28218. // contain valid data otherwise I don't see how the application can make a funded decision.
  28219. if not DoNodeCopying(Node, TargetNode) then
  28220. begin
  28221. DeleteNode(Node);
  28222. end
  28223. else
  28224. begin
  28225. DoNodeCopied(Node);
  28226. StructureChange(Node, ChangeReason);
  28227. // In order to maintain the same node order when restoring nodes in the case of amInsertAfter
  28228. // we have to move the reference node continously. Othwise we would end up with reversed node order.
  28229. if Mode = amInsertAfter then
  28230. TargetNode := Node;
  28231. end;
  28232. end;
  28233. Result := True;
  28234. finally
  28235. Stream.Free;
  28236. if Medium.tymed = TYMED_HGLOBAL then
  28237. GlobalUnlock(Medium.hGlobal);
  28238. end;
  28239. end;
  28240. end;
  28241. ReleaseStgMedium(Medium);
  28242. end;
  28243. end;
  28244. finally
  28245. EndUpdate;
  28246. end;
  28247. end;
  28248. end;
  28249. //----------------------------------------------------------------------------------------------------------------------
  28250. procedure TBaseVirtualTree.ReinitChildren(Node: PVirtualNode; Recursive: Boolean);
  28251. // Forces all child nodes of Node to be reinitialized.
  28252. // If Recursive is True then also the grandchildren are reinitialized.
  28253. var
  28254. Run: PVirtualNode;
  28255. begin
  28256. if Assigned(Node) then
  28257. begin
  28258. InitChildren(Node);
  28259. Run := Node.FirstChild;
  28260. end
  28261. else
  28262. begin
  28263. InitChildren(FRoot);
  28264. Run := FRoot.FirstChild;
  28265. end;
  28266. while Assigned(Run) do
  28267. begin
  28268. ReinitNode(Run, Recursive);
  28269. Run := Run.NextSibling;
  28270. end;
  28271. end;
  28272. //----------------------------------------------------------------------------------------------------------------------
  28273. procedure TBaseVirtualTree.ReinitNode(Node: PVirtualNode; Recursive: Boolean);
  28274. // Forces the given node and all its children (if recursive is True) to be initialized again without
  28275. // modifying any data in the nodes nor deleting children (unless the application requests a different amount).
  28276. begin
  28277. if Assigned(Node) and (Node <> FRoot) then
  28278. begin
  28279. // Remove dynamic styles.
  28280. Node.States := Node.States - [vsChecking, vsCutOrCopy, vsDeleting, vsHeightMeasured];
  28281. InitNode(Node);
  28282. end;
  28283. if Recursive then
  28284. ReinitChildren(Node, True);
  28285. end;
  28286. //----------------------------------------------------------------------------------------------------------------------
  28287. procedure TBaseVirtualTree.RepaintNode(Node: PVirtualNode);
  28288. // Causes an immediate repaint of the given node.
  28289. var
  28290. R: Trect;
  28291. begin
  28292. if Assigned(Node) and (Node <> FRoot) then
  28293. begin
  28294. R := GetDisplayRect(Node, -1, False);
  28295. RedrawWindow(Handle, @R, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE or RDW_VALIDATE or RDW_NOCHILDREN);
  28296. end;
  28297. end;
  28298. //----------------------------------------------------------------------------------------------------------------------
  28299. procedure TBaseVirtualTree.ResetNode(Node: PVirtualNode);
  28300. // Deletes all children of the given node and marks it as being uninitialized.
  28301. begin
  28302. DoCancelEdit;
  28303. if (Node = nil) or (Node = FRoot) then
  28304. Clear
  28305. else
  28306. begin
  28307. DoReset(Node);
  28308. DeleteChildren(Node);
  28309. // Remove initialized and other dynamic styles, keep persistent styles.
  28310. Node.States := Node.States - [vsInitialized, vsChecking, vsCutOrCopy, vsDeleting, vsHasChildren, vsExpanded,
  28311. vsHeightMeasured];
  28312. InvalidateNode(Node);
  28313. end;
  28314. end;
  28315. //----------------------------------------------------------------------------------------------------------------------
  28316. procedure TBaseVirtualTree.SaveToFile(const FileName: TFileName);
  28317. // Saves the entire content of the tree into a file (see further notes in SaveToStream).
  28318. var
  28319. FileStream: TFileStream;
  28320. begin
  28321. FileStream := TFileStream.Create(FileName, fmCreate);
  28322. try
  28323. SaveToStream(FileStream);
  28324. finally
  28325. FileStream.Free;
  28326. end;
  28327. end;
  28328. //----------------------------------------------------------------------------------------------------------------------
  28329. procedure TBaseVirtualTree.SaveToStream(Stream: TStream; Node: PVirtualNode = nil);
  28330. // Saves Node and all its children to Stream. If Node is nil then all top level nodes will be stored.
  28331. // Note: You should be careful about assuming what is actually saved. The problem here is that we are dealing with
  28332. // virtual data. The tree can so not know what it has to save. The only fact we reliably know is the tree's
  28333. // structure. To be flexible for future enhancements as well as unknown content (unknown to the tree class which
  28334. // is saving/loading the stream) a chunk based approach is used here. Every tree class handles only those
  28335. // chunks which are not handled by an anchestor class and are known by the class.
  28336. //
  28337. // The base tree class saves only the structure of the tree along with application provided data. descendants may
  28338. // optionally add their own chunks to store additional information. See: WriteChunks.
  28339. var
  28340. Count: Cardinal;
  28341. begin
  28342. Stream.Write(MagicID, SizeOf(MagicID));
  28343. if Node = nil then
  28344. begin
  28345. // Keep number of top level nodes for easy restauration.
  28346. Count := FRoot.ChildCount;
  28347. Stream.WriteBuffer(Count, SizeOf(Count));
  28348. // Save entire tree here.
  28349. Node := FRoot.FirstChild;
  28350. while Assigned(Node) do
  28351. begin
  28352. WriteNode(Stream, Node);
  28353. Node := Node.NextSibling;
  28354. end;
  28355. end
  28356. else
  28357. begin
  28358. Count := 1;
  28359. Stream.WriteBuffer(Count, SizeOf(Count));
  28360. WriteNode(Stream, Node);
  28361. end;
  28362. if Assigned(FOnSaveTree) then
  28363. FOnSaveTree(Self, Stream);
  28364. end;
  28365. //----------------------------------------------------------------------------------------------------------------------
  28366. function TBaseVirtualTree.ScrollIntoView(Node: PVirtualNode; Center: Boolean; Horizontally: Boolean = False): Boolean;
  28367. // Scrolls the tree so that the given node is in the client area and returns True if the tree really has been
  28368. // scrolled (e.g. to avoid further updates) else returns False. If extened focus is enabled then the tree will also
  28369. // be horizontally scrolled if needed.
  28370. // Note: All collapsed parents of the node are expanded.
  28371. var
  28372. R: TRect;
  28373. Run: PVirtualNode;
  28374. UseColumns,
  28375. HScrollBarVisible: Boolean;
  28376. ScrolledVertically,
  28377. ScrolledHorizontally: Boolean;
  28378. begin
  28379. ScrolledVertically := False;
  28380. ScrolledHorizontally := False;
  28381. if Assigned(Node) and (Node <> FRoot) then
  28382. begin
  28383. // Make sure all parents of the node are expanded.
  28384. Run := Node.Parent;
  28385. while Run <> FRoot do
  28386. begin
  28387. if not (vsExpanded in Run.States) then
  28388. ToggleNode(Run);
  28389. Run := Run.Parent;
  28390. end;
  28391. UseColumns := FHeader.UseColumns;
  28392. if UseColumns and FHeader.FColumns.IsValidColumn(FFocusedColumn) then
  28393. R := GetDisplayRect(Node, FFocusedColumn, not (toGridExtensions in FOptions.FMiscOptions))
  28394. else
  28395. R := GetDisplayRect(Node, NoColumn, not (toGridExtensions in FOptions.FMiscOptions));
  28396. // The returned rectangle can never be empty after the expand code above.
  28397. // 1) scroll vertically
  28398. if R.Top < 0 then
  28399. begin
  28400. if Center then
  28401. SetOffsetY(FOffsetY - R.Top + ClientHeight div 2)
  28402. else
  28403. SetOffsetY(FOffsetY - R.Top);
  28404. ScrolledVertically := True;
  28405. end
  28406. else
  28407. if (R.Bottom > ClientHeight) or Center then
  28408. begin
  28409. HScrollBarVisible := (ScrollBarOptions.ScrollBars in [{$if CompilerVersion >= 24}System.UITypes.TScrollStyle.{$ifend}ssBoth, {$if CompilerVersion >= 24}System.UITypes.TScrollStyle.{$ifend}ssHorizontal]) and
  28410. (ScrollBarOptions.AlwaysVisible or (Integer(FRangeX) > ClientWidth));
  28411. if Center then
  28412. SetOffsetY(FOffsetY - R.Bottom + ClientHeight div 2)
  28413. else
  28414. SetOffsetY(FOffsetY - R.Bottom + ClientHeight);
  28415. // When scrolling up and the horizontal scroll appears because of the operation
  28416. // then we have to move up the node the horizontal scrollbar's height too
  28417. // in order to avoid that the scroll bar hides the node which we wanted to have in view.
  28418. if not UseColumns and not HScrollBarVisible and (Integer(FRangeX) > ClientWidth) then
  28419. SetOffsetY(FOffsetY - GetSystemMetrics(SM_CYHSCROLL));
  28420. ScrolledVertically := True;
  28421. end;
  28422. if Horizontally then
  28423. // 2) scroll horizontally
  28424. ScrolledHorizontally := ScrollIntoView(FFocusedColumn, Center, Node);
  28425. end;
  28426. Result := ScrolledVertically or ScrolledHorizontally;
  28427. end;
  28428. //----------------------------------------------------------------------------------------------------------------------
  28429. function TBaseVirtualTree.ScrollIntoView(Column: TColumnIndex; Center: Boolean; Node: PVirtualNode = nil): Boolean;
  28430. // Scrolls the columns so that the given column is in the client area and returns True if the columns really have been
  28431. // scrolled (e.g. to avoid further updates) else returns False.
  28432. var
  28433. ColumnLeft,
  28434. ColumnRight: Integer;
  28435. NewOffset: Integer;
  28436. R: TRect;
  28437. begin
  28438. Result := False;
  28439. if FHeader.UseColumns and FHeader.Columns.IsValidColumn(Column) then begin
  28440. ColumnLeft := Header.Columns.Items[Column].Left;
  28441. ColumnRight := ColumnLeft + Header.Columns.Items[Column].Width;
  28442. end else if Assigned(Node) then begin
  28443. R := GetDisplayRect(Node, NoColumn, not (toGridExtensions in FOptions.FMiscOptions));
  28444. ColumnLeft := R.Left;
  28445. ColumnRight := R.Right;
  28446. end else
  28447. Exit;
  28448. NewOffset := FEffectiveOffsetX;
  28449. if (Header.Columns.GetVisibleFixedWidth > 0) and (not Center) then
  28450. begin
  28451. if ColumnRight > ClientWidth then
  28452. NewOffset := FEffectiveOffsetX + (ColumnRight - ClientWidth)
  28453. else if ColumnLeft < Header.Columns.GetVisibleFixedWidth then
  28454. NewOffset := FEffectiveOffsetX - (Header.Columns.GetVisibleFixedWidth - ColumnLeft);
  28455. if NewOffset <> FEffectiveOffsetX then
  28456. begin
  28457. if UseRightToLeftAlignment then
  28458. SetOffsetX(-Integer(FRangeX) + ClientWidth + NewOffset)
  28459. else
  28460. SetOffsetX(-NewOffset);
  28461. end;
  28462. Result := True;
  28463. end
  28464. else
  28465. begin
  28466. NewOffset := FEffectiveOffsetX + ColumnLeft - (Header.Columns.GetVisibleFixedWidth div 2) - (ClientWidth div 2) + ((ColumnRight - ColumnLeft) div 2);
  28467. if NewOffset <> FEffectiveOffsetX then
  28468. begin
  28469. if UseRightToLeftAlignment then
  28470. SetOffsetX(-Integer(FRangeX) + ClientWidth + NewOffset)
  28471. else
  28472. SetOffsetX(-NewOffset);
  28473. end;
  28474. Result := True;
  28475. end;
  28476. end;
  28477. //----------------------------------------------------------------------------------------------------------------------
  28478. procedure TBaseVirtualTree.SelectAll(VisibleOnly: Boolean);
  28479. // Select all nodes in the tree.
  28480. // If VisibleOnly is True then only visible nodes are selected.
  28481. var
  28482. Run: PVirtualNode;
  28483. NextFunction: TGetNextNodeProc;
  28484. begin
  28485. if not FSelectionLocked and (toMultiSelect in FOptions.FSelectionOptions) then
  28486. begin
  28487. ClearTempCache;
  28488. if VisibleOnly then
  28489. begin
  28490. Run := GetFirstVisible(nil, True);
  28491. NextFunction := GetNextVisible;
  28492. end
  28493. else
  28494. begin
  28495. Run := GetFirst;
  28496. NextFunction := GetNext;
  28497. end;
  28498. while Assigned(Run) do
  28499. begin
  28500. if not(vsSelected in Run.States) then
  28501. InternalCacheNode(Run);
  28502. Run := NextFunction(Run);
  28503. end;
  28504. if FTempNodeCount > 0 then
  28505. AddToSelection(FTempNodeCache, FTempNodeCount);
  28506. ClearTempCache;
  28507. Invalidate;
  28508. end;
  28509. end;
  28510. //----------------------------------------------------------------------------------------------------------------------
  28511. procedure TBaseVirtualTree.Sort(Node: PVirtualNode; Column: TColumnIndex; Direction: TSortDirection; DoInit: Boolean = True);
  28512. // Sorts the given node. The application is queried about how to sort via the OnCompareNodes event.
  28513. // Column is simply passed to the the compare function so the application can also sort in a particular column.
  28514. // In order to free the application from taking care about the sort direction the parameter Direction is used.
  28515. // This way the application can always sort in increasing order, while this method reorders nodes according to this flag.
  28516. //--------------- local functions -------------------------------------------
  28517. function MergeAscending(A, B: PVirtualNode): PVirtualNode;
  28518. // Merges A and B (which both must be sorted via Compare) into one list.
  28519. var
  28520. Dummy: TVirtualNode;
  28521. CompareResult: Integer;
  28522. begin
  28523. // This avoids checking for Result = nil in the loops.
  28524. Result := @Dummy;
  28525. while Assigned(A) and Assigned(B) do
  28526. begin
  28527. if OperationCanceled then
  28528. CompareResult := 0
  28529. else
  28530. CompareResult := DoCompare(A, B, Column);
  28531. if CompareResult <= 0 then
  28532. begin
  28533. Result.NextSibling := A;
  28534. Result := A;
  28535. A := A.NextSibling;
  28536. end
  28537. else
  28538. begin
  28539. Result.NextSibling := B;
  28540. Result := B;
  28541. B := B.NextSibling;
  28542. end;
  28543. end;
  28544. // Just append the list which is not nil (or set end of result list to nil if both lists are nil).
  28545. if Assigned(A) then
  28546. Result.NextSibling := A
  28547. else
  28548. Result.NextSibling := B;
  28549. // return start of the new merged list
  28550. Result := Dummy.NextSibling;
  28551. end;
  28552. //---------------------------------------------------------------------------
  28553. function MergeDescending(A, B: PVirtualNode): PVirtualNode;
  28554. // Merges A and B (which both must be sorted via Compare) into one list.
  28555. var
  28556. Dummy: TVirtualNode;
  28557. CompareResult: Integer;
  28558. begin
  28559. // this avoids checking for Result = nil in the loops
  28560. Result := @Dummy;
  28561. while Assigned(A) and Assigned(B) do
  28562. begin
  28563. if OperationCanceled then
  28564. CompareResult := 0
  28565. else
  28566. CompareResult := DoCompare(A, B, Column);
  28567. if CompareResult >= 0 then
  28568. begin
  28569. Result.NextSibling := A;
  28570. Result := A;
  28571. A := A.NextSibling;
  28572. end
  28573. else
  28574. begin
  28575. Result.NextSibling := B;
  28576. Result := B;
  28577. B := B.NextSibling;
  28578. end;
  28579. end;
  28580. // Just append the list which is not nil (or set end of result list to nil if both lists are nil).
  28581. if Assigned(A) then
  28582. Result.NextSibling := A
  28583. else
  28584. Result.NextSibling := B;
  28585. // Return start of the newly merged list.
  28586. Result := Dummy.NextSibling;
  28587. end;
  28588. //---------------------------------------------------------------------------
  28589. function MergeSortAscending(var Node: PVirtualNode; N: Cardinal): PVirtualNode;
  28590. // Sorts the list of nodes given by Node (which must not be nil).
  28591. var
  28592. A, B: PVirtualNode;
  28593. begin
  28594. if N > 1 then
  28595. begin
  28596. A := MergeSortAscending(Node, N div 2);
  28597. B := MergeSortAscending(Node, (N + 1) div 2);
  28598. Result := MergeAscending(A, B);
  28599. end
  28600. else
  28601. begin
  28602. Result := Node;
  28603. Node := Node.NextSibling;
  28604. Result.NextSibling := nil;
  28605. end;
  28606. end;
  28607. //---------------------------------------------------------------------------
  28608. function MergeSortDescending(var Node: PVirtualNode; N: Cardinal): PVirtualNode;
  28609. // Sorts the list of nodes given by Node (which must not be nil).
  28610. var
  28611. A, B: PVirtualNode;
  28612. begin
  28613. if N > 1 then
  28614. begin
  28615. A := MergeSortDescending(Node, N div 2);
  28616. B := MergeSortDescending(Node, (N + 1) div 2);
  28617. Result := MergeDescending(A, B);
  28618. end
  28619. else
  28620. begin
  28621. Result := Node;
  28622. Node := Node.NextSibling;
  28623. Result.NextSibling := nil;
  28624. end;
  28625. end;
  28626. //--------------- end local functions ---------------------------------------
  28627. var
  28628. Run: PVirtualNode;
  28629. Index: Cardinal;
  28630. begin
  28631. InterruptValidation;
  28632. if tsEditPending in FStates then
  28633. begin
  28634. StopTimer(EditTimer);
  28635. DoStateChange([], [tsEditPending]);
  28636. end;
  28637. if not (tsEditing in FStates) or DoEndEdit then
  28638. begin
  28639. if Node = nil then
  28640. Node := FRoot;
  28641. if vsHasChildren in Node.States then
  28642. begin
  28643. if (Node.ChildCount = 0) and DoInit then
  28644. InitChildren(Node);
  28645. // Make sure the children are valid, so they can be sorted at all.
  28646. if DoInit and (Node.ChildCount > 0) then
  28647. ValidateChildren(Node, False);
  28648. // Child count might have changed.
  28649. if Node.ChildCount > 1 then
  28650. begin
  28651. StartOperation(okSortNode);
  28652. try
  28653. // Sort the linked list, check direction flag only once.
  28654. if Direction = sdAscending then
  28655. Node.FirstChild := MergeSortAscending(Node.FirstChild, Node.ChildCount)
  28656. else
  28657. Node.FirstChild := MergeSortDescending(Node.FirstChild, Node.ChildCount);
  28658. finally
  28659. EndOperation(okSortNode);
  28660. end;
  28661. // Consolidate the child list finally.
  28662. Run := Node.FirstChild;
  28663. Run.PrevSibling := nil;
  28664. Index := 0;
  28665. repeat
  28666. Run.Index := Index;
  28667. Inc(Index);
  28668. if Run.NextSibling = nil then
  28669. Break;
  28670. Run.NextSibling.PrevSibling := Run;
  28671. Run := Run.NextSibling;
  28672. until False;
  28673. Node.LastChild := Run;
  28674. InvalidateCache;
  28675. end;
  28676. if FUpdateCount = 0 then
  28677. begin
  28678. ValidateCache;
  28679. Invalidate;
  28680. end;
  28681. end;
  28682. end;
  28683. end;
  28684. //----------------------------------------------------------------------------------------------------------------------
  28685. procedure TBaseVirtualTree.SortTree(Column: TColumnIndex; Direction: TSortDirection; DoInit: Boolean = True);
  28686. //--------------- local function --------------------------------------------
  28687. procedure DoSort(Node: PVirtualNode);
  28688. // Recursively sorts Node and its child nodes.
  28689. var
  28690. Run: PVirtualNode;
  28691. begin
  28692. Sort(Node, Column, Direction, DoInit);
  28693. // Recurse to next level
  28694. Run := Node.FirstChild;
  28695. while Assigned(Run) and not FOperationCanceled do
  28696. begin
  28697. if DoInit and not (vsInitialized in Run.States) then
  28698. InitNode(Run);
  28699. if (vsInitialized in Run.States) and (not (toAutoSort in TreeOptions.AutoOptions) or Expanded[Run]) then // There is no need to sort collapsed branches
  28700. DoSort(Run);
  28701. Run := Run.NextSibling;
  28702. end;
  28703. end;
  28704. //--------------- end local function ----------------------------------------
  28705. begin
  28706. if RootNode.TotalCount <= 2 then
  28707. Exit;//Nothing to do if there are one or zero nodes. RootNode.TotalCount is 1 if there are no nodes in the treee as the root node counts too here.
  28708. // Instead of wrapping the sort using BeginUpdate/EndUpdate simply the update counter
  28709. // is modified. Otherwise the EndUpdate call will recurse here.
  28710. Inc(FUpdateCount);
  28711. try
  28712. if Column > InvalidColumn then
  28713. begin
  28714. StartOperation(okSortTree);
  28715. try
  28716. DoSort(FRoot);
  28717. finally
  28718. EndOperation(okSortTree);
  28719. end;
  28720. end;
  28721. InvalidateCache;
  28722. finally
  28723. if FUpdateCount > 0 then
  28724. Dec(FUpdateCount);
  28725. if FUpdateCount = 0 then
  28726. begin
  28727. ValidateCache;
  28728. Invalidate;
  28729. end;
  28730. end;
  28731. end;
  28732. //----------------------------------------------------------------------------------------------------------------------
  28733. procedure TBaseVirtualTree.ToggleNode(Node: PVirtualNode);
  28734. // Changes a node's expand state to the opposite state.
  28735. var
  28736. Child,
  28737. FirstVisible: PVirtualNode;
  28738. HeightDelta,
  28739. StepsR1,
  28740. StepsR2,
  28741. Steps: Integer;
  28742. TogglingTree,
  28743. ChildrenInView,
  28744. NeedFullInvalidate,
  28745. NeedUpdate,
  28746. NodeInView,
  28747. PosHoldable,
  28748. TotalFit: Boolean;
  28749. ToggleData: TToggleAnimationData;
  28750. //--------------- local function --------------------------------------------
  28751. procedure PrepareAnimation;
  28752. // Prepares ToggleData.
  28753. var
  28754. R: TRect;
  28755. S: Integer;
  28756. M: TToggleAnimationMode;
  28757. begin
  28758. with ToggleData do
  28759. begin
  28760. Window := Handle;
  28761. DC := GetDC(Handle);
  28762. Self.Brush.Color := FColors.BackGroundColor;
  28763. Brush := Self.Brush.Handle;
  28764. if (Mode1 <> tamNoScroll) and (Mode2 <> tamNoScroll) then
  28765. begin
  28766. if StepsR1 < StepsR2 then
  28767. begin
  28768. // As the primary rectangle is always R1 we will get a much smoother
  28769. // animation if R1 is the one that will be scrolled more.
  28770. R := R2;
  28771. R2 := R1;
  28772. R1 := R;
  28773. M := Mode2;
  28774. Mode2 := Mode1;
  28775. Mode1 := M;
  28776. S := StepsR2;
  28777. StepsR2 := StepsR1;
  28778. StepsR1 := S;
  28779. end;
  28780. ScaleFactor := StepsR2 / StepsR1;
  28781. MissedSteps := 0;
  28782. end;
  28783. if Mode1 <> tamNoScroll then
  28784. Steps := StepsR1
  28785. else
  28786. Steps := StepsR2;
  28787. end;
  28788. end;
  28789. //--------------- end local function ----------------------------------------
  28790. begin
  28791. Assert(Assigned(Node), 'Node must not be nil.');
  28792. TogglingTree := tsToggling in FStates;
  28793. ChildrenInView := False;
  28794. HeightDelta := 0;
  28795. NeedFullInvalidate := False;
  28796. NeedUpdate := False;
  28797. NodeInView := False;
  28798. PosHoldable := False;
  28799. TotalFit := False;
  28800. // We don't need to switch the expand state if the node is being deleted otherwise some
  28801. // updates (e.g. visible node count) are done twice with disasterous results).
  28802. if [vsDeleting, vsToggling] * Node.States = [] then
  28803. begin
  28804. try
  28805. DoStateChange([tsToggling]);
  28806. Include(Node.States, vsToggling);
  28807. if vsExpanded in Node.States then
  28808. begin
  28809. if DoCollapsing(Node) then
  28810. begin
  28811. NeedUpdate := True;
  28812. // Calculate the height delta right now as we need it for toChildrenAbove anyway.
  28813. HeightDelta := -Integer(Node.TotalHeight) + Integer(NodeHeight[Node]);
  28814. if (FUpdateCount = 0) and (toAnimatedToggle in FOptions.FAnimationOptions) and not
  28815. (tsCollapsing in FStates) then
  28816. begin
  28817. if tsHint in Self.FStates then
  28818. Application.CancelHint;
  28819. UpdateWindow(Handle);
  28820. // animated collapsing
  28821. with ToggleData do
  28822. begin
  28823. // Determine the animation behaviour and rectangle. If toChildrenAbove is set, the behaviour is depending
  28824. // on the position of the node to be collapsed.
  28825. R1 := GetDisplayRect(Node, NoColumn, False);
  28826. Mode2 := tamNoScroll;
  28827. if toChildrenAbove in FOptions.FPaintOptions then
  28828. begin
  28829. PosHoldable := (FOffsetY + (Integer(Node.TotalHeight) - Integer(NodeHeight[Node]))) <= 0;
  28830. NodeInView := R1.Top < ClientHeight;
  28831. StepsR1 := 0;
  28832. if NodeInView then
  28833. begin
  28834. if PosHoldable or not (toAdvancedAnimatedToggle in FOptions.FAnimationOptions) then
  28835. begin
  28836. // Scroll the child nodes down.
  28837. Mode1 := tamScrollDown;
  28838. R1.Bottom := R1.Top;
  28839. R1.Top := 0;
  28840. StepsR1 := Min(R1.Bottom - R1.Top + 1, Integer(Node.TotalHeight) - Integer(NodeHeight[Node]));
  28841. end
  28842. else
  28843. begin
  28844. // The position cannot be kept. So scroll the node up to its future position.
  28845. Mode1 := tamScrollUp;
  28846. R1.Top := Max(0, R1.Top + HeightDelta);
  28847. R1.Bottom := ClientHeight;
  28848. StepsR1 := FOffsetY - HeightDelta;
  28849. end;
  28850. end;
  28851. end
  28852. else
  28853. begin
  28854. if (Integer(FRangeY) + FOffsetY - R1.Bottom + HeightDelta >= ClientHeight - R1.Bottom) or
  28855. (Integer(FRangeY) <= ClientHeight) or (FOffsetY = 0) or not
  28856. (toAdvancedAnimatedToggle in FOptions.FAnimationOptions) then
  28857. begin
  28858. // Do a simple scroll up over the child nodes.
  28859. Mode1 := tamScrollUp;
  28860. Inc(R1.Top, NodeHeight[Node]);
  28861. R1.Bottom := ClientHeight;
  28862. StepsR1 := Min(R1.Bottom - R1.Top + 1, -HeightDelta);
  28863. end
  28864. else
  28865. begin
  28866. // Scroll the node down to its future position. As FOffsetY will change we need to invalidate the
  28867. // whole tree.
  28868. Mode1 := tamScrollDown;
  28869. StepsR1 := Min(-FOffsetY, ClientHeight - Integer(FRangeY) -FOffsetY - HeightDelta);
  28870. R1.Top := 0;
  28871. R1.Bottom := Min(ClientHeight, R1.Bottom + Steps);
  28872. NeedFullInvalidate := True;
  28873. end;
  28874. end;
  28875. // No animation necessary if the node is below the current client height.
  28876. if R1.Top < ClientHeight then
  28877. begin
  28878. PrepareAnimation;
  28879. try
  28880. Animate(Steps, FAnimationDuration, ToggleCallback, @ToggleData);
  28881. finally
  28882. ReleaseDC(Window, DC);
  28883. end;
  28884. end;
  28885. end;
  28886. end;
  28887. // collapse the node
  28888. AdjustTotalHeight(Node, IfThen(IsEffectivelyFiltered[Node], 0, NodeHeight[Node]));
  28889. if FullyVisible[Node] then
  28890. Dec(FVisibleCount, CountVisibleChildren(Node));
  28891. Exclude(Node.States, vsExpanded);
  28892. DoCollapsed(Node);
  28893. // Remove child nodes now, if enabled.
  28894. if (toAutoFreeOnCollapse in FOptions.FAutoOptions) and (Node.ChildCount > 0) then
  28895. begin
  28896. DeleteChildren(Node);
  28897. Include(Node.States, vsHasChildren);
  28898. end;
  28899. end;
  28900. end
  28901. else
  28902. if DoExpanding(Node) then
  28903. begin
  28904. NeedUpdate := True;
  28905. // expand the node, need to adjust the height
  28906. if not (vsInitialized in Node.States) then
  28907. InitNode(Node);
  28908. if (vsHasChildren in Node.States) and (Node.ChildCount = 0) then
  28909. InitChildren(Node);
  28910. // Avoid setting the vsExpanded style if there are no child nodes.
  28911. if Node.ChildCount > 0 then
  28912. begin
  28913. // Iterate through the child nodes without initializing them. We have to determine the entire height.
  28914. Child := Node.FirstChild;
  28915. repeat
  28916. if vsVisible in Child.States then
  28917. begin
  28918. // Ensure the item height is measured
  28919. MeasureItemHeight(Canvas, Child);
  28920. Inc(HeightDelta, Child.TotalHeight);
  28921. end;
  28922. Child := Child.NextSibling;
  28923. until Child = nil;
  28924. // Getting the display rectangle is already done here as it is needed for toChildrenAbove in any case.
  28925. if (toChildrenAbove in FOptions.FPaintOptions) or (FUpdateCount = 0) then
  28926. begin
  28927. with ToggleData do
  28928. begin
  28929. R1 := GetDisplayRect(Node, NoColumn, False);
  28930. Mode2 := tamNoScroll;
  28931. TotalFit := HeightDelta + Integer(NodeHeight[Node]) <= ClientHeight;
  28932. if toChildrenAbove in FOptions.FPaintOptions then
  28933. begin
  28934. // The main goal with toChildrenAbove being set is to keep the nodes visual position so the user does
  28935. // not get confused. Therefore we need to scroll the view when the expanding is done.
  28936. PosHoldable := TotalFit and (Integer(FRangeY) - ClientHeight >= 0) ;
  28937. ChildrenInView := (R1.Top - HeightDelta) >= 0;
  28938. NodeInView := R1.Bottom <= ClientHeight;
  28939. end
  28940. else
  28941. begin
  28942. PosHoldable := TotalFit;
  28943. ChildrenInView := R1.Bottom + HeightDelta <= ClientHeight;
  28944. end;
  28945. R1.Bottom := ClientHeight;
  28946. end;
  28947. end;
  28948. if FUpdateCount = 0 then
  28949. begin
  28950. // Do animated expanding if enabled.
  28951. if (ToggleData.R1.Top < ClientHeight) and ([tsPainting, tsExpanding] * FStates = []) and
  28952. (toAnimatedToggle in FOptions.FAnimationOptions)then
  28953. begin
  28954. if tsHint in Self.FStates then
  28955. Application.CancelHint;
  28956. UpdateWindow(Handle);
  28957. // animated expanding
  28958. with ToggleData do
  28959. begin
  28960. if toChildrenAbove in FOptions.FPaintOptions then
  28961. begin
  28962. // At first check if we hold the position, which is the most common case.
  28963. if not (toAdvancedAnimatedToggle in FOptions.FAnimationOptions) or
  28964. (PosHoldable and ( (NodeInView and ChildrenInView) or not
  28965. (toAutoScrollOnExpand in FOptions.FAutoOptions) )) then
  28966. begin
  28967. Mode1 := tamScrollUp;
  28968. R1 := Rect(R1.Left, 0, R1.Right, R1.Top);
  28969. StepsR1 := Min(HeightDelta, R1.Bottom);
  28970. end
  28971. else
  28972. begin
  28973. // If we will not hold the node's visual position we mostly scroll in both directions.
  28974. Mode1 := tamScrollDown;
  28975. Mode2 := tamScrollUp;
  28976. R2 := Rect(R1.Left, 0, R1.Right, R1.Top);
  28977. if not (toAutoScrollOnExpand in FOptions.FAutoOptions) then
  28978. begin
  28979. // If we shall not or cannot scroll to the desired extent we calculate the new position (with
  28980. // max FOffsetY applied) and animate it that way.
  28981. StepsR1 := -FOffsetY - Max(Integer(FRangeY) + HeightDelta - ClientHeight, 0) + HeightDelta;
  28982. if (Integer(FRangeY) + HeightDelta - ClientHeight) <= 0 then
  28983. Mode2 := tamNoScroll
  28984. else
  28985. StepsR2 := Min(Integer(FRangeY) + HeightDelta - ClientHeight, R2.Bottom);
  28986. end
  28987. else
  28988. begin
  28989. if TotalFit and NodeInView and (Integer(FRangeY) + HeightDelta > ClientHeight) then
  28990. begin
  28991. // If the whole subtree will fit into the client area and the node is currently fully visible,
  28992. // the first child will be made the top node if possible.
  28993. if HeightDelta >= R1.Top then
  28994. StepsR1 := Abs(R1.Top - HeightDelta)
  28995. else
  28996. StepsR1 := ClientHeight - Integer(FRangeY);
  28997. end
  28998. else
  28999. if Integer(FRangeY) + HeightDelta <= ClientHeight then
  29000. begin
  29001. // We cannot make the first child the top node as we cannot scroll to that extent,
  29002. // so we do a simple scroll down.
  29003. Mode2 := tamNoScroll;
  29004. StepsR1 := HeightDelta;
  29005. end
  29006. else
  29007. // If the subtree does not fit into the client area at once, the expanded node will
  29008. // be made the bottom node.
  29009. StepsR1 := ClientHeight - R1.Top - Integer(NodeHeight[Node]);
  29010. if Mode2 <> tamNoScroll then
  29011. begin
  29012. if StepsR1 > 0 then
  29013. StepsR2 := Min(R1.Top, HeightDelta - StepsR1)
  29014. else
  29015. begin
  29016. // If the node is already at the bottom scrolling is needed.
  29017. Mode1 := tamNoScroll;
  29018. StepsR2 := Min(HeightDelta, R1.Bottom);
  29019. end;
  29020. end;
  29021. end;
  29022. end;
  29023. end
  29024. else
  29025. begin
  29026. // toChildrenAbove is not set.
  29027. if (PosHoldable and ChildrenInView) or not (toAutoScrollOnExpand in FOptions.FAutoOptions) or not
  29028. (toAdvancedAnimatedToggle in FOptions.FAnimationOptions) or (R1.Top <= 0) then
  29029. begin
  29030. // If the node will stay at its visual position, do a simple down-scroll.
  29031. Mode1 := tamScrollDown;
  29032. Inc(R1.Top, NodeHeight[Node]);
  29033. StepsR1 := Min(R1.Bottom - R1.Top, HeightDelta);
  29034. end
  29035. else
  29036. begin
  29037. // We will not hold the nodes visual position so perform a double scroll.
  29038. Mode1 := tamScrollUp;
  29039. Mode2 := tamScrollDown;
  29040. R1.Bottom := R1.Top + Integer(NodeHeight[Node]) + 1;
  29041. R1.Top := 0;
  29042. R2 := Rect(R1.Left, R1.Bottom, R1.Right, ClientHeight);
  29043. StepsR1 := Min(HeightDelta - (ClientHeight - R2.Top), R1.Bottom - Integer(NodeHeight[Node]));
  29044. StepsR2 := ClientHeight - R2.Top;
  29045. end;
  29046. end;
  29047. if ClientHeight >= R1.Top then
  29048. begin
  29049. PrepareAnimation;
  29050. try
  29051. Animate(Steps, FAnimationDuration, ToggleCallback, @ToggleData);
  29052. finally
  29053. ReleaseDC(Window, DC);
  29054. end;
  29055. end;
  29056. end;
  29057. end;
  29058. if toAutoSort in FOptions.FAutoOptions then
  29059. Sort(Node, FHeader.FSortColumn, FHeader.FSortDirection, False);
  29060. end;// if UpdateCount = 0
  29061. Include(Node.States, vsExpanded);
  29062. AdjustTotalHeight(Node, HeightDelta, True);
  29063. if FullyVisible[Node] then
  29064. Inc(FVisibleCount, CountVisibleChildren(Node));
  29065. DoExpanded(Node);
  29066. end;
  29067. end;
  29068. if NeedUpdate then
  29069. begin
  29070. InvalidateCache;
  29071. if FUpdateCount = 0 then
  29072. begin
  29073. ValidateCache;
  29074. if Node.ChildCount > 0 then
  29075. begin
  29076. UpdateRanges;
  29077. UpdateScrollBars(True);
  29078. if [tsPainting, tsExpanding] * FStates = [] then
  29079. begin
  29080. if (vsExpanded in Node.States) and ((toAutoScrollOnExpand in FOptions.FAutoOptions) or
  29081. (toChildrenAbove in FOptions.FPaintOptions)) then
  29082. begin
  29083. if toChildrenAbove in FOptions.FPaintOptions then
  29084. begin
  29085. NeedFullInvalidate := True;
  29086. if (PosHoldable and ChildrenInView and NodeInView) or not
  29087. (toAutoScrollOnExpand in FOptions.FAutoOptions) then
  29088. SetOffsetY(FOffsetY - Integer(HeightDelta))
  29089. else
  29090. if TotalFit and NodeInView then
  29091. begin
  29092. FirstVisible := GetFirstVisible(Node, True);
  29093. if Assigned(FirstVisible) then // otherwise there is no visible child at all
  29094. SetOffsetY(FOffsetY - GetDisplayRect(FirstVisible, NoColumn, False).Top);
  29095. end
  29096. else
  29097. BottomNode := Node;
  29098. end
  29099. else
  29100. begin
  29101. // Scroll as much child nodes into view as possible if the node has been expanded.
  29102. if PosHoldable then
  29103. NeedFullInvalidate := ScrollIntoView(GetLastVisible(Node, True), False)
  29104. else
  29105. begin
  29106. TopNode := Node;
  29107. NeedFullInvalidate := True;
  29108. end;
  29109. end;
  29110. end
  29111. else
  29112. begin
  29113. // If we have collapsed the node or toAutoScrollOnExpand is not set, we try to keep the nodes
  29114. // visual position.
  29115. if toChildrenAbove in FOptions.FPaintOptions then
  29116. SetOffsetY(FOffsetY - Integer(HeightDelta));
  29117. NeedFullInvalidate := True;
  29118. end;
  29119. end;
  29120. //UpdateScrollBars(True); Moved up
  29121. // Check for automatically scrolled tree.
  29122. if NeedFullInvalidate then
  29123. Invalidate
  29124. else
  29125. InvalidateToBottom(Node);
  29126. end
  29127. else
  29128. InvalidateNode(Node);
  29129. end
  29130. else
  29131. UpdateRanges;
  29132. end;
  29133. finally
  29134. Exclude(Node.States, vsToggling);
  29135. if not TogglingTree then
  29136. DoStateChange([], [tsToggling]);
  29137. end;
  29138. end;
  29139. end;
  29140. //----------------------------------------------------------------------------------------------------------------------
  29141. function TBaseVirtualTree.UpdateAction(Action: TBasicAction): Boolean;
  29142. // Support for standard actions.
  29143. begin
  29144. if not Focused then
  29145. Result := inherited UpdateAction(Action)
  29146. else
  29147. begin
  29148. Result := (Action is TEditCut) or (Action is TEditCopy) or (Action is TEditDelete);
  29149. if Result then
  29150. TAction(Action).Enabled := (FSelectionCount > 0) and ((Action is TEditDelete) or (FClipboardFormats.Count > 0))
  29151. else
  29152. begin
  29153. Result := Action is TEditPaste;
  29154. if Result then
  29155. TAction(Action).Enabled := True
  29156. else
  29157. begin
  29158. Result := Action is TEditSelectAll;
  29159. if Result then
  29160. TAction(Action).Enabled := (toMultiSelect in FOptions.FSelectionOptions) and (FVisibleCount > 0)
  29161. else
  29162. Result := inherited UpdateAction(Action);
  29163. end;
  29164. end;
  29165. end;
  29166. end;
  29167. //----------------------------------------------------------------------------------------------------------------------
  29168. procedure TBaseVirtualTree.UpdateHorizontalRange;
  29169. begin
  29170. if FHeader.UseColumns then
  29171. FRangeX := FHeader.FColumns.TotalWidth
  29172. else
  29173. FRangeX := GetMaxRightExtend;
  29174. end;
  29175. //----------------------------------------------------------------------------------------------------------------------
  29176. procedure TBaseVirtualTree.UpdateHorizontalScrollBar(DoRepaint: Boolean);
  29177. var
  29178. ScrollInfo: TScrollInfo;
  29179. begin
  29180. UpdateHorizontalRange;
  29181. if (tsUpdating in FStates) or not HandleAllocated then
  29182. Exit;
  29183. // Adjust effect scroll offset depending on bidi mode.
  29184. if UseRightToLeftAlignment then
  29185. FEffectiveOffsetX := Integer(FRangeX) - ClientWidth + FOffsetX
  29186. else
  29187. FEffectiveOffsetX := -FOffsetX;
  29188. if FScrollBarOptions.ScrollBars in [{$if CompilerVersion >= 24}System.UITypes.TScrollStyle.{$ifend}ssHorizontal, {$if CompilerVersion >= 24}System.UITypes.TScrollStyle.{$ifend}ssBoth] then
  29189. begin
  29190. ZeroMemory (@ScrollInfo, SizeOf(ScrollInfo));
  29191. ScrollInfo.cbSize := SizeOf(ScrollInfo);
  29192. ScrollInfo.fMask := SIF_ALL;
  29193. GetScrollInfo(Handle, SB_HORZ, ScrollInfo);
  29194. if (Integer(FRangeX) > ClientWidth) or FScrollBarOptions.AlwaysVisible then
  29195. begin
  29196. DoShowScrollBar(SB_HORZ, True);
  29197. ScrollInfo.nMin := 0;
  29198. ScrollInfo.nMax := FRangeX;
  29199. ScrollInfo.nPos := FEffectiveOffsetX;
  29200. ScrollInfo.nPage := Max(0, ClientWidth + 1);
  29201. ScrollInfo.fMask := SIF_ALL or ScrollMasks[FScrollBarOptions.AlwaysVisible];
  29202. SetScrollInfo(Handle, SB_HORZ, ScrollInfo, DoRepaint);
  29203. end
  29204. else
  29205. begin
  29206. ScrollInfo.nMin := 0;
  29207. ScrollInfo.nMax := 0;
  29208. ScrollInfo.nPos := 0;
  29209. ScrollInfo.nPage := 0;
  29210. DoShowScrollBar(SB_HORZ, False);
  29211. SetScrollInfo(Handle, SB_HORZ, ScrollInfo, False);
  29212. end;
  29213. // Since the position is automatically changed if it doesn't meet the range
  29214. // we better read the current position back to stay synchronized.
  29215. FEffectiveOffsetX := GetScrollPos(Handle, SB_HORZ);
  29216. if UseRightToLeftAlignment then
  29217. SetOffsetX(-Integer(FRangeX) + ClientWidth + FEffectiveOffsetX)
  29218. else
  29219. SetOffsetX(-FEffectiveOffsetX);
  29220. end
  29221. else
  29222. begin
  29223. DoShowScrollBar(SB_HORZ, False);
  29224. // Reset the current horizontal offset to account for window resize etc.
  29225. SetOffsetX(FOffsetX);
  29226. end;
  29227. end;
  29228. //----------------------------------------------------------------------------------------------------------------------
  29229. procedure TBaseVirtualTree.UpdateRanges;
  29230. begin
  29231. UpdateVerticalRange;
  29232. UpdateHorizontalRange;
  29233. end;
  29234. //----------------------------------------------------------------------------------------------------------------------
  29235. procedure TBaseVirtualTree.UpdateScrollBars(DoRepaint: Boolean);
  29236. // adjusts scrollbars to reflect current size and paint offset of the tree
  29237. begin
  29238. if HandleAllocated then
  29239. begin
  29240. UpdateVerticalScrollBar(DoRepaint);
  29241. UpdateHorizontalScrollBar(DoRepaint);
  29242. Perform(CM_UPDATE_VCLSTYLE_SCROLLBARS,0,0);
  29243. end;
  29244. end;
  29245. //----------------------------------------------------------------------------------------------------------------------
  29246. procedure TBaseVirtualTree.UpdateVerticalRange;
  29247. begin
  29248. // Total node height includes the height of the invisible root node.
  29249. if FRoot.TotalHeight < FDefaultNodeHeight then
  29250. FRoot.TotalHeight := FDefaultNodeHeight;
  29251. FRangeY := FRoot.TotalHeight - FRoot.NodeHeight + FBottomSpace;
  29252. end;
  29253. //----------------------------------------------------------------------------------------------------------------------
  29254. procedure TBaseVirtualTree.UpdateVerticalScrollBar(DoRepaint: Boolean);
  29255. var
  29256. ScrollInfo: TScrollInfo;
  29257. begin
  29258. UpdateVerticalRange;
  29259. if tsUpdating in FStates then
  29260. Exit;
  29261. if FScrollBarOptions.ScrollBars in [ssVertical, ssBoth] then
  29262. begin
  29263. ScrollInfo.cbSize := SizeOf(ScrollInfo);
  29264. ScrollInfo.fMask := SIF_ALL;
  29265. GetScrollInfo(Handle, SB_VERT, ScrollInfo);
  29266. if (Integer(FRangeY) > ClientHeight) or FScrollBarOptions.AlwaysVisible then
  29267. begin
  29268. DoShowScrollBar(SB_VERT, True);
  29269. ScrollInfo.nMin := 0;
  29270. ScrollInfo.nMax := FRangeY;
  29271. ScrollInfo.nPos := -FOffsetY;
  29272. ScrollInfo.nPage := Max(0, ClientHeight + 1);
  29273. ScrollInfo.fMask := SIF_ALL or ScrollMasks[FScrollBarOptions.AlwaysVisible];
  29274. SetScrollInfo(Handle, SB_VERT, ScrollInfo, DoRepaint);
  29275. end
  29276. else
  29277. begin
  29278. ScrollInfo.nMin := 0;
  29279. ScrollInfo.nMax := 0;
  29280. ScrollInfo.nPos := 0;
  29281. ScrollInfo.nPage := 0;
  29282. DoShowScrollBar(SB_VERT, False);
  29283. SetScrollInfo(Handle, SB_VERT, ScrollInfo, False);
  29284. end;
  29285. // Since the position is automatically changed if it doesn't meet the range
  29286. // we better read the current position back to stay synchronized.
  29287. SetOffsetY(-GetScrollPos(Handle, SB_VERT));
  29288. end
  29289. else
  29290. begin
  29291. DoShowScrollBar(SB_VERT, False);
  29292. // Reset the current vertical offset to account for window resize etc.
  29293. SetOffsetY(FOffsetY);
  29294. end;
  29295. end;
  29296. //----------------------------------------------------------------------------------------------------------------------
  29297. function TBaseVirtualTree.UseRightToLeftReading: Boolean;
  29298. // The tree can handle right-to-left reading also on non-middle-east systems, so we cannot use the same function as
  29299. // it is implemented in TControl.
  29300. begin
  29301. Result := BiDiMode <> bdLeftToRight;
  29302. end;
  29303. //----------------------------------------------------------------------------------------------------------------------
  29304. procedure TBaseVirtualTree.ValidateChildren(Node: PVirtualNode; Recursive: Boolean);
  29305. // Ensures that the children of the given node (and all their children, if Recursive is True) are initialized.
  29306. // Node must already be initialized
  29307. var
  29308. Child: PVirtualNode;
  29309. begin
  29310. if Node = nil then
  29311. Node := FRoot;
  29312. if (vsHasChildren in Node.States) and (Node.ChildCount = 0) then
  29313. InitChildren(Node);
  29314. Child := Node.FirstChild;
  29315. while Assigned(Child) do
  29316. begin
  29317. ValidateNode(Child, Recursive);
  29318. Child := Child.NextSibling;
  29319. end;
  29320. end;
  29321. //----------------------------------------------------------------------------------------------------------------------
  29322. procedure TBaseVirtualTree.ValidateNode(Node: PVirtualNode; Recursive: Boolean);
  29323. // Ensures that the given node (and all its children, if Recursive is True) are initialized.
  29324. var
  29325. Child: PVirtualNode;
  29326. begin
  29327. if Node = nil then
  29328. Node := FRoot
  29329. else
  29330. if not (vsInitialized in Node.States) then
  29331. InitNode(Node);
  29332. if Recursive then
  29333. begin
  29334. if (vsHasChildren in Node.States) and (Node.ChildCount = 0) then
  29335. InitChildren(Node);
  29336. Child := Node.FirstChild;
  29337. while Assigned(Child) do
  29338. begin
  29339. ValidateNode(Child, Recursive);
  29340. Child := Child.NextSibling;
  29341. end;
  29342. end;
  29343. end;
  29344. //----------------- TCustomStringTreeOptions ---------------------------------------------------------------------------
  29345. constructor TCustomStringTreeOptions.Create(AOwner: TBaseVirtualTree);
  29346. begin
  29347. inherited;
  29348. FStringOptions := DefaultStringOptions;
  29349. end;
  29350. //----------------------------------------------------------------------------------------------------------------------
  29351. procedure TCustomStringTreeOptions.SetStringOptions(const Value: TVTStringOptions);
  29352. var
  29353. ChangedOptions: TVTStringOptions;
  29354. begin
  29355. if FStringOptions <> Value then
  29356. begin
  29357. // Exclusive ORing to get all entries wich are in either set but not in both.
  29358. ChangedOptions := FStringOptions + Value - (FStringOptions * Value);
  29359. FStringOptions := Value;
  29360. with FOwner do
  29361. if (toShowStaticText in ChangedOptions) and not (csLoading in ComponentState) and HandleAllocated then
  29362. Invalidate;
  29363. end;
  29364. end;
  29365. //----------------------------------------------------------------------------------------------------------------------
  29366. procedure TCustomStringTreeOptions.AssignTo(Dest: TPersistent);
  29367. begin
  29368. if Dest is TCustomStringTreeOptions then
  29369. begin
  29370. with Dest as TCustomStringTreeOptions do
  29371. StringOptions := Self.StringOptions;
  29372. end;
  29373. // Let ancestors assign their options to the destination class.
  29374. inherited;
  29375. end;
  29376. //----------------- TVTEdit --------------------------------------------------------------------------------------------
  29377. // Implementation of a generic node caption editor.
  29378. constructor TVTEdit.Create(Link: TStringEditLink);
  29379. begin
  29380. inherited Create(nil);
  29381. ShowHint := False;
  29382. ParentShowHint := False;
  29383. // This assignment increases the reference count for the interface.
  29384. FRefLink := Link;
  29385. // This reference is used to access the link.
  29386. FLink := Link;
  29387. end;
  29388. //----------------------------------------------------------------------------------------------------------------------
  29389. procedure TVTEdit.CMAutoAdjust(var Message: TMessage);
  29390. begin
  29391. AutoAdjustSize;
  29392. end;
  29393. //----------------------------------------------------------------------------------------------------------------------
  29394. procedure TVTEdit.CMExit(var Message: TMessage);
  29395. begin
  29396. if Assigned(FLink) and not FLink.FStopping then
  29397. with FLink, FTree do
  29398. begin
  29399. if (toAutoAcceptEditChange in TreeOptions.StringOptions) then
  29400. DoEndEdit
  29401. else
  29402. DoCancelEdit;
  29403. end;
  29404. end;
  29405. //----------------------------------------------------------------------------------------------------------------------
  29406. procedure TVTEdit.CMRelease(var Message: TMessage);
  29407. begin
  29408. Free;
  29409. end;
  29410. //----------------------------------------------------------------------------------------------------------------------
  29411. procedure TVTEdit.CNCommand(var Message: TWMCommand);
  29412. begin
  29413. if Assigned(FLink) and Assigned(FLink.FTree) and (Message.NotifyCode = EN_UPDATE) and
  29414. not (vsMultiline in FLink.FNode.States) then
  29415. // Instead directly calling AutoAdjustSize it is necessary on Win9x/Me to decouple this notification message
  29416. // and eventual resizing. Hence we use a message to accomplish that.
  29417. AutoAdjustSize()
  29418. else
  29419. inherited;
  29420. end;
  29421. //----------------------------------------------------------------------------------------------------------------------
  29422. procedure TVTEdit.WMChar(var Message: TWMChar);
  29423. begin
  29424. if not (Message.CharCode in [VK_ESCAPE, VK_TAB]) then
  29425. inherited;
  29426. end;
  29427. //----------------------------------------------------------------------------------------------------------------------
  29428. procedure TVTEdit.WMDestroy(var Message: TWMDestroy);
  29429. begin
  29430. // If editing stopped by other means than accept or cancel then we have to do default processing for
  29431. // pending changes.
  29432. if Assigned(FLink) and not FLink.FStopping then
  29433. begin
  29434. with FLink, FTree do
  29435. begin
  29436. if (toAutoAcceptEditChange in TreeOptions.StringOptions) and Modified then
  29437. Text[FNode, FColumn] := FEdit.Text;
  29438. end;
  29439. FLink := nil;
  29440. FRefLink := nil;
  29441. end;
  29442. inherited;
  29443. end;
  29444. //----------------------------------------------------------------------------------------------------------------------
  29445. procedure TVTEdit.WMGetDlgCode(var Message: TWMGetDlgCode);
  29446. begin
  29447. inherited;
  29448. Message.Result := Message.Result or DLGC_WANTALLKEYS or DLGC_WANTTAB or DLGC_WANTARROWS;
  29449. end;
  29450. //----------------------------------------------------------------------------------------------------------------------
  29451. procedure TVTEdit.WMKeyDown(var Message: TWMKeyDown);
  29452. // Handles some control keys.
  29453. var
  29454. Shift: TShiftState;
  29455. EndEdit: Boolean;
  29456. Tree: TBaseVirtualTree;
  29457. NextNode: PVirtualNode;
  29458. begin
  29459. Tree := FLink.FTree;
  29460. case Message.CharCode of
  29461. VK_ESCAPE:
  29462. begin
  29463. Tree.DoCancelEdit;
  29464. Tree.SetFocus;
  29465. end;
  29466. VK_RETURN:
  29467. begin
  29468. EndEdit := not (vsMultiline in FLink.FNode.States);
  29469. if not EndEdit then
  29470. begin
  29471. // If a multiline node is being edited the finish editing only if Ctrl+Enter was pressed,
  29472. // otherwise allow to insert line breaks into the text.
  29473. Shift := KeyDataToShiftState(Message.KeyData);
  29474. EndEdit := ssCtrl in Shift;
  29475. end;
  29476. if EndEdit then
  29477. begin
  29478. Tree := FLink.FTree;
  29479. FLink.FTree.InvalidateNode(FLink.FNode);
  29480. FLink.FTree.DoEndEdit;
  29481. Tree.SetFocus;
  29482. end;
  29483. end;
  29484. VK_UP:
  29485. begin
  29486. if not (vsMultiline in FLink.FNode.States) then
  29487. Message.CharCode := VK_LEFT;
  29488. inherited;
  29489. end;
  29490. VK_DOWN:
  29491. begin
  29492. if not (vsMultiline in FLink.FNode.States) then
  29493. Message.CharCode := VK_RIGHT;
  29494. inherited;
  29495. end;
  29496. VK_TAB:
  29497. begin
  29498. if Tree.IsEditing then
  29499. begin
  29500. Tree.InvalidateNode(FLink.FNode);
  29501. NextNode := Tree.GetNextVisible(FLink.FNode, True);
  29502. Tree.EndEditNode;
  29503. Tree.FocusedNode := NextNode;
  29504. if Tree.CanEdit(Tree.FocusedNode, Tree.FocusedColumn) then
  29505. Tree.DoEdit;
  29506. end;
  29507. end;
  29508. Ord('A'):
  29509. begin
  29510. if Tree.IsEditing and ([ssCtrl] = KeyboardStateToShiftState) then
  29511. begin
  29512. Self.SelectAll();
  29513. Message.CharCode := 0;
  29514. end;
  29515. end;
  29516. else
  29517. inherited;
  29518. end;
  29519. end;
  29520. //----------------------------------------------------------------------------------------------------------------------
  29521. procedure TVTEdit.AutoAdjustSize;
  29522. // Changes the size of the edit to accomodate as much as possible of its text within its container window.
  29523. // NewChar describes the next character which will be added to the edit's text.
  29524. var
  29525. DC: HDC;
  29526. Size: TSize;
  29527. LastFont: THandle;
  29528. begin
  29529. if not (vsMultiline in FLink.FNode.States) and not (toGridExtensions in FLink.FTree.FOptions.FMiscOptions{see issue #252}) then
  29530. begin
  29531. // avoid flicker
  29532. SendMessage(Handle, WM_SETREDRAW, 0, 0);
  29533. DC := GetDC(Handle);
  29534. LastFont := SelectObject(DC, Font.Handle);
  29535. try
  29536. // Read needed space for the current text.
  29537. {$ifdef TntSupport}
  29538. GetTextExtentPoint32W(DC, PWideChar(Text), Length(Text), Size);
  29539. {$else}
  29540. GetTextExtentPoint32(DC, PChar(Text+'yG'), Length(Text)+2, Size);
  29541. {$endif TntSupport}
  29542. Inc(Size.cx, 2 * FLink.FTree.FTextMargin);
  29543. Inc(Size.cy, 2 * FLink.FTree.FTextMargin);
  29544. Height := Max(Size.cy, Height); // Ensure a minimum height so that the edit field's content and cursor are displayed correctly. See #159
  29545. // Repaint associated node if the edit becomes smaller.
  29546. if Size.cx < Width then
  29547. FLink.FTree.Invalidate();
  29548. if FLink.FAlignment = taRightJustify then
  29549. FLink.SetBounds(Rect(Left + Width - Size.cx, Top, Left + Width, Top + Height))
  29550. else
  29551. FLink.SetBounds(Rect(Left, Top, Left + Size.cx, Top + Height));
  29552. finally
  29553. SelectObject(DC, LastFont);
  29554. ReleaseDC(Handle, DC);
  29555. SendMessage(Handle, WM_SETREDRAW, 1, 0);
  29556. end;
  29557. end;
  29558. end;
  29559. //----------------------------------------------------------------------------------------------------------------------
  29560. procedure TVTEdit.CreateParams(var Params: TCreateParams);
  29561. begin
  29562. inherited;
  29563. // Only with multiline style we can use the text formatting rectangle.
  29564. // This does not harm formatting as single line control, if we don't use word wrapping.
  29565. with Params do
  29566. begin
  29567. Style := Style or ES_MULTILINE;
  29568. if vsMultiline in FLink.FNode.States then
  29569. Style := Style and not (ES_AUTOHSCROLL or WS_HSCROLL) or WS_VSCROLL or ES_AUTOVSCROLL;
  29570. if tsUseThemes in FLink.FTree.FStates then
  29571. begin
  29572. Style := Style and not WS_BORDER;
  29573. ExStyle := ExStyle or WS_EX_CLIENTEDGE;
  29574. end
  29575. else
  29576. begin
  29577. Style := Style or WS_BORDER;
  29578. ExStyle := ExStyle and not WS_EX_CLIENTEDGE;
  29579. end;
  29580. end;
  29581. end;
  29582. //----------------------------------------------------------------------------------------------------------------------
  29583. procedure TVTEdit.Release;
  29584. begin
  29585. if HandleAllocated then
  29586. PostMessage(Handle, CM_RELEASE, 0, 0);
  29587. end;
  29588. //----------------- TStringEditLink ------------------------------------------------------------------------------------
  29589. constructor TStringEditLink.Create;
  29590. begin
  29591. inherited;
  29592. FEdit := TVTEdit.Create(Self);
  29593. with FEdit do
  29594. begin
  29595. Visible := False;
  29596. BorderStyle := bsSingle;
  29597. AutoSize := False;
  29598. end;
  29599. end;
  29600. //----------------------------------------------------------------------------------------------------------------------
  29601. destructor TStringEditLink.Destroy;
  29602. begin
  29603. if Assigned(FEdit) then
  29604. FEdit.Release;
  29605. inherited;
  29606. end;
  29607. //----------------------------------------------------------------------------------------------------------------------
  29608. function TStringEditLink.BeginEdit: Boolean;
  29609. // Notifies the edit link that editing can start now. descendants may cancel node edit
  29610. // by returning False.
  29611. begin
  29612. Result := not FStopping;
  29613. if Result then
  29614. begin
  29615. FEdit.Show;
  29616. FEdit.SelectAll;
  29617. FEdit.SetFocus;
  29618. FEdit.AutoAdjustSize;
  29619. end;
  29620. end;
  29621. //----------------------------------------------------------------------------------------------------------------------
  29622. procedure TStringEditLink.SetEdit(const Value: TVTEdit);
  29623. begin
  29624. if Assigned(FEdit) then
  29625. FEdit.Free;
  29626. FEdit := Value;
  29627. end;
  29628. //----------------------------------------------------------------------------------------------------------------------
  29629. function TStringEditLink.CancelEdit: Boolean;
  29630. begin
  29631. Result := not FStopping;
  29632. if Result then
  29633. begin
  29634. FStopping := True;
  29635. FEdit.Hide;
  29636. FTree.CancelEditNode;
  29637. FEdit.FLink := nil;
  29638. FEdit.FRefLink := nil;
  29639. end;
  29640. end;
  29641. //----------------------------------------------------------------------------------------------------------------------
  29642. function TStringEditLink.EndEdit: Boolean;
  29643. begin
  29644. Result := not FStopping;
  29645. if Result then
  29646. try
  29647. FStopping := True;
  29648. if FEdit.Modified then
  29649. FTree.Text[FNode, FColumn] := FEdit.Text;
  29650. FEdit.Hide;
  29651. FEdit.FLink := nil;
  29652. FEdit.FRefLink := nil;
  29653. except
  29654. FStopping := False;
  29655. raise;
  29656. end;
  29657. end;
  29658. //----------------------------------------------------------------------------------------------------------------------
  29659. function TStringEditLink.GetBounds: TRect;
  29660. begin
  29661. Result := FEdit.BoundsRect;
  29662. end;
  29663. //----------------------------------------------------------------------------------------------------------------------
  29664. function TStringEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean;
  29665. // Retrieves the true text bounds from the owner tree.
  29666. var
  29667. Text: UnicodeString;
  29668. begin
  29669. Result := Tree is TCustomVirtualStringTree;
  29670. if Result then
  29671. begin
  29672. if not Assigned(FEdit) then
  29673. begin
  29674. FEdit := TVTEdit.Create(Self);
  29675. FEdit.Visible := False;
  29676. FEdit.BorderStyle := bsSingle;
  29677. FEdit.AutoSize := False;
  29678. end;
  29679. FTree := Tree as TCustomVirtualStringTree;
  29680. FNode := Node;
  29681. FColumn := Column;
  29682. // Initial size, font and text of the node.
  29683. FTree.GetTextInfo(Node, Column, FEdit.Font, FTextBounds, Text);
  29684. FEdit.Font.Color := clWindowText;
  29685. FEdit.Parent := Tree;
  29686. FEdit.RecreateWnd;
  29687. FEdit.HandleNeeded;
  29688. FEdit.Text := Text;
  29689. if Column <= NoColumn then
  29690. begin
  29691. FEdit.BidiMode := FTree.BidiMode;
  29692. FAlignment := FTree.Alignment;
  29693. end
  29694. else
  29695. begin
  29696. FEdit.BidiMode := FTree.Header.Columns[Column].BidiMode;
  29697. FAlignment := FTree.Header.Columns[Column].Alignment;
  29698. end;
  29699. if FEdit.BidiMode <> bdLeftToRight then
  29700. ChangeBidiModeAlignment(FAlignment);
  29701. end;
  29702. end;
  29703. //----------------------------------------------------------------------------------------------------------------------
  29704. procedure TStringEditLink.ProcessMessage(var Message: TMessage);
  29705. begin
  29706. FEdit.WindowProc(Message);
  29707. end;
  29708. //----------------------------------------------------------------------------------------------------------------------
  29709. procedure TStringEditLink.SetBounds(R: TRect);
  29710. // Sets the outer bounds of the edit control and the actual edit area in the control.
  29711. var
  29712. lOffset: Integer;
  29713. begin
  29714. if not FStopping then
  29715. begin
  29716. // Set the edit's bounds but make sure there's a minimum width and the right border does not
  29717. // extend beyond the parent's left/right border.
  29718. if R.Left < 0 then
  29719. R.Left := 0;
  29720. if R.Right - R.Left < 30 then
  29721. begin
  29722. if FAlignment = taRightJustify then
  29723. R.Left := R.Right - 30
  29724. else
  29725. R.Right := R.Left + 30;
  29726. end;
  29727. if R.Right > FTree.ClientWidth then
  29728. R.Right := FTree.ClientWidth;
  29729. FEdit.BoundsRect := R;
  29730. // The selected text shall exclude the text margins and be centered vertically.
  29731. // We have to take out the two pixel border of the edit control as well as a one pixel "edit border" the
  29732. // control leaves around the (selected) text.
  29733. R := FEdit.ClientRect;
  29734. lOffset := IfThen(vsMultiline in FNode.States, 0, 2);
  29735. if tsUseThemes in FTree.FStates then
  29736. Inc(lOffset);
  29737. InflateRect(R, -FTree.FTextMargin + lOffset, lOffset);
  29738. if not (vsMultiline in FNode.States) then
  29739. OffsetRect(R, 0, FTextBounds.Top - FEdit.Top);
  29740. R.Top := Max(-1, R.Top); // A value smaller than -1 will prevent the edit cursor from being shown by Windows, see issue #159
  29741. R.Left := Max(-1, R.Left);
  29742. SendMessage(FEdit.Handle, EM_SETRECTNP, 0, LPARAM(@R));
  29743. end;
  29744. end;
  29745. //----------------- TCustomVirtualString -------------------------------------------------------------------------------
  29746. constructor TCustomVirtualStringTree.Create(AOwner: TComponent);
  29747. begin
  29748. inherited;
  29749. FPreviouslySelected := nil;
  29750. FDefaultText := 'Node';
  29751. FInternalDataOffset := AllocateInternalDataArea(SizeOf(Cardinal));
  29752. end;
  29753. //----------------------------------------------------------------------------------------------------------------------
  29754. procedure TCustomVirtualStringTree.GetRenderStartValues(Source: TVSTTextSourceType; var Node: PVirtualNode;
  29755. var NextNodeProc: TGetNextNodeProc);
  29756. begin
  29757. case Source of
  29758. tstInitialized:
  29759. begin
  29760. Node := GetFirstInitialized;
  29761. NextNodeProc := GetNextInitialized;
  29762. end;
  29763. tstSelected:
  29764. begin
  29765. Node := GetFirstSelected;
  29766. NextNodeProc := GetNextSelected;
  29767. end;
  29768. tstCutCopySet:
  29769. begin
  29770. Node := GetFirstCutCopy;
  29771. NextNodeProc := GetNextCutCopy;
  29772. end;
  29773. tstVisible:
  29774. begin
  29775. Node := GetFirstVisible(nil, True);
  29776. NextNodeProc := GetNextVisible;
  29777. end;
  29778. tstChecked:
  29779. begin
  29780. Node := GetFirstChecked;
  29781. NextNodeProc := GetNextChecked;
  29782. end;
  29783. else // tstAll
  29784. Node := GetFirst;
  29785. NextNodeProc := GetNext;
  29786. end;
  29787. end;
  29788. //----------------------------------------------------------------------------------------------------------------------
  29789. procedure TCustomVirtualStringTree.GetDataFromGrid(const AStrings: TStringList;
  29790. const IncludeHeading: Boolean);
  29791. var
  29792. LColIndex : Integer;
  29793. LStartIndex : Integer;
  29794. LAddString : string;
  29795. LCellText : string;
  29796. LChildNode : PVirtualNode;
  29797. begin
  29798. { Start from the First column. }
  29799. LStartIndex := 0;
  29800. { Do it for Header first }
  29801. if IncludeHeading then
  29802. begin
  29803. LAddString := EmptyStr;
  29804. for LColIndex := LStartIndex to Pred(Header.Columns.Count) do
  29805. begin
  29806. if (LColIndex > LStartIndex) then
  29807. LAddString := LAddString + ',';
  29808. LAddString := LAddString + AnsiQuotedStr(Header.Columns.Items[LColIndex].Text, '"');
  29809. end;//for
  29810. AStrings.Add(LAddString);
  29811. end;//if
  29812. { Loop thru the virtual tree for Data }
  29813. LChildNode := GetFirst;
  29814. while Assigned(LChildNode) do
  29815. begin
  29816. LAddString := EmptyStr;
  29817. { Read for each column and then populate the text }
  29818. for LColIndex := LStartIndex to Pred(Header.Columns.Count) do
  29819. begin
  29820. LCellText := Text[LChildNode, LColIndex];
  29821. if (LCellText = EmptyStr) then
  29822. LCellText := ' ';
  29823. if (LColIndex > LStartIndex) then
  29824. LAddString := LAddString + ',';
  29825. LAddString := LAddString + AnsiQuotedStr(LCellText, '"');
  29826. end;//for - Header.Columns.Count
  29827. AStrings.Add(LAddString);
  29828. LChildNode := LChildNode.NextSibling;
  29829. end;//while Assigned(LChildNode);
  29830. end;
  29831. function TCustomVirtualStringTree.GetImageText(Node: PVirtualNode;
  29832. Kind: TVTImageKind; Column: TColumnIndex): UnicodeString;
  29833. begin
  29834. Assert(Assigned(Node), 'Node must not be nil.');
  29835. if not (vsInitialized in Node.States) then
  29836. InitNode(Node);
  29837. Result := '';
  29838. DoGetImageText(Node, Kind, Column, Result);
  29839. end;
  29840. //----------------------------------------------------------------------------------------------------------------------
  29841. function TCustomVirtualStringTree.GetOptions: TCustomStringTreeOptions;
  29842. begin
  29843. Result := FOptions as TCustomStringTreeOptions;
  29844. end;
  29845. //----------------------------------------------------------------------------------------------------------------------
  29846. function TCustomVirtualStringTree.GetStaticText(Node: PVirtualNode; Column: TColumnIndex): UnicodeString;
  29847. begin
  29848. Assert(Assigned(Node), 'Node must not be nil.');
  29849. if not (vsInitialized in Node.States) then
  29850. InitNode(Node);
  29851. Result := '';
  29852. DoGetText(Node, Column, ttStatic, Result);
  29853. end;
  29854. //----------------------------------------------------------------------------------------------------------------------
  29855. function TCustomVirtualStringTree.GetText(Node: PVirtualNode; Column: TColumnIndex): UnicodeString;
  29856. begin
  29857. Assert(Assigned(Node), 'Node must not be nil.');
  29858. if not (vsInitialized in Node.States) then
  29859. InitNode(Node);
  29860. Result := FDefaultText;
  29861. DoGetText(Node, Column, ttNormal, Result);
  29862. end;
  29863. //----------------------------------------------------------------------------------------------------------------------
  29864. procedure TCustomVirtualStringTree.InitializeTextProperties(var PaintInfo: TVTPaintInfo);
  29865. // Initializes default values for customization in PaintNormalText.
  29866. begin
  29867. with PaintInfo do
  29868. begin
  29869. // Set default font values first.
  29870. Canvas.Font := Font;
  29871. if Enabled then // Es werden sonst nur die Farben verwendet von Font die an Canvas.Font übergeben wurden
  29872. Canvas.Font.Color := FColors.NodeFontColor
  29873. else
  29874. Canvas.Font.Color := FColors.DisabledColor;
  29875. if (toHotTrack in FOptions.FPaintOptions) and (Node = FCurrentHotNode) then
  29876. begin
  29877. if not (tsUseExplorerTheme in FStates) then
  29878. begin
  29879. Canvas.Font.Style := Canvas.Font.Style + [fsUnderline];
  29880. Canvas.Font.Color := FColors.HotColor;
  29881. end;
  29882. end;
  29883. // Change the font color only if the node also is drawn in selected style.
  29884. if poDrawSelection in PaintOptions then
  29885. begin
  29886. if (Column = FFocusedColumn) or (toFullRowSelect in FOptions.FSelectionOptions) then
  29887. begin
  29888. if Node = FDropTargetNode then
  29889. begin
  29890. if ((FLastDropMode = dmOnNode) or (vsSelected in Node.States)) and not
  29891. (tsUseExplorerTheme in FStates) then
  29892. Canvas.Font.Color := FColors.SelectionTextColor;
  29893. end
  29894. else
  29895. if vsSelected in Node.States then
  29896. begin
  29897. if (Focused or (toPopupMode in FOptions.FPaintOptions)) and not
  29898. (tsUseExplorerTheme in FStates) then
  29899. Canvas.Font.Color := FColors.SelectionTextColor;
  29900. end;
  29901. end;
  29902. end;
  29903. end;
  29904. end;
  29905. //----------------------------------------------------------------------------------------------------------------------
  29906. procedure TCustomVirtualStringTree.PaintNormalText(var PaintInfo: TVTPaintInfo; TextOutFlags: Integer;
  29907. Text: UnicodeString);
  29908. // This method is responsible for painting the given text to target canvas (under consideration of the given rectangles).
  29909. // The text drawn here is considered as the normal text in a node.
  29910. // Note: NodeWidth is the actual width of the text to be drawn. This does not necessarily correspond to the width of
  29911. // the node rectangle. The clipping rectangle comprises the entire node (including tree lines, buttons etc.).
  29912. var
  29913. TripleWidth: Integer;
  29914. R: TRect;
  29915. DrawFormat: Cardinal;
  29916. Size: TSize;
  29917. Height: Integer;
  29918. begin
  29919. InitializeTextProperties(PaintInfo);
  29920. with PaintInfo do
  29921. begin
  29922. R := ContentRect;
  29923. Canvas.TextFlags := 0;
  29924. InflateRect(R, -FTextMargin, 0);
  29925. // Multiline nodes don't need special font handling or text manipulation.
  29926. // Note: multiline support requires the Unicode version of DrawText, which is able to do word breaking.
  29927. // The emulation in this unit does not support this so we have to use the OS version. However
  29928. // DrawTextW is only available on NT/2000/XP and up. Hence there is only partial multiline support
  29929. // for 9x/Me.
  29930. if vsMultiline in Node.States then
  29931. begin
  29932. DoPaintText(Node, Canvas, Column, ttNormal);
  29933. Height := ComputeNodeHeight(Canvas, Node, Column);
  29934. // Disabled node color overrides all other variants.
  29935. if (vsDisabled in Node.States) or not Enabled then
  29936. Canvas.Font.Color := FColors.DisabledColor;
  29937. // The edit control flag will ensure that no partial line is displayed, that is, only lines
  29938. // which are (vertically) fully visible are drawn.
  29939. DrawFormat := DT_NOPREFIX or DT_WORDBREAK or DT_END_ELLIPSIS or DT_EDITCONTROL or AlignmentToDrawFlag[Alignment];
  29940. if BidiMode <> bdLeftToRight then
  29941. DrawFormat := DrawFormat or DT_RTLREADING;
  29942. // Center the text vertically if it fits entirely into the content rect.
  29943. if R.Bottom - R.Top > Height then
  29944. InflateRect(R, 0, (Height - R.Bottom - R.Top) div 2);
  29945. end
  29946. else
  29947. begin
  29948. FFontChanged := False;
  29949. TripleWidth := FEllipsisWidth;
  29950. DoPaintText(Node, Canvas, Column, ttNormal);
  29951. if FFontChanged then
  29952. begin
  29953. // If the font has been changed then the ellipsis width must be recalculated.
  29954. TripleWidth := 0;
  29955. // Recalculate also the width of the normal text.
  29956. GetTextExtentPoint32W(Canvas.Handle, PWideChar(Text), Length(Text), Size);
  29957. NodeWidth := Size.cx + 2 * FTextMargin;
  29958. end;
  29959. // Disabled node color overrides all other variants.
  29960. if (vsDisabled in Node.States) or not Enabled then
  29961. Canvas.Font.Color := FColors.DisabledColor;
  29962. DrawFormat := DT_NOPREFIX or DT_VCENTER or DT_SINGLELINE;
  29963. if BidiMode <> bdLeftToRight then
  29964. DrawFormat := DrawFormat or DT_RTLREADING;
  29965. // Check if the text must be shortend.
  29966. if (Column > -1) and ((NodeWidth - 2 * FTextMargin) > R.Right - R.Left) then
  29967. begin
  29968. Text := DoShortenString(Canvas, Node, Column, Text, R.Right - R.Left, TripleWidth);
  29969. if Alignment = taRightJustify then
  29970. DrawFormat := DrawFormat or DT_RIGHT
  29971. else
  29972. DrawFormat := DrawFormat or DT_LEFT;
  29973. end
  29974. else
  29975. DrawFormat := DrawFormat or AlignmentToDrawFlag[Alignment];
  29976. end;
  29977. if Canvas.TextFlags and ETO_OPAQUE = 0 then
  29978. SetBkMode(Canvas.Handle, TRANSPARENT)
  29979. else
  29980. SetBkMode(Canvas.Handle, OPAQUE);
  29981. DoTextDrawing(PaintInfo, Text, R, DrawFormat);
  29982. end;
  29983. end;
  29984. //----------------------------------------------------------------------------------------------------------------------
  29985. procedure TCustomVirtualStringTree.PaintStaticText(const PaintInfo: TVTPaintInfo; TextOutFlags: Integer;
  29986. const Text: UnicodeString);
  29987. // This method retrives and draws the static text bound to a particular node.
  29988. var
  29989. R: TRect;
  29990. DrawFormat: Cardinal;
  29991. begin
  29992. with PaintInfo do
  29993. begin
  29994. Canvas.Font := Font;
  29995. if toFullRowSelect in FOptions.FSelectionOptions then
  29996. begin
  29997. if Node = FDropTargetNode then
  29998. begin
  29999. if (FLastDropMode = dmOnNode) or (vsSelected in Node.States) then
  30000. Canvas.Font.Color := FColors.SelectionTextColor
  30001. else
  30002. Canvas.Font.Color := FColors.NodeFontColor;
  30003. end
  30004. else
  30005. if vsSelected in Node.States then
  30006. begin
  30007. if Focused or (toPopupMode in FOptions.FPaintOptions) then
  30008. Canvas.Font.Color := FColors.SelectionTextColor
  30009. else
  30010. Canvas.Font.Color := FColors.NodeFontColor;
  30011. end;
  30012. end;
  30013. DrawFormat := DT_NOPREFIX or DT_VCENTER or DT_SINGLELINE;
  30014. Canvas.TextFlags := 0;
  30015. DoPaintText(Node, Canvas, Column, ttStatic);
  30016. // Disabled node color overrides all other variants.
  30017. if (vsDisabled in Node.States) or not Enabled then
  30018. Canvas.Font.Color := FColors.DisabledColor;
  30019. R := ContentRect;
  30020. if Alignment = taRightJustify then
  30021. Dec(R.Right, NodeWidth + FTextMargin)
  30022. else
  30023. Inc(R.Left, NodeWidth + FTextMargin);
  30024. if Canvas.TextFlags and ETO_OPAQUE = 0 then
  30025. SetBkMode(Canvas.Handle, TRANSPARENT)
  30026. else
  30027. SetBkMode(Canvas.Handle, OPAQUE);
  30028. Windows.DrawTextW(Canvas.Handle, PWideChar(Text), Length(Text), R, DrawFormat);
  30029. end;
  30030. end;
  30031. //----------------------------------------------------------------------------------------------------------------------
  30032. procedure TCustomVirtualStringTree.ReadText(Reader: TReader);
  30033. begin
  30034. case Reader.NextValue of
  30035. vaLString, vaString:
  30036. SetDefaultText(Reader.ReadString);
  30037. else
  30038. SetDefaultText(Reader.{$if CompilerVersion >= 23}ReadString{$else}ReadWideString{$ifend});
  30039. end;
  30040. end;
  30041. //----------------------------------------------------------------------------------------------------------------------
  30042. function TCustomVirtualStringTree.SaveToCSVFile(
  30043. const FileNameWithPath: TFileName; const IncludeHeading: Boolean): Boolean;
  30044. var
  30045. LResultList : TStringList;
  30046. begin
  30047. Result := False;
  30048. if (FileNameWithPath = '') then
  30049. Exit;
  30050. LResultList := TStringList.Create;
  30051. try
  30052. { Get the data from grid. }
  30053. GetDataFromGrid(LResultList, IncludeHeading);
  30054. { Save File to Disk }
  30055. LResultList.SaveToFile(FileNameWithPath);
  30056. Result := True;
  30057. finally
  30058. FreeAndNil(LResultList);
  30059. end;//try-finally
  30060. end;
  30061. //----------------------------------------------------------------------------------------------------------------------
  30062. procedure TCustomVirtualStringTree.SetDefaultText(const Value: UnicodeString);
  30063. begin
  30064. if FDefaultText <> Value then
  30065. begin
  30066. FDefaultText := Value;
  30067. if not (csLoading in ComponentState) then
  30068. Invalidate;
  30069. end;
  30070. end;
  30071. //----------------------------------------------------------------------------------------------------------------------
  30072. procedure TCustomVirtualStringTree.SetOptions(const Value: TCustomStringTreeOptions);
  30073. begin
  30074. FOptions.Assign(Value);
  30075. end;
  30076. //----------------------------------------------------------------------------------------------------------------------
  30077. procedure TCustomVirtualStringTree.SetText(Node: PVirtualNode; Column: TColumnIndex; const Value: UnicodeString);
  30078. begin
  30079. DoNewText(Node, Column, Value);
  30080. InvalidateNode(Node);
  30081. end;
  30082. //----------------------------------------------------------------------------------------------------------------------
  30083. procedure TCustomVirtualStringTree.WriteText(Writer: TWriter);
  30084. begin
  30085. Writer.{$IF CompilerVersion >= 20}WriteString{$else}WriteWideString{$ifend}(FDefaultText);
  30086. end;
  30087. //----------------------------------------------------------------------------------------------------------------------
  30088. procedure TCustomVirtualStringTree.WMSetFont(var Msg: TWMSetFont);
  30089. // Whenever a new font is applied to the tree some default values are determined to avoid frequent
  30090. // determination of the same value.
  30091. var
  30092. MemDC: HDC;
  30093. Run: PVirtualNode;
  30094. TM: TTextMetric;
  30095. Size: TSize;
  30096. Data: PInteger;
  30097. begin
  30098. inherited;
  30099. MemDC := CreateCompatibleDC(0);
  30100. try
  30101. SelectObject(MemDC, Msg.Font);
  30102. GetTextMetrics(MemDC, TM);
  30103. FTextHeight := TM.tmHeight;
  30104. GetTextExtentPoint32W(MemDC, '...', 3, Size);
  30105. FEllipsisWidth := Size.cx;
  30106. finally
  30107. DeleteDC(MemDC);
  30108. end;
  30109. // Have to reset all node widths.
  30110. Run := FRoot.FirstChild;
  30111. while Assigned(Run) do
  30112. begin
  30113. Data := InternalData(Run);
  30114. if Assigned(Data) then
  30115. Data^ := 0;
  30116. Run := GetNextNoInit(Run);
  30117. end;
  30118. end;
  30119. //----------------------------------------------------------------------------------------------------------------------
  30120. function TCustomVirtualStringTree.AddChild(Parent: PVirtualNode; UserData: Pointer): PVirtualNode;
  30121. var
  30122. NewNodeText: UnicodeString;
  30123. begin
  30124. Result := inherited AddChild(Parent, UserData);
  30125. // Restore the prviously restored node if the caption of this node is knwon and no other node was selected
  30126. if (toRestoreSelection in TreeOptions.SelectionOptions) and Assigned(FPreviouslySelected) and Assigned(OnGetText) then
  30127. begin
  30128. // See if this was the previously selected node and restore it in this case
  30129. Self.OnGetText(Self, Result, 0, ttNormal, NewNodeText);
  30130. if FPreviouslySelected.IndexOf(NewNodeText) >= 0 then
  30131. begin
  30132. // Select this node and make sure that the parent node is expanded
  30133. Include(FStates, tsPreviouslySelectedLocked);
  30134. try
  30135. Self.Selected[Result] := True;
  30136. finally
  30137. Exclude(FStates, tsPreviouslySelectedLocked);
  30138. end;
  30139. // if a there is a selected node now, then make sure that it is visible
  30140. if Self.GetFirstSelected <> nil then
  30141. Self.ScrollIntoView(Self.GetFirstSelected, True);
  30142. end;
  30143. end;
  30144. end;
  30145. //----------------------------------------------------------------------------------------------------------------------
  30146. procedure TCustomVirtualStringTree.AdjustPaintCellRect(var PaintInfo: TVTPaintInfo; var NextNonEmpty: TColumnIndex);
  30147. // In the case a node spans several columns (if enabled) we need to determine how many columns.
  30148. // Note: the autospan feature can only be used with left-to-right layout.
  30149. begin
  30150. if (toAutoSpanColumns in FOptions.FAutoOptions) and FHeader.UseColumns and (PaintInfo.BidiMode = bdLeftToRight) then
  30151. with FHeader.FColumns, PaintInfo do
  30152. begin
  30153. // Start with the directly following column.
  30154. NextNonEmpty := GetNextVisibleColumn(Column);
  30155. // Auto spanning columns can only be used for left-to-right directionality because the tree is drawn
  30156. // from left to right. For RTL directionality it would be necessary to draw it from right to left.
  30157. // While this could be managed, it becomes impossible when directionality is mixed.
  30158. repeat
  30159. if (NextNonEmpty = InvalidColumn) or not ColumnIsEmpty(Node, NextNonEmpty) or
  30160. (Items[NextNonEmpty].BidiMode <> bdLeftToRight) then
  30161. Break;
  30162. Inc(CellRect.Right, Items[NextNonEmpty].Width);
  30163. NextNonEmpty := GetNextVisibleColumn(NextNonEmpty);
  30164. until False;
  30165. end
  30166. else
  30167. inherited;
  30168. end;
  30169. //----------------------------------------------------------------------------------------------------------------------
  30170. function TCustomVirtualStringTree.CalculateStaticTextWidth(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
  30171. Text: UnicodeString): Integer;
  30172. begin
  30173. Result := 0;
  30174. if (Length(Text) > 0) and (Alignment <> taCenter) and not
  30175. (vsMultiline in Node.States) and (toShowStaticText in TreeOptions.FStringOptions) then
  30176. begin
  30177. DoPaintText(Node, Canvas, Column, ttStatic);
  30178. Inc(Result, DoTextMeasuring(Canvas, Node, Column, Text).cx);
  30179. Inc(Result, FTextMargin);
  30180. end;
  30181. end;
  30182. //----------------------------------------------------------------------------------------------------------------------
  30183. function TCustomVirtualStringTree.CalculateTextWidth(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
  30184. Text: UnicodeString): Integer;
  30185. // Determines the width of the given text.
  30186. begin
  30187. Result := 2 * FTextMargin;
  30188. if Length(Text) > 0 then
  30189. begin
  30190. Canvas.Font := Font;
  30191. DoPaintText(Node, Canvas, Column, ttNormal);
  30192. Inc(Result, DoTextMeasuring(Canvas, Node, Column, Text).cx);
  30193. end;
  30194. end;
  30195. //----------------------------------------------------------------------------------------------------------------------
  30196. function TCustomVirtualStringTree.ColumnIsEmpty(Node: PVirtualNode; Column: TColumnIndex): Boolean;
  30197. // For hit tests it is necessary to consider cases where columns are empty and automatic column spanning is enabled.
  30198. // This method simply checks the given column's text and if this is empty then the column is considered as being empty.
  30199. begin
  30200. Result := Length(Text[Node, Column]) = 0;
  30201. // If there is no text then let the ancestor decide if the column is to be considered as being empty
  30202. // (e.g. by asking the application). If there is text then the column is never be considered as being empty.
  30203. if Result then
  30204. Result := inherited ColumnIsEmpty(Node, Column);
  30205. end;
  30206. //----------------------------------------------------------------------------------------------------------------------
  30207. procedure TCustomVirtualStringTree.DefineProperties(Filer: TFiler);
  30208. begin
  30209. inherited;
  30210. // Delphi still cannot handle wide strings properly while streaming
  30211. Filer.DefineProperty('WideDefaultText', ReadText, WriteText, FDefaultText <> 'Node');
  30212. Filer.DefineProperty('StringOptions', ReadOldStringOptions, nil, False);
  30213. end;
  30214. //----------------------------------------------------------------------------------------------------------------------
  30215. destructor TCustomVirtualStringTree.Destroy;
  30216. begin
  30217. FreeAndNil(FPreviouslySelected);
  30218. inherited;
  30219. end;
  30220. //----------------------------------------------------------------------------------------------------------------------
  30221. function TCustomVirtualStringTree.DoCreateEditor(Node: PVirtualNode; Column: TColumnIndex): IVTEditLink;
  30222. begin
  30223. Result := inherited DoCreateEditor(Node, Column);
  30224. // Enable generic label editing support if the application does not have own editors.
  30225. if Result = nil then
  30226. Result := TStringEditLink.Create;
  30227. end;
  30228. //----------------------------------------------------------------------------------------------------------------------
  30229. function TCustomVirtualStringTree.DoGetNodeHint(Node: PVirtualNode; Column: TColumnIndex;
  30230. var LineBreakStyle: TVTTooltipLineBreakStyle): UnicodeString;
  30231. begin
  30232. Result := inherited DoGetNodeHint(Node, Column, LineBreakStyle);
  30233. if Assigned(FOnGetHint) then
  30234. FOnGetHint(Self, Node, Column, LineBreakStyle, Result);
  30235. end;
  30236. //----------------------------------------------------------------------------------------------------------------------
  30237. function TCustomVirtualStringTree.DoGetNodeTooltip(Node: PVirtualNode; Column: TColumnIndex;
  30238. var LineBreakStyle: TVTTooltipLineBreakStyle): UnicodeString;
  30239. begin
  30240. Result := inherited DoGetNodeToolTip(Node, Column, LineBreakStyle);
  30241. if Assigned(FOnGetHint) then
  30242. FOnGetHint(Self, Node, Column, LineBreakStyle, Result)
  30243. else
  30244. Result := Text[Node, Column];
  30245. end;
  30246. //----------------------------------------------------------------------------------------------------------------------
  30247. function TCustomVirtualStringTree.DoGetNodeExtraWidth(Node: PVirtualNode; Column: TColumnIndex;
  30248. Canvas: TCanvas = nil): Integer;
  30249. begin
  30250. if Canvas = nil then
  30251. Canvas := Self.Canvas;
  30252. Result := CalculateStaticTextWidth(Canvas, Node, Column, StaticText[Node, Column]);
  30253. end;
  30254. //----------------------------------------------------------------------------------------------------------------------
  30255. function TCustomVirtualStringTree.DoGetNodeWidth(Node: PVirtualNode; Column: TColumnIndex; Canvas: TCanvas = nil): Integer;
  30256. // Returns the text width of the given node in pixels.
  30257. // This width is stored in the node's data member to increase access speed.
  30258. var
  30259. Data: PInteger;
  30260. begin
  30261. if (Column > NoColumn) and (vsMultiline in Node.States) then
  30262. Result := FHeader.Columns[Column].Width
  30263. else
  30264. begin
  30265. if Canvas = nil then
  30266. Canvas := Self.Canvas;
  30267. if Column = FHeader.MainColumn then
  30268. begin
  30269. // Primary column or no columns.
  30270. Data := InternalData(Node);
  30271. if Assigned(Data) then
  30272. begin
  30273. Result := Data^;
  30274. if Result = 0 then
  30275. begin
  30276. Data^ := CalculateTextWidth(Canvas, Node, Column, Text[Node, Column]);
  30277. Result := Data^;
  30278. end;
  30279. end
  30280. else
  30281. Result := 0;
  30282. end
  30283. else
  30284. // any other column
  30285. Result := CalculateTextWidth(Canvas, Node, Column, Text[Node, Column]);
  30286. end;
  30287. end;
  30288. //----------------------------------------------------------------------------------------------------------------------
  30289. procedure TCustomVirtualStringTree.DoGetText(Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
  30290. var Text: UnicodeString);
  30291. begin
  30292. if Assigned(FOnGetText) then
  30293. FOnGetText(Self, Node, Column, TextType, Text);
  30294. end;
  30295. //----------------------------------------------------------------------------------------------------------------------
  30296. function TCustomVirtualStringTree.DoIncrementalSearch(Node: PVirtualNode; const Text: UnicodeString): Integer;
  30297. // Since the string tree has access to node text it can do incremental search on its own. Use the event to
  30298. // override the default behavior.
  30299. begin
  30300. Result := 0;
  30301. if Assigned(FOnIncrementalSearch) then
  30302. FOnIncrementalSearch(Self, Node, Text, Result)
  30303. else
  30304. // Default behavior is to match the search string with the start of the node text.
  30305. if Pos(Text, GetText(Node, FocusedColumn)) <> 1 then
  30306. Result := 1;
  30307. end;
  30308. //----------------------------------------------------------------------------------------------------------------------
  30309. procedure TCustomVirtualStringTree.DoNewText(Node: PVirtualNode; Column: TColumnIndex; Text: UnicodeString);
  30310. begin
  30311. if Assigned(FOnNewText) then
  30312. FOnNewText(Self, Node, Column, Text);
  30313. // The width might have changed, so update the scrollbar.
  30314. if FUpdateCount = 0 then
  30315. UpdateHorizontalScrollBar(True);
  30316. end;
  30317. //----------------------------------------------------------------------------------------------------------------------
  30318. procedure TCustomVirtualStringTree.DoPaintNode(var PaintInfo: TVTPaintInfo);
  30319. // Main output routine to print the text of the given node using the space provided in PaintInfo.ContentRect.
  30320. var
  30321. S: UnicodeString;
  30322. TextOutFlags: Integer;
  30323. begin
  30324. // Set a new OnChange event for the canvas' font so we know if the application changes it in the callbacks.
  30325. // This long winded procedure is necessary because font changes (as well as brush and pen changes) are
  30326. // unfortunately not announced via the Canvas.OnChange event.
  30327. RedirectFontChangeEvent(PaintInfo.Canvas);
  30328. try
  30329. // Determine main text direction as well as other text properties.
  30330. TextOutFlags := ETO_CLIPPED or RTLFlag[PaintInfo.BidiMode <> bdLeftToRight];
  30331. S := Text[PaintInfo.Node, PaintInfo.Column];
  30332. // Paint the normal text first...
  30333. if Length(S) > 0 then
  30334. PaintNormalText(PaintInfo, TextOutFlags, S);
  30335. // ... and afterwards the static text if not centered and the node is not multiline enabled.
  30336. if (Alignment <> taCenter) and not (vsMultiline in PaintInfo.Node.States) and (toShowStaticText in TreeOptions.FStringOptions) then
  30337. begin
  30338. S := '';
  30339. with PaintInfo do
  30340. DoGetText(Node, Column, ttStatic, S);
  30341. if Length(S) > 0 then
  30342. PaintStaticText(PaintInfo, TextOutFlags, S);
  30343. end;
  30344. finally
  30345. RestoreFontChangeEvent(PaintInfo.Canvas);
  30346. end;
  30347. end;
  30348. //----------------------------------------------------------------------------------------------------------------------
  30349. procedure TCustomVirtualStringTree.DoPaintText(Node: PVirtualNode; const Canvas: TCanvas; Column: TColumnIndex;
  30350. TextType: TVSTTextType);
  30351. begin
  30352. if Assigned(FOnPaintText) then
  30353. FOnPaintText(Self, Canvas, Node, Column, TextType);
  30354. end;
  30355. //----------------------------------------------------------------------------------------------------------------------
  30356. function TCustomVirtualStringTree.DoShortenString(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
  30357. const S: UnicodeString; Width: Integer; EllipsisWidth: Integer = 0): UnicodeString;
  30358. var
  30359. Done: Boolean;
  30360. begin
  30361. Done := False;
  30362. if Assigned(FOnShortenString) then
  30363. FOnShortenString(Self, Canvas, Node, Column, S, Width, Result, Done);
  30364. if not Done then
  30365. Result := ShortenString(Canvas.Handle, S, Width, EllipsisWidth);
  30366. end;
  30367. //----------------------------------------------------------------------------------------------------------------------
  30368. procedure TCustomVirtualStringTree.DoTextDrawing(var PaintInfo: TVTPaintInfo; Text: UnicodeString; CellRect: TRect;
  30369. DrawFormat: Cardinal);
  30370. var
  30371. DefaultDraw: Boolean;
  30372. begin
  30373. DefaultDraw := True;
  30374. if Assigned(FOnDrawText) then
  30375. FOnDrawText(Self, PaintInfo.Canvas, PaintInfo.Node, PaintInfo.Column, Text, CellRect, DefaultDraw);
  30376. if DefaultDraw then
  30377. Windows.DrawTextW(PaintInfo.Canvas.Handle, PWideChar(Text), Length(Text), CellRect, DrawFormat);
  30378. end;
  30379. //----------------------------------------------------------------------------------------------------------------------
  30380. function TCustomVirtualStringTree.DoTextMeasuring(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
  30381. Text: UnicodeString): TSize;
  30382. var
  30383. R: TRect;
  30384. DrawFormat: Integer;
  30385. begin
  30386. GetTextExtentPoint32W(Canvas.Handle, PWideChar(Text), Length(Text), Result);
  30387. if vsMultiLine in Node.States then
  30388. begin
  30389. DrawFormat := DT_CALCRECT or DT_NOPREFIX or DT_WORDBREAK or DT_END_ELLIPSIS or DT_EDITCONTROL or AlignmentToDrawFlag[Alignment];
  30390. if BidiMode <> bdLeftToRight then
  30391. DrawFormat := DrawFormat or DT_RTLREADING;
  30392. R := Rect(0, 0, Result.cx, MaxInt);
  30393. Windows.DrawTextW(Canvas.Handle, PWideChar(Text), Length(Text), R, DrawFormat);
  30394. Result.cx := R.Right - R.Left;
  30395. end;
  30396. if Assigned(FOnMeasureTextWidth) then
  30397. FOnMeasureTextWidth(Self, Canvas, Node, Column, Text, Result.cx);
  30398. if Assigned(FOnMeasureTextHeight) then
  30399. FOnMeasureTextHeight(Self, Canvas, Node, Column, Text, Result.cy);
  30400. end;
  30401. //----------------------------------------------------------------------------------------------------------------------
  30402. function TCustomVirtualStringTree.GetOptionsClass: TTreeOptionsClass;
  30403. begin
  30404. Result := TCustomStringTreeOptions;
  30405. end;
  30406. //----------------------------------------------------------------------------------------------------------------------
  30407. function TCustomVirtualStringTree.InternalData(Node: PVirtualNode): Pointer;
  30408. begin
  30409. if (Node = FRoot) or (Node = nil) then
  30410. Result := nil
  30411. else
  30412. Result := PByte(Node) + FInternalDataOffset;
  30413. end;
  30414. //----------------------------------------------------------------------------------------------------------------------
  30415. procedure TCustomVirtualStringTree.MainColumnChanged;
  30416. var
  30417. Run: PVirtualNode;
  30418. Data: PInteger;
  30419. begin
  30420. inherited;
  30421. // Have to reset all node widths.
  30422. Run := FRoot.FirstChild;
  30423. while Assigned(Run) do
  30424. begin
  30425. Data := InternalData(Run);
  30426. if Assigned(Data) then
  30427. Data^ := 0;
  30428. Run := GetNextNoInit(Run);
  30429. end;
  30430. end;
  30431. //----------------------------------------------------------------------------------------------------------------------
  30432. function TCustomVirtualStringTree.ReadChunk(Stream: TStream; Version: Integer; Node: PVirtualNode; ChunkType,
  30433. ChunkSize: Integer): Boolean;
  30434. // read in the caption chunk if there is one
  30435. var
  30436. NewText: UnicodeString;
  30437. begin
  30438. case ChunkType of
  30439. CaptionChunk:
  30440. begin
  30441. NewText := '';
  30442. if ChunkSize > 0 then
  30443. begin
  30444. SetLength(NewText, ChunkSize div 2);
  30445. Stream.Read(PWideChar(NewText)^, ChunkSize);
  30446. end;
  30447. // Do a new text event regardless of the caption content to allow removing the default string.
  30448. Text[Node, FHeader.MainColumn] := NewText;
  30449. Result := True;
  30450. end;
  30451. else
  30452. Result := inherited ReadChunk(Stream, Version, Node, ChunkType, ChunkSize);
  30453. end;
  30454. end;
  30455. //----------------------------------------------------------------------------------------------------------------------
  30456. type
  30457. TOldVTStringOption = (soSaveCaptions, soShowStaticText);
  30458. procedure TCustomVirtualStringTree.ReadOldStringOptions(Reader: TReader);
  30459. // Migration helper routine to silently convert forms containing the old tree options member into the new
  30460. // sub-options structure.
  30461. var
  30462. OldOption: TOldVTStringOption;
  30463. EnumName: string;
  30464. begin
  30465. // If we are at design time currently then let the designer know we changed something.
  30466. UpdateDesigner;
  30467. // It should never happen at this place that there is something different than the old set.
  30468. if Reader.ReadValue = vaSet then
  30469. with TreeOptions do
  30470. begin
  30471. // Remove all default values set by the constructor.
  30472. StringOptions := [];
  30473. while True do
  30474. begin
  30475. // Sets are stored with their members as simple strings. Read them one by one and map them to the new option
  30476. // in the correct sub-option set.
  30477. EnumName := Reader.ReadStr;
  30478. if EnumName = '' then
  30479. Break;
  30480. OldOption := TOldVTStringOption(GetEnumValue(TypeInfo(TOldVTStringOption), EnumName));
  30481. case OldOption of
  30482. soSaveCaptions:
  30483. StringOptions := FStringOptions + [toSaveCaptions];
  30484. soShowStaticText:
  30485. StringOptions := FStringOptions + [toShowStaticText];
  30486. end;
  30487. end;
  30488. end;
  30489. end;
  30490. //----------------------------------------------------------------------------------------------------------------------
  30491. function TCustomVirtualStringTree.RenderOLEData(const FormatEtcIn: TFormatEtc; out Medium: TStgMedium;
  30492. ForClipboard: Boolean): HResult;
  30493. // Returns string expressions of all currently selected nodes in the Medium structure.
  30494. begin
  30495. Result := inherited RenderOLEData(FormatEtcIn, Medium, ForClipboard);
  30496. if Failed(Result) then
  30497. try
  30498. if ForClipboard then
  30499. Medium.hGlobal := ContentToClipboard(FormatEtcIn.cfFormat, tstCutCopySet)
  30500. else
  30501. Medium.hGlobal := ContentToClipboard(FormatEtcIn.cfFormat, tstSelected);
  30502. // Fill rest of the Medium structure if rendering went fine.
  30503. if Medium.hGlobal <> 0 then
  30504. begin
  30505. Medium.tymed := TYMED_HGLOBAL;
  30506. Medium.unkForRelease := nil;
  30507. Result := S_OK;
  30508. end;
  30509. except
  30510. Result := E_FAIL;
  30511. end;
  30512. end;
  30513. //----------------------------------------------------------------------------------------------------------------------
  30514. procedure TCustomVirtualStringTree.WriteChunks(Stream: TStream; Node: PVirtualNode);
  30515. // Adds another sibling chunk for Node storing the label if the node is initialized.
  30516. // Note: If the application stores a node's caption in the node's data member (which will be quite common) and needs to
  30517. // store more node specific data then it should use the OnSaveNode event rather than the caption autosave function
  30518. // (take out soSaveCaption from StringOptions). Otherwise the caption is unnecessarily stored twice.
  30519. var
  30520. Header: TChunkHeader;
  30521. S: UnicodeString;
  30522. Len: Integer;
  30523. begin
  30524. inherited;
  30525. if (toSaveCaptions in TreeOptions.FStringOptions) and (Node <> FRoot) and
  30526. (vsInitialized in Node.States) then
  30527. with Stream do
  30528. begin
  30529. // Read the node's caption (primary column only).
  30530. S := Text[Node, FHeader.MainColumn];
  30531. Len := 2 * Length(S);
  30532. if Len > 0 then
  30533. begin
  30534. // Write a new sub chunk.
  30535. Header.ChunkType := CaptionChunk;
  30536. Header.ChunkSize := Len;
  30537. Write(Header, SizeOf(Header));
  30538. Write(PWideChar(S)^, Len);
  30539. end;
  30540. end;
  30541. end;
  30542. //----------------------------------------------------------------------------------------------------------------------
  30543. function TCustomVirtualStringTree.ComputeNodeHeight(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
  30544. S: UnicodeString): Integer;
  30545. // Default node height calculation for multi line nodes. This method can be used by the application to delegate the
  30546. // computation to the string tree.
  30547. // Canvas is used to compute that value by using its current font settings.
  30548. // Node and Column describe the cell to be used for the computation.
  30549. // S is the string for which the height must be computed. If this string is empty the cell text is used instead.
  30550. var
  30551. DrawFormat: Cardinal;
  30552. BidiMode: TBidiMode;
  30553. Alignment: TAlignment;
  30554. PaintInfo: TVTPaintInfo;
  30555. Dummy: TColumnIndex;
  30556. LineImage: TLineImage;
  30557. begin
  30558. if Length(S) = 0 then
  30559. S := Text[Node, Column];
  30560. DrawFormat := DT_TOP or DT_NOPREFIX or DT_CALCRECT or DT_WORDBREAK;
  30561. if Column <= NoColumn then
  30562. begin
  30563. BidiMode := Self.BidiMode;
  30564. Alignment := Self.Alignment;
  30565. end
  30566. else
  30567. begin
  30568. BidiMode := Header.Columns[Column].BidiMode;
  30569. Alignment := Header.Columns[Column].Alignment;
  30570. end;
  30571. if BidiMode <> bdLeftToRight then
  30572. ChangeBidiModeAlignment(Alignment);
  30573. // Allow for autospanning.
  30574. PaintInfo.Node := Node;
  30575. PaintInfo.BidiMode := BidiMode;
  30576. PaintInfo.Column := Column;
  30577. PaintInfo.CellRect := Rect(0, 0, 0, 0);
  30578. if Column > NoColumn then
  30579. begin
  30580. PaintInfo.CellRect.Right := FHeader.Columns[Column].Width - FTextMargin;
  30581. PaintInfo.CellRect.Left := FTextMargin + FMargin;
  30582. if Column = Header.MainColumn then
  30583. begin
  30584. if toFixedIndent in FOptions.FPaintOptions then
  30585. SetLength(LineImage, 1)
  30586. else
  30587. DetermineLineImageAndSelectLevel(Node, LineImage);
  30588. Inc(PaintInfo.CellRect.Left, Length(LineImage) * Integer(Indent));
  30589. end;
  30590. end
  30591. else
  30592. PaintInfo.CellRect.Right := ClientWidth;
  30593. AdjustPaintCellRect(PaintInfo, Dummy);
  30594. if BidiMode <> bdLeftToRight then
  30595. DrawFormat := DrawFormat or DT_RIGHT or DT_RTLREADING
  30596. else
  30597. DrawFormat := DrawFormat or DT_LEFT;
  30598. Windows.DrawTextW(Canvas.Handle, PWideChar(S), Length(S), PaintInfo.CellRect, DrawFormat);
  30599. Result := PaintInfo.CellRect.Bottom - PaintInfo.CellRect.Top;
  30600. end;
  30601. //----------------------------------------------------------------------------------------------------------------------
  30602. function TCustomVirtualStringTree.ContentToClipboard(Format: Word; Source: TVSTTextSourceType): HGLOBAL;
  30603. // This method constructs a shareable memory object filled with string data in the required format. Supported are:
  30604. // CF_TEXT - plain ANSI text (Unicode text is converted using the user's current locale)
  30605. // CF_UNICODETEXT - plain Unicode text
  30606. // CF_CSV - comma separated plain ANSI text
  30607. // CF_VRTF + CF_RTFNOOBS - rich text (plain ANSI)
  30608. // CF_HTML - HTML text encoded using UTF-8
  30609. //
  30610. // Result is the handle to a globally allocated memory block which can directly be used for clipboard and drag'n drop
  30611. // transfers. The caller is responsible for freeing the memory. If for some reason the content could not be rendered
  30612. // the Result is 0.
  30613. //--------------- local function --------------------------------------------
  30614. procedure MakeFragment(var HTML: AnsiString);
  30615. // Helper routine to build a properly-formatted HTML fragment.
  30616. const
  30617. Version = 'Version:1.0'#13#10;
  30618. StartHTML = 'StartHTML:';
  30619. EndHTML = 'EndHTML:';
  30620. StartFragment = 'StartFragment:';
  30621. EndFragment = 'EndFragment:';
  30622. DocType = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">';
  30623. HTMLIntro = '<html><head><META http-equiv=Content-Type content="text/html; charset=utf-8">' +
  30624. '</head><body><!--StartFragment-->';
  30625. HTMLExtro = '<!--EndFragment--></body></html>';
  30626. NumberLengthAndCR = 10;
  30627. // Let the compiler determine the description length.
  30628. DescriptionLength = Length(Version) + Length(StartHTML) + Length(EndHTML) + Length(StartFragment) +
  30629. Length(EndFragment) + 4 * NumberLengthAndCR;
  30630. var
  30631. Description: AnsiString;
  30632. StartHTMLIndex,
  30633. EndHTMLIndex,
  30634. StartFragmentIndex,
  30635. EndFragmentIndex: Integer;
  30636. begin
  30637. // The HTML clipboard format is defined by using byte positions in the entire block where HTML text and
  30638. // fragments start and end. These positions are written in a description. Unfortunately the positions depend on the
  30639. // length of the description but the description may change with varying positions.
  30640. // To solve this dilemma the offsets are converted into fixed length strings which makes it possible to know
  30641. // the description length in advance.
  30642. StartHTMLIndex := DescriptionLength; // position 0 after the description
  30643. StartFragmentIndex := StartHTMLIndex + Length(DocType) + Length(HTMLIntro);
  30644. EndFragmentIndex := StartFragmentIndex + Length(HTML);
  30645. EndHTMLIndex := EndFragmentIndex + Length(HTMLExtro);
  30646. Description := Version +
  30647. SysUtils.Format('%s%.8d', [StartHTML, StartHTMLIndex]) + #13#10 +
  30648. SysUtils.Format('%s%.8d', [EndHTML, EndHTMLIndex]) + #13#10 +
  30649. SysUtils.Format('%s%.8d', [StartFragment, StartFragmentIndex]) + #13#10 +
  30650. SysUtils.Format('%s%.8d', [EndFragment, EndFragmentIndex]) + #13#10;
  30651. HTML := Description + DocType + HTMLIntro + HTML + HTMLExtro;
  30652. end;
  30653. //--------------- end local function ----------------------------------------
  30654. var
  30655. Data: Pointer;
  30656. DataSize: Cardinal;
  30657. S: AnsiString;
  30658. WS: UnicodeString;
  30659. P: Pointer;
  30660. begin
  30661. Result := 0;
  30662. case Format of
  30663. CF_TEXT:
  30664. begin
  30665. S := ContentToText(Source, #9) + #0;
  30666. Data := PAnsiChar(S);
  30667. DataSize := Length(S);
  30668. end;
  30669. CF_UNICODETEXT:
  30670. begin
  30671. WS := ContentToUnicode(Source, #9) + #0;
  30672. Data := PWideChar(WS);
  30673. DataSize := 2 * Length(WS);
  30674. end;
  30675. else
  30676. if Format = CF_CSV then
  30677. S := ContentToText(Source, AnsiChar ({$if CompilerVersion >= 22}FormatSettings.{$ifend}ListSeparator)) + #0
  30678. else
  30679. if (Format = CF_VRTF) or (Format = CF_VRTFNOOBJS) then
  30680. S := ContentToRTF(Source) + #0
  30681. else
  30682. if Format = CF_HTML then
  30683. begin
  30684. S := ContentToHTML(Source);
  30685. // Build a valid HTML clipboard fragment.
  30686. MakeFragment(S);
  30687. S := S + #0;
  30688. end;
  30689. Data := PAnsiChar(S);
  30690. DataSize := Length(S);
  30691. end;
  30692. if DataSize > 0 then
  30693. begin
  30694. Result := GlobalAlloc(GHND or GMEM_SHARE, DataSize);
  30695. P := GlobalLock(Result);
  30696. Move(Data^, P^, DataSize);
  30697. GlobalUnlock(Result);
  30698. end;
  30699. end;
  30700. //----------------------------------------------------------------------------------------------------------------------
  30701. function TCustomVirtualStringTree.ContentToHTML(Source: TVSTTextSourceType; Caption: UnicodeString = ''): RawByteString;
  30702. // Renders the current tree content (depending on Source) as HTML text encoded in UTF-8.
  30703. // If Caption is not empty then it is used to create and fill the header for the table built here.
  30704. // Based on ideas and code from Frank van den Bergh and Andreas Hörstemeier.
  30705. type
  30706. UCS2 = Word;
  30707. UCS4 = Cardinal;
  30708. const
  30709. MaximumUCS4: UCS4 = $7FFFFFFF;
  30710. ReplacementCharacter: UCS4 = $0000FFFD;
  30711. var
  30712. Buffer: TBufferedAnsiString;
  30713. //--------------- local functions -------------------------------------------
  30714. function ConvertSurrogate(S1, S2: UCS2): UCS4;
  30715. // Converts a pair of high and low surrogate into the corresponding UCS4 character.
  30716. const
  30717. SurrogateOffset = ($D800 shl 10) + $DC00 - $10000;
  30718. begin
  30719. Result := Word(S1) shl 10 + Word(S2) - SurrogateOffset;
  30720. end;
  30721. //---------------------------------------------------------------------------
  30722. function UTF16ToUTF8(const S: UnicodeString): AnsiString;
  30723. // Converts the given Unicode text (which may contain surrogates) into
  30724. // the UTF-8 encoding used for the HTML clipboard format.
  30725. const
  30726. FirstByteMark: array[0..6] of Byte = ($00, $00, $C0, $E0, $F0, $F8, $FC);
  30727. var
  30728. Ch: UCS4;
  30729. I, J, T: Integer;
  30730. BytesToWrite: Cardinal;
  30731. begin
  30732. if Length(S) = 0 then
  30733. Result := ''
  30734. else
  30735. begin
  30736. // Make room for the result. Assume worst case, there are only short texts to convert.
  30737. SetLength(Result, 6 * Length(S));
  30738. T := 1;
  30739. I := 1;
  30740. while I <= Length(S) do
  30741. begin
  30742. Ch := UCS4(S[I]);
  30743. // Is the character a surrogate?
  30744. if (Ch and $FFFFF800) = $D800 then
  30745. begin
  30746. Inc(I);
  30747. // Check the following char whether it forms a valid surrogate pair with the first character.
  30748. if (I <= Length(S)) and ((UCS4(S[I]) and $FFFFFC00) = $DC00) then
  30749. Ch := ConvertSurrogate(UCS2(Ch), UCS2(S[I]))
  30750. else // Skip invalid surrogate value.
  30751. Continue;
  30752. end;
  30753. if Ch < $80 then
  30754. BytesToWrite := 1
  30755. else
  30756. if Ch < $800 then
  30757. BytesToWrite := 2
  30758. else
  30759. if Ch < $10000 then
  30760. BytesToWrite := 3
  30761. else
  30762. if Ch < $200000 then
  30763. BytesToWrite := 4
  30764. else
  30765. if Ch < $4000000 then
  30766. BytesToWrite := 5
  30767. else
  30768. if Ch <= MaximumUCS4 then
  30769. BytesToWrite := 6
  30770. else
  30771. begin
  30772. BytesToWrite := 2;
  30773. Ch := ReplacementCharacter;
  30774. end;
  30775. for J := BytesToWrite downto 2 do
  30776. begin
  30777. Result[T + J - 1] := AnsiChar((Ch or $80) and $BF);
  30778. Ch := Ch shr 6;
  30779. end;
  30780. Result[T] := AnsiChar(Ch or FirstByteMark[BytesToWrite]);
  30781. Inc(T, BytesToWrite);
  30782. Inc(I);
  30783. end;
  30784. SetLength(Result, T - 1); // set to actual length
  30785. end;
  30786. end;
  30787. //---------------------------------------------------------------------------
  30788. procedure WriteColorAsHex(Color: TColor);
  30789. var
  30790. WinColor: COLORREF;
  30791. I: Integer;
  30792. Component,
  30793. Value: Byte;
  30794. begin
  30795. Buffer.Add('#');
  30796. WinColor := ColorToRGB(Color);
  30797. I := 1;
  30798. while I <= 6 do
  30799. begin
  30800. Component := WinColor and $FF;
  30801. Value := 48 + (Component shr 4);
  30802. if Value > $39 then
  30803. Inc(Value, 7);
  30804. Buffer.Add(AnsiChar(Value));
  30805. Inc(I);
  30806. Value := 48 + (Component and $F);
  30807. if Value > $39 then
  30808. Inc(Value, 7);
  30809. Buffer.Add(AnsiChar(Value));
  30810. Inc(I);
  30811. WinColor := WinColor shr 8;
  30812. end;
  30813. end;
  30814. //---------------------------------------------------------------------------
  30815. procedure WriteStyle(Name: AnsiString; Font: TFont);
  30816. // Creates a CSS style entry with the given name for the given font.
  30817. // If Name is empty then the entry is created as inline style.
  30818. begin
  30819. if Length(Name) = 0 then
  30820. Buffer.Add(' style="{')
  30821. else
  30822. begin
  30823. Buffer.Add('.');
  30824. Buffer.Add(Name);
  30825. Buffer.Add('{');
  30826. end;
  30827. Buffer.Add(Format('font-family: ''%s''; ', [Font.Name]));
  30828. if Font.Size < 0 then
  30829. Buffer.Add(Format('font-size: %dpx; ', [Font.Height]))
  30830. else
  30831. Buffer.Add(Format('font-size: %dpt; ', [Font.Size]));
  30832. Buffer.Add(Format('font-style: %s; ', [IfThen(fsItalic in Font.Style, 'italic', 'normal')]));
  30833. Buffer.Add(Format('font-weight: %s; ', [IfThen(fsBold in Font.Style, 'bold', 'normal')]));
  30834. Buffer.Add(Format('text-decoration: %s; ', [IfThen(fsUnderline in Font.Style, 'underline', 'none')]));
  30835. Buffer.Add('color: ');
  30836. WriteColorAsHex(Font.Color);
  30837. Buffer.Add(';}');
  30838. if Length(Name) = 0 then
  30839. Buffer.Add('"');
  30840. end;
  30841. //--------------- end local functions ---------------------------------------
  30842. var
  30843. I, J : Integer;
  30844. Level, MaxLevel: Cardinal;
  30845. AddHeader: AnsiString;
  30846. Save, Run: PVirtualNode;
  30847. GetNextNode: TGetNextNodeProc;
  30848. Text: UnicodeString;
  30849. RenderColumns: Boolean;
  30850. Columns: TColumnsArray;
  30851. ColumnColors: array of AnsiString;
  30852. Index: Integer;
  30853. IndentWidth,
  30854. LineStyleText: AnsiString;
  30855. Alignment: TAlignment;
  30856. BidiMode: TBidiMode;
  30857. CellPadding: AnsiString;
  30858. begin
  30859. Buffer := TBufferedAnsiString.Create;
  30860. try
  30861. // For customization by the application or descendants we use again the redirected font change event.
  30862. RedirectFontChangeEvent(Canvas);
  30863. CellPadding := Format('padding-left: %dpx; padding-right: %0:dpx;', [FMargin]);
  30864. IndentWidth := IntToStr(FIndent);
  30865. AddHeader := ' ';
  30866. // Add title if adviced so by giving a caption.
  30867. if Length(Caption) > 0 then
  30868. AddHeader := AddHeader + 'caption="' + UTF16ToUTF8(Caption) + '"';
  30869. if Borderstyle <> bsNone then
  30870. AddHeader := AddHeader + Format(' border="%d" frame=box', [BorderWidth + 1]);
  30871. Buffer.Add('<META http-equiv="Content-Type" content="text/html; charset=utf-8">');
  30872. // Create HTML table based on the tree structure. To simplify formatting we use styles defined in a small CSS area.
  30873. Buffer.Add('<style type="text/css">');
  30874. Buffer.AddnewLine;
  30875. WriteStyle('default', Font);
  30876. Buffer.AddNewLine;
  30877. WriteStyle('header', FHeader.Font);
  30878. Buffer.AddNewLine;
  30879. // Determine grid/table lines and create CSS for it.
  30880. // Vertical and/or horizontal border to show.
  30881. if FLineStyle = lsSolid then
  30882. LineStyleText := 'solid;'
  30883. else
  30884. LineStyleText := 'dotted;';
  30885. if toShowHorzGridLines in FOptions.FPaintOptions then
  30886. begin
  30887. Buffer.Add('.noborder{');
  30888. Buffer.Add(' border-bottom:1px; border-left: 0px; border-right: 0px; border-top: 1px;');
  30889. Buffer.Add('border-style:');
  30890. Buffer.Add(LineStyleText);
  30891. Buffer.Add(CellPadding);
  30892. Buffer.Add('}');
  30893. end
  30894. else
  30895. begin
  30896. Buffer.Add('.noborder{border-style: none;');
  30897. Buffer.Add(CellPadding);
  30898. Buffer.Add('}');
  30899. end;
  30900. Buffer.AddNewLine;
  30901. Buffer.Add('.normalborder {vertical-align: top; ');
  30902. if toShowVertGridLines in FOptions.FPaintOptions then
  30903. Buffer.Add('border-right: 1px; border-left: 1px; ')
  30904. else
  30905. Buffer.Add('border-right: none; border-left:none; ');
  30906. if toShowHorzGridLines in FOptions.FPaintOptions then
  30907. Buffer.Add('border-top: 1px; border-bottom: 1px; ')
  30908. else
  30909. Buffer.Add('border-top:none; border-bottom: none;');
  30910. Buffer.Add('border-width: thin; border-style: ');
  30911. Buffer.Add(LineStyleText);
  30912. Buffer.Add(CellPadding);
  30913. Buffer.Add('}');
  30914. Buffer.Add('</style>');
  30915. Buffer.AddNewLine;
  30916. // General table properties.
  30917. Buffer.Add('<table class="default" style="border-collapse: collapse;" bgcolor=');
  30918. WriteColorAsHex(Color);
  30919. Buffer.Add(AddHeader);
  30920. Buffer.Add(' cellspacing="0">');
  30921. Buffer.AddNewLine;
  30922. Columns := nil;
  30923. ColumnColors := nil;
  30924. RenderColumns := FHeader.UseColumns;
  30925. if RenderColumns then
  30926. begin
  30927. Columns := FHeader.FColumns.GetVisibleColumns;
  30928. SetLength(ColumnColors, Length(Columns));
  30929. end;
  30930. GetRenderStartValues(Source, Run, GetNextNode);
  30931. Save := Run;
  30932. MaxLevel := 0;
  30933. // The table consists of visible columns and rows as used in the tree, but the main tree column is splitted
  30934. // into several HTML columns to accomodate the indentation.
  30935. while Assigned(Run) do
  30936. begin
  30937. if (CanExportNode(Run)) then
  30938. begin
  30939. Level := GetNodeLevel(Run);
  30940. if Level > MaxLevel then
  30941. MaxLevel := Level;
  30942. end;
  30943. Run := GetNextNode(Run);
  30944. end;
  30945. if RenderColumns then
  30946. begin
  30947. if Assigned(FOnBeforeHeaderExport) then
  30948. FOnBeforeHeaderExport(Self, etHTML);
  30949. Buffer.Add('<tr class="header" style="');
  30950. Buffer.Add(CellPadding);
  30951. Buffer.Add('">');
  30952. Buffer.AddNewLine;
  30953. // Make the first row in the HTML table an image of the tree header.
  30954. for I := 0 to High(Columns) do
  30955. begin
  30956. if Assigned(FOnBeforeColumnExport) then
  30957. FOnBeforeColumnExport(Self, etHTML, Columns[I]);
  30958. Buffer.Add('<th height="');
  30959. Buffer.Add(IntToStr(FHeader.FHeight));
  30960. Buffer.Add('px"');
  30961. Alignment := Columns[I].CaptionAlignment;
  30962. // Consider directionality.
  30963. if Columns[I].FBiDiMode <> bdLeftToRight then
  30964. begin
  30965. ChangeBidiModeAlignment(Alignment);
  30966. Buffer.Add(' dir="rtl"');
  30967. end;
  30968. // Consider aligment.
  30969. case Alignment of
  30970. taRightJustify:
  30971. Buffer.Add(' align=right');
  30972. taCenter:
  30973. Buffer.Add(' align=center');
  30974. else
  30975. Buffer.Add(' align=left');
  30976. end;
  30977. Index := Columns[I].Index;
  30978. // Merge cells of the header emulation in the main column.
  30979. if (MaxLevel > 0) and (Index = Header.MainColumn) then
  30980. begin
  30981. Buffer.Add(' colspan="');
  30982. Buffer.Add(IntToStr(MaxLevel + 1));
  30983. Buffer.Add('"');
  30984. end;
  30985. // The color of the header is usually clBtnFace.
  30986. Buffer.Add(' bgcolor=');
  30987. WriteColorAsHex(clBtnFace);
  30988. // Set column width in pixels.
  30989. Buffer.Add(' width="');
  30990. Buffer.Add(IntToStr(Columns[I].Width));
  30991. Buffer.Add('px">');
  30992. if Length(Columns[I].Text) > 0 then
  30993. Buffer.Add(UTF16ToUTF8(Columns[I].Text));
  30994. Buffer.Add('</th>');
  30995. if Assigned(FOnAfterColumnExport) then
  30996. FOnAfterColumnExport(Self, etHTML, Columns[I]);
  30997. end;
  30998. Buffer.Add('</tr>');
  30999. Buffer.AddNewLine;
  31000. if Assigned(FOnAfterHeaderExport) then
  31001. FOnAfterHeaderExport(Self, etHTML);
  31002. end;
  31003. // Now go through the tree.
  31004. Run := Save;
  31005. while Assigned(Run) do
  31006. begin
  31007. if ((not CanExportNode(Run)) or (Assigned(FOnBeforeNodeExport) and (not FOnBeforeNodeExport(Self, etHTML, Run)))) then
  31008. begin
  31009. Run := GetNextNode(Run);
  31010. Continue;
  31011. end;
  31012. Level := GetNodeLevel(Run);
  31013. Buffer.Add(' <tr class="default">');
  31014. Buffer.AddNewLine;
  31015. I := 0;
  31016. while (I < Length(Columns)) or not RenderColumns do
  31017. begin
  31018. if RenderColumns then
  31019. Index := Columns[I].Index
  31020. else
  31021. Index := NoColumn;
  31022. if not RenderColumns or (coVisible in Columns[I].FOptions) then
  31023. begin
  31024. // Call back the application to know about font customization.
  31025. Canvas.Font := Font;
  31026. FFontChanged := False;
  31027. DoPaintText(Run, Canvas, Index, ttNormal);
  31028. if Index = Header.MainColumn then
  31029. begin
  31030. // Create a cell for each indentation level.
  31031. if RenderColumns and not (coParentColor in Columns[I].FOptions) then
  31032. begin
  31033. for J := 1 to Level do
  31034. begin
  31035. Buffer.Add('<td class="noborder" width="');
  31036. Buffer.Add(IndentWidth);
  31037. Buffer.Add('" height="');
  31038. Buffer.Add(IntToStr(NodeHeight[Run]));
  31039. Buffer.Add('px"');
  31040. if not (coParentColor in Columns[I].FOptions) then
  31041. begin
  31042. Buffer.Add(' bgcolor=');
  31043. WriteColorAsHex(Columns[I].Color);
  31044. end;
  31045. Buffer.Add('>&nbsp;</td>');
  31046. end;
  31047. end
  31048. else
  31049. begin
  31050. for J := 1 to Level do
  31051. if J = 1 then
  31052. begin
  31053. Buffer.Add(' <td height="');
  31054. Buffer.Add(IntToStr(NodeHeight[Run]));
  31055. Buffer.Add('px" class="normalborder">&nbsp;</td>');
  31056. end
  31057. else
  31058. Buffer.Add(' <td>&nbsp;</td>');
  31059. end;
  31060. end;
  31061. if FFontChanged then
  31062. begin
  31063. Buffer.Add(' <td class="normalborder" ');
  31064. WriteStyle('', Canvas.Font);
  31065. Buffer.Add(' height="');
  31066. Buffer.Add(IntToStr(NodeHeight[Run]));
  31067. Buffer.Add('px"');
  31068. end
  31069. else
  31070. begin
  31071. Buffer.Add(' <td class="normalborder" height="');
  31072. Buffer.Add(IntToStr(NodeHeight[Run]));
  31073. Buffer.Add('px"');
  31074. end;
  31075. if RenderColumns then
  31076. begin
  31077. Alignment := Columns[I].Alignment;
  31078. BidiMode := Columns[I].BidiMode;
  31079. end
  31080. else
  31081. begin
  31082. Alignment := Self.Alignment;
  31083. BidiMode := Self.BidiMode;
  31084. end;
  31085. // Consider directionality.
  31086. if BiDiMode <> bdLeftToRight then
  31087. begin
  31088. ChangeBidiModeAlignment(Alignment);
  31089. Buffer.Add(' dir="rtl"');
  31090. end;
  31091. // Consider aligment.
  31092. case Alignment of
  31093. taRightJustify:
  31094. Buffer.Add(' align=right');
  31095. taCenter:
  31096. Buffer.Add(' align=center');
  31097. else
  31098. Buffer.Add(' align=left');
  31099. end;
  31100. // Merge cells in the main column.
  31101. if (MaxLevel > 0) and (Index = FHeader.MainColumn) and (Level < MaxLevel) then
  31102. begin
  31103. Buffer.Add(' colspan="');
  31104. Buffer.Add(IntToStr(MaxLevel - Level + 1));
  31105. Buffer.Add('"');
  31106. end;
  31107. if RenderColumns and not (coParentColor in Columns[I].FOptions) then
  31108. begin
  31109. Buffer.Add(' bgcolor=');
  31110. WriteColorAsHex(Columns[I].Color);
  31111. end;
  31112. Buffer.Add('>');
  31113. Text := Self.Text[Run, Index];
  31114. if Length(Text) > 0 then
  31115. begin
  31116. Text := UTF16ToUTF8(Text);
  31117. Buffer.Add(Text);
  31118. end;
  31119. Buffer.Add('</td>');
  31120. end;
  31121. if not RenderColumns then
  31122. Break;
  31123. Inc(I);
  31124. end;
  31125. if Assigned(FOnAfterNodeExport) then
  31126. FOnAfterNodeExport(Self, etHTML, Run);
  31127. Run := GetNextNode(Run);
  31128. Buffer.Add(' </tr>');
  31129. Buffer.AddNewLine;
  31130. end;
  31131. Buffer.Add('</table>');
  31132. RestoreFontChangeEvent(Canvas);
  31133. Result := Buffer.AsString;
  31134. finally
  31135. Buffer.Free;
  31136. end;
  31137. end;
  31138. //----------------------------------------------------------------------------------------------------------------------
  31139. function TCustomVirtualStringTree.CanExportNode(Node: PVirtualNode): Boolean;
  31140. begin
  31141. case FOptions.ExportMode of
  31142. emChecked:
  31143. Result := Node.CheckState = csCheckedNormal;
  31144. emUnchecked:
  31145. Result := Node.CheckState = csUncheckedNormal;
  31146. emVisibleDueToExpansion: //Do not export nodes that are not visible because their parent is not expanded
  31147. Result := not Assigned(Node.Parent) or Self.Expanded[Node.Parent];
  31148. emSelected: // export selected nodes only
  31149. Result := Selected[Node];
  31150. else
  31151. Result := True;
  31152. end;
  31153. end;
  31154. //----------------------------------------------------------------------------------------------------------------------
  31155. procedure TCustomVirtualStringTree.AddToSelection(Node: PVirtualNode);
  31156. var
  31157. lSelectedNodeCaption: UnicodeString;
  31158. begin
  31159. inherited;
  31160. if (toRestoreSelection in TreeOptions.SelectionOptions) and Assigned(Self.OnGetText) and Self.Selected[Node] and not (tsPreviouslySelectedLocked in FStates) then
  31161. begin
  31162. if not Assigned(FPreviouslySelected) then
  31163. begin
  31164. FPreviouslySelected := TStringList.Create();
  31165. FPreviouslySelected.Duplicates := dupIgnore;
  31166. FPreviouslySelected.Sorted := True; //Improves performance, required to use Find()
  31167. FPreviouslySelected.CaseSensitive := False;
  31168. end;
  31169. if Self.SelectedCount = 1 then
  31170. FPreviouslySelected.Clear();
  31171. Self.OnGetText(Self, Node, 0, ttNormal, lSelectedNodeCaption);
  31172. FPreviouslySelected.Add(lSelectedNodeCaption);
  31173. end;//if
  31174. UpdateNextNodeToSelect(Node);
  31175. end;
  31176. //----------------------------------------------------------------------------------------------------------------------
  31177. procedure TCustomVirtualStringTree.RemoveFromSelection(Node: PVirtualNode);
  31178. var
  31179. lSelectedNodeCaption: UnicodeString;
  31180. lIndex: Integer;
  31181. begin
  31182. inherited;
  31183. if (toRestoreSelection in TreeOptions.SelectionOptions) and Assigned(FPreviouslySelected) and not Self.Selected[Node] then
  31184. begin
  31185. if Self.SelectedCount = 0 then
  31186. FPreviouslySelected.Clear()
  31187. else
  31188. begin
  31189. Self.OnGetText(Self, Node, 0, ttNormal, lSelectedNodeCaption);
  31190. if FPreviouslySelected.Find(lSelectedNodeCaption, lIndex) then
  31191. FPreviouslySelected.Delete(lIndex);
  31192. end;//else
  31193. end;//if
  31194. end;
  31195. //----------------------------------------------------------------------------------------------------------------------
  31196. function TCustomVirtualStringTree.ContentToRTF(Source: TVSTTextSourceType): RawByteString;
  31197. // Renders the current tree content (depending on Source) as RTF (rich text).
  31198. // Based on ideas and code from Frank van den Bergh and Andreas Hörstemeier.
  31199. var
  31200. Fonts: TStringList;
  31201. Colors: TList;
  31202. CurrentFontIndex,
  31203. CurrentFontColor,
  31204. CurrentFontSize: Integer;
  31205. Buffer: TBufferedAnsiString;
  31206. //--------------- local functions -------------------------------------------
  31207. procedure SelectFont(Font: string);
  31208. var
  31209. I: Integer;
  31210. begin
  31211. I := Fonts.IndexOf(Font);
  31212. if I > -1 then
  31213. begin
  31214. // Font has already been used
  31215. if I <> CurrentFontIndex then
  31216. begin
  31217. Buffer.Add('\f');
  31218. Buffer.Add(IntToStr(I));
  31219. CurrentFontIndex := I;
  31220. end;
  31221. end
  31222. else
  31223. begin
  31224. I := Fonts.Add(Font);
  31225. Buffer.Add('\f');
  31226. Buffer.Add(IntToStr(I));
  31227. CurrentFontIndex := I;
  31228. end;
  31229. end;
  31230. //---------------------------------------------------------------------------
  31231. procedure SelectColor(Color: TColor);
  31232. var
  31233. I: Integer;
  31234. begin
  31235. I := Colors.IndexOf(Pointer(Color));
  31236. if I > -1 then
  31237. begin
  31238. // Color has already been used
  31239. if I <> CurrentFontColor then
  31240. begin
  31241. Buffer.Add('\cf');
  31242. Buffer.Add(IntToStr(I + 1));
  31243. CurrentFontColor := I;
  31244. end;
  31245. end
  31246. else
  31247. begin
  31248. I := Colors.Add(Pointer(Color));
  31249. Buffer.Add('\cf');
  31250. Buffer.Add(IntToStr(I + 1));
  31251. CurrentFontColor := I;
  31252. end;
  31253. end;
  31254. //---------------------------------------------------------------------------
  31255. procedure TextPlusFont(Text: UnicodeString; Font: TFont);
  31256. var
  31257. UseUnderline,
  31258. UseItalic,
  31259. UseBold: Boolean;
  31260. I: Integer;
  31261. begin
  31262. if Length(Text) > 0 then
  31263. begin
  31264. UseUnderline := fsUnderline in Font.Style;
  31265. if UseUnderline then
  31266. Buffer.Add('\ul');
  31267. UseItalic := fsItalic in Font.Style;
  31268. if UseItalic then
  31269. Buffer.Add('\i');
  31270. UseBold := fsBold in Font.Style;
  31271. if UseBold then
  31272. Buffer.Add('\b');
  31273. SelectFont(Font.Name);
  31274. SelectColor(Font.Color);
  31275. if Font.Size <> CurrentFontSize then
  31276. begin
  31277. // Font size must be given in half points.
  31278. Buffer.Add('\fs');
  31279. Buffer.Add(IntToStr(2 * Font.Size));
  31280. CurrentFontSize := Font.Size;
  31281. end;
  31282. // Use escape sequences to note Unicode text.
  31283. Buffer.Add(' ');
  31284. // Note: Unicode values > 32767 must be expressed as negative numbers. This is implicitly done
  31285. // by interpreting the wide chars (word values) as small integers.
  31286. for I := 1 to Length(Text) do
  31287. begin
  31288. if (Text[I] = WideLF) then
  31289. Buffer.Add( '{\par}' )
  31290. else
  31291. if (Text[I] <> WideCR) then
  31292. begin
  31293. Buffer.Add(Format('\u%d\''3f', [SmallInt(Text[I])]));
  31294. Continue;
  31295. end;
  31296. end;
  31297. if UseUnderline then
  31298. Buffer.Add('\ul0');
  31299. if UseItalic then
  31300. Buffer.Add('\i0');
  31301. if UseBold then
  31302. Buffer.Add('\b0');
  31303. end;
  31304. end;
  31305. //--------------- end local functions ---------------------------------------
  31306. var
  31307. Level, LastLevel: Integer;
  31308. I, J: Integer;
  31309. Save, Run: PVirtualNode;
  31310. GetNextNode: TGetNextNodeProc;
  31311. S, Tabs : RawByteString;
  31312. Text: UnicodeString;
  31313. Twips: Integer;
  31314. RenderColumns: Boolean;
  31315. Columns: TColumnsArray;
  31316. Index: Integer;
  31317. Alignment: TAlignment;
  31318. BidiMode: TBidiMode;
  31319. LocaleBuffer: array [0..1] of Char;
  31320. begin
  31321. Buffer := TBufferedAnsiString.Create;
  31322. try
  31323. // For customization by the application or descendants we use again the redirected font change event.
  31324. RedirectFontChangeEvent(Canvas);
  31325. Fonts := TStringList.Create;
  31326. Colors := TList.Create;
  31327. CurrentFontIndex := -1;
  31328. CurrentFontColor := -1;
  31329. CurrentFontSize := -1;
  31330. Columns := nil;
  31331. Tabs := '';
  31332. LastLevel := 0;
  31333. RenderColumns := FHeader.UseColumns;
  31334. if RenderColumns then
  31335. Columns := FHeader.FColumns.GetVisibleColumns;
  31336. GetRenderStartValues(Source, Run, GetNextNode);
  31337. Save := Run;
  31338. // First make a table structure. The \rtf and other header stuff is included
  31339. // when the font and color tables are created.
  31340. Buffer.Add('\uc1\trowd\trgaph70');
  31341. J := 0;
  31342. if RenderColumns then
  31343. begin
  31344. for I := 0 to High(Columns) do
  31345. begin
  31346. Inc(J, Columns[I].Width);
  31347. // This value must be expressed in twips (1 inch = 1440 twips).
  31348. Twips := Round(1440 * J / Screen.PixelsPerInch);
  31349. Buffer.Add('\cellx');
  31350. Buffer.Add(IntToStr(Twips));
  31351. end;
  31352. end
  31353. else
  31354. begin
  31355. Twips := Round(1440 * ClientWidth / Screen.PixelsPerInch);
  31356. Buffer.Add('\cellx');
  31357. Buffer.Add(IntToStr(Twips));
  31358. end;
  31359. // Fill table header.
  31360. if RenderColumns then
  31361. begin
  31362. if Assigned(FOnBeforeHeaderExport) then
  31363. FOnBeforeHeaderExport(Self, etRTF);
  31364. Buffer.Add('\pard\intbl');
  31365. for I := 0 to High(Columns) do
  31366. begin
  31367. if Assigned(FOnBeforeColumnExport) then
  31368. FOnBeforeColumnExport(Self, etRTF, Columns[I]);
  31369. Alignment := Columns[I].CaptionAlignment;
  31370. BidiMode := Columns[I].BidiMode;
  31371. // Alignment is not supported with older RTF formats, however it will be ignored.
  31372. if BidiMode <> bdLeftToRight then
  31373. ChangeBidiModeAlignment(Alignment);
  31374. case Alignment of
  31375. taLeftJustify:
  31376. Buffer.Add('\ql');
  31377. taRightJustify:
  31378. Buffer.Add('\qr');
  31379. taCenter:
  31380. Buffer.Add('\qc');
  31381. end;
  31382. TextPlusFont(Columns[I].Text, Header.Font);
  31383. Buffer.Add('\cell');
  31384. if Assigned(FOnAfterColumnExport) then
  31385. FOnAfterColumnExport(Self, etRTF, Columns[I]);
  31386. end;
  31387. Buffer.Add('\row');
  31388. if Assigned(FOnAfterHeaderExport) then
  31389. FOnAfterHeaderExport(Self, etRTF);
  31390. end;
  31391. // Now write the contents.
  31392. Run := Save;
  31393. while Assigned(Run) do
  31394. begin
  31395. if ((not CanExportNode(Run)) or
  31396. (Assigned(FOnBeforeNodeExport) and (not FOnBeforeNodeExport(Self, etRTF, Run)))) then
  31397. begin
  31398. Run := GetNextNode(Run);
  31399. Continue;
  31400. end;
  31401. I := 0;
  31402. while not RenderColumns or (I < Length(Columns)) do
  31403. begin
  31404. if RenderColumns then
  31405. begin
  31406. Index := Columns[I].Index;
  31407. Alignment := Columns[I].Alignment;
  31408. BidiMode := Columns[I].BidiMode;
  31409. end
  31410. else
  31411. begin
  31412. Index := NoColumn;
  31413. Alignment := FAlignment;
  31414. BidiMode := Self.BidiMode;
  31415. end;
  31416. if not RenderColumns or (coVisible in Columns[I].Options) then
  31417. begin
  31418. Text := Self.Text[Run, Index];
  31419. Buffer.Add('\pard\intbl');
  31420. // Alignment is not supported with older RTF formats, however it will be ignored.
  31421. if BidiMode <> bdLeftToRight then
  31422. ChangeBidiModeAlignment(Alignment);
  31423. case Alignment of
  31424. taRightJustify:
  31425. Buffer.Add('\qr');
  31426. taCenter:
  31427. Buffer.Add('\qc');
  31428. end;
  31429. // Call back the application to know about font customization.
  31430. Canvas.Font := Font;
  31431. FFontChanged := False;
  31432. DoPaintText(Run, Canvas, Index, ttNormal);
  31433. if Index = Header.MainColumn then
  31434. begin
  31435. Level := GetNodeLevel(Run);
  31436. if Level <> LastLevel then
  31437. begin
  31438. LastLevel := Level;
  31439. Tabs := '';
  31440. for J := 0 to Level - 1 do
  31441. Tabs := Tabs + '\tab';
  31442. end;
  31443. if Level > 0 then
  31444. begin
  31445. Buffer.Add(Tabs);
  31446. Buffer.Add(' ');
  31447. TextPlusFont(Text, Canvas.Font);
  31448. Buffer.Add('\cell');
  31449. end
  31450. else
  31451. begin
  31452. TextPlusFont(Text, Canvas.Font);
  31453. Buffer.Add('\cell');
  31454. end;
  31455. end
  31456. else
  31457. begin
  31458. TextPlusFont(Text, Canvas.Font);
  31459. Buffer.Add('\cell');
  31460. end;
  31461. end;
  31462. if not RenderColumns then
  31463. Break;
  31464. Inc(I);
  31465. end;
  31466. Buffer.Add('\row');
  31467. Buffer.AddNewLine;
  31468. if (Assigned(FOnAfterNodeExport)) then
  31469. FOnAfterNodeExport(Self, etRTF, Run);
  31470. Run := GetNextNode(Run);
  31471. end;
  31472. Buffer.Add('\pard\par');
  31473. // Build lists with fonts and colors. They have to be at the start of the document.
  31474. S := '{\rtf1\ansi\ansicpg1252\deff0\deflang1043{\fonttbl';
  31475. for I := 0 to Fonts.Count - 1 do
  31476. S := S + Format('{\f%d %s;}', [I, Fonts[I]]);
  31477. S := S + '}';
  31478. S := S + '{\colortbl;';
  31479. for I := 0 to Colors.Count - 1 do
  31480. begin
  31481. J := ColorToRGB(TColor(Colors[I]));
  31482. S := S + Format('\red%d\green%d\blue%d;', [J and $FF, (J shr 8) and $FF, (J shr 16) and $FF]);
  31483. end;
  31484. S := S + '}';
  31485. if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IMEASURE, @LocaleBuffer[0], Length(LocaleBuffer)) <> 0) and (LocaleBuffer[0] = '0'{metric}) then
  31486. S := S + '\paperw16840\paperh11907'// This sets A4 landscape format
  31487. else
  31488. S := S + '\paperw15840\paperh12240';//[JAM:marder] This sets US Letter landscape format
  31489. // Make sure a small margin is used so that a lot of the table fits on a paper. This defines a margin of 0.5"
  31490. S := S + '\margl720\margr720\margt720\margb720';
  31491. Result := S + Buffer.AsString + '}';
  31492. Fonts.Free;
  31493. Colors.Free;
  31494. RestoreFontChangeEvent(Canvas);
  31495. finally
  31496. Buffer.Free;
  31497. end;
  31498. end;
  31499. //----------------------------------------------------------------------------------------------------------------------
  31500. procedure TCustomVirtualStringTree.ContentToCustom(Source: TVSTTextSourceType);
  31501. // Generic export procedure which polls the application at every stage of the export.
  31502. var
  31503. I: Integer;
  31504. Save, Run: PVirtualNode;
  31505. GetNextNode: TGetNextNodeProc;
  31506. RenderColumns: Boolean;
  31507. Columns: TColumnsArray;
  31508. begin
  31509. Columns := nil;
  31510. GetRenderStartValues(Source, Run, GetNextNode);
  31511. Save := Run;
  31512. RenderColumns := FHeader.UseColumns and ( hoVisible in FHeader.Options );
  31513. if Assigned(FOnBeforeTreeExport) then
  31514. FOnBeforeTreeExport(Self, etCustom);
  31515. // Fill table header.
  31516. if RenderColumns then
  31517. begin
  31518. if Assigned(FOnBeforeHeaderExport) then
  31519. FOnBeforeHeaderExport(Self, etCustom);
  31520. Columns := FHeader.FColumns.GetVisibleColumns;
  31521. for I := 0 to High(Columns) do
  31522. begin
  31523. if Assigned(FOnBeforeColumnExport) then
  31524. FOnBeforeColumnExport(Self, etCustom, Columns[I]);
  31525. if Assigned(FOnColumnExport) then
  31526. FOnColumnExport(Self, etCustom, Columns[I]);
  31527. if Assigned(FOnAfterColumnExport) then
  31528. FOnAfterColumnExport(Self, etCustom, Columns[I]);
  31529. end;
  31530. if Assigned(FOnAfterHeaderExport) then
  31531. FOnAfterHeaderExport(Self, etCustom);
  31532. end;
  31533. // Now write the content.
  31534. Run := Save;
  31535. while Assigned(Run) do
  31536. begin
  31537. if CanExportNode(Run) then
  31538. begin
  31539. if Assigned(FOnBeforeNodeExport) then
  31540. FOnBeforeNodeExport(Self, etCustom, Run);
  31541. if Assigned(FOnNodeExport) then
  31542. FOnNodeExport(Self, etCustom, Run);
  31543. if Assigned(FOnAfterNodeExport) then
  31544. FOnAfterNodeExport(Self, etCustom, Run);
  31545. end;
  31546. Run := GetNextNode(Run);
  31547. end;
  31548. if Assigned(FOnAfterTreeExport) then
  31549. FOnAfterTreeExport(Self, etCustom);
  31550. end;
  31551. //----------------------------------------------------------------------------------------------------------------------
  31552. function TCustomVirtualStringTree.ContentToText(Source: TVSTTextSourceType; Separator: Char): AnsiString;
  31553. begin
  31554. Result := ContentToText(Source, AnsiString(Separator));
  31555. end;
  31556. //----------------------------------------------------------------------------------------------------------------------
  31557. function TCustomVirtualStringTree.ContentToText(Source: TVSTTextSourceType; const Separator: AnsiString): AnsiString;
  31558. // Renders the current tree content (depending on Source) as plain ANSI text.
  31559. // If an entry contains the separator char or double quotes then it is wrapped with double quotes
  31560. // and existing double quotes are duplicated.
  31561. // Note: Unicode strings are implicitely converted to ANSI strings based on the currently active user locale.
  31562. var
  31563. RenderColumns: Boolean;
  31564. Tabs: AnsiString;
  31565. GetNextNode: TGetNextNodeProc;
  31566. Run, Save: PVirtualNode;
  31567. Level, MaxLevel: Cardinal;
  31568. Columns: TColumnsArray;
  31569. LastColumn: TVirtualTreeColumn;
  31570. Index,
  31571. I: Integer;
  31572. Text: AnsiString;
  31573. Buffer: TBufferedAnsiString;
  31574. begin
  31575. Columns := nil;
  31576. Buffer := TBufferedAnsiString.Create;
  31577. try
  31578. RenderColumns := FHeader.UseColumns;
  31579. if RenderColumns then
  31580. Columns := FHeader.FColumns.GetVisibleColumns;
  31581. GetRenderStartValues(Source, Run, GetNextNode);
  31582. Save := Run;
  31583. // The text consists of visible groups representing the columns, which are separated by one or more separator
  31584. // characters. There are always MaxLevel separator chars in a line (main column only). Either before the caption
  31585. // to ident it or after the caption to make the following column aligned.
  31586. MaxLevel := 0;
  31587. while Assigned(Run) do
  31588. begin
  31589. Level := GetNodeLevel(Run);
  31590. if Level > MaxLevel then
  31591. MaxLevel := Level;
  31592. Run := GetNextNode(Run);
  31593. end;
  31594. Tabs := DupeString(Separator, MaxLevel);
  31595. // First line is always the header if used.
  31596. if RenderColumns then
  31597. begin
  31598. LastColumn := Columns[High(Columns)];
  31599. for I := 0 to High(Columns) do
  31600. begin
  31601. Buffer.Add(Columns[I].Text);
  31602. if Columns[I] <> LastColumn then
  31603. begin
  31604. if Columns[I].Index = Header.MainColumn then
  31605. begin
  31606. Buffer.Add(Tabs);
  31607. Buffer.Add(Separator);
  31608. end
  31609. else
  31610. Buffer.Add(Separator);
  31611. end;
  31612. end;
  31613. Buffer.AddNewLine;
  31614. end
  31615. else
  31616. LastColumn := nil;
  31617. Run := Save;
  31618. if RenderColumns then
  31619. begin
  31620. while Assigned(Run) do
  31621. begin
  31622. if (not CanExportNode(Run) or
  31623. (Assigned(FOnBeforeNodeExport) and (not FOnBeforeNodeExport(Self, etText, Run)))) then
  31624. begin
  31625. Run := GetNextNode(Run);
  31626. Continue;
  31627. end;
  31628. for I := 0 to High(Columns) do
  31629. begin
  31630. if coVisible in Columns[I].Options then
  31631. begin
  31632. Index := Columns[I].Index;
  31633. // This line implicitly converts the Unicode text to ANSI.
  31634. Text := Self.Text[Run, Index];
  31635. if Index = Header.MainColumn then
  31636. begin
  31637. Level := GetNodeLevel(Run);
  31638. Buffer.Add(Copy(Tabs, 1, Integer(Level) * Length(Separator)));
  31639. // Wrap the text with quotation marks if it contains the separator character.
  31640. if (Pos(Separator, Text) > 0) or (Pos('"', Text) > 0) then
  31641. Buffer.Add(AnsiQuotedStr(Text, '"'))
  31642. else
  31643. Buffer.Add(Text);
  31644. Buffer.Add(Copy(Tabs, 1, Integer(MaxLevel - Level) * Length(Separator)));
  31645. end
  31646. else
  31647. if (Pos(Separator, Text) > 0) or (Pos('"', Text) > 0) then
  31648. Buffer.Add(AnsiQuotedStr(Text, '"'))
  31649. else
  31650. Buffer.Add(Text);
  31651. if Columns[I] <> LastColumn then
  31652. Buffer.Add(Separator);
  31653. end;
  31654. end;
  31655. if Assigned(FOnAfterNodeExport) then
  31656. FOnAfterNodeExport(Self, etText, Run);
  31657. Run := GetNextNode(Run);
  31658. Buffer.AddNewLine;
  31659. end;
  31660. end
  31661. else
  31662. begin
  31663. while Assigned(Run) do
  31664. begin
  31665. if ((not CanExportNode(Run)) or
  31666. (Assigned(FOnBeforeNodeExport) and (not FOnBeforeNodeExport(Self, etText, Run)))) then
  31667. begin
  31668. Run := GetNextNode(Run);
  31669. Continue;
  31670. end;
  31671. // This line implicitly converts the Unicode text to ANSI.
  31672. Text := Self.Text[Run, NoColumn];
  31673. Level := GetNodeLevel(Run);
  31674. Buffer.Add(Copy(Tabs, 1, Integer(Level) * Length(Separator)));
  31675. Buffer.Add(Text);
  31676. Buffer.AddNewLine;
  31677. if Assigned(FOnAfterNodeExport) then
  31678. FOnAfterNodeExport(Self, etText, Run);
  31679. Run := GetNextNode(Run);
  31680. end;
  31681. end;
  31682. Result := Buffer.AsString;
  31683. finally
  31684. Buffer.Free;
  31685. end;
  31686. end;
  31687. //----------------------------------------------------------------------------------------------------------------------
  31688. function TCustomVirtualStringTree.ContentToUnicode(Source: TVSTTextSourceType; Separator: WideChar): UnicodeString;
  31689. begin
  31690. Result := ContentToUnicode(Source, UnicodeString(Separator));
  31691. end;
  31692. //----------------------------------------------------------------------------------------------------------------------
  31693. function TCustomVirtualStringTree.ContentToUnicode(Source: TVSTTextSourceType; const Separator: UnicodeString): UnicodeString;
  31694. // Renders the current tree content (depending on Source) as Unicode text.
  31695. // If an entry contains the separator char then it is wrapped with double quotation marks.
  31696. // Note: There is no QuotedStr function for Unicode in the VCL (like AnsiQuotedStr) so we have the limitation here
  31697. // that an entry must not contain double quotation marks, otherwise import into other programs might fail!
  31698. const
  31699. WideCRLF: UnicodeString = #13#10;
  31700. var
  31701. RenderColumns: Boolean;
  31702. Tabs: UnicodeString;
  31703. GetNextNode: TGetNextNodeProc;
  31704. Run, Save: PVirtualNode;
  31705. Columns: TColumnsArray;
  31706. LastColumn: TVirtualTreeColumn;
  31707. Level, MaxLevel: Cardinal;
  31708. Index,
  31709. I: Integer;
  31710. Text: UnicodeString;
  31711. Buffer: TWideBufferedString;
  31712. begin
  31713. Columns := nil;
  31714. Buffer := TWideBufferedString.Create;
  31715. try
  31716. RenderColumns := FHeader.UseColumns;
  31717. if RenderColumns then
  31718. Columns := FHeader.FColumns.GetVisibleColumns;
  31719. GetRenderStartValues(Source, Run, GetNextNode);
  31720. Save := Run;
  31721. // The text consists of visible groups representing the columns, which are separated by one or more separator
  31722. // characters. There are always MaxLevel separator chars in a line (main column only). Either before the caption
  31723. // to ident it or after the caption to make the following column aligned.
  31724. MaxLevel := 0;
  31725. while Assigned(Run) do
  31726. begin
  31727. Level := GetNodeLevel(Run);
  31728. if Level > MaxLevel then
  31729. MaxLevel := Level;
  31730. Run := GetNextNode(Run);
  31731. end;
  31732. Tabs := DupeString(Separator, MaxLevel);
  31733. // First line is always the header if used.
  31734. if RenderColumns then
  31735. begin
  31736. LastColumn := Columns[High(Columns)];
  31737. for I := 0 to High(Columns) do
  31738. begin
  31739. Buffer.Add(Columns[I].Text);
  31740. if Columns[I] <> LastColumn then
  31741. begin
  31742. if Columns[I].Index = Header.MainColumn then
  31743. begin
  31744. Buffer.Add(Tabs);
  31745. Buffer.Add(Separator);
  31746. end
  31747. else
  31748. Buffer.Add(Separator);
  31749. end;
  31750. end;
  31751. Buffer.AddNewLine;
  31752. end
  31753. else
  31754. LastColumn := nil;
  31755. Run := Save;
  31756. if RenderColumns then
  31757. begin
  31758. while Assigned(Run) do
  31759. begin
  31760. for I := 0 to High(Columns) do
  31761. begin
  31762. if coVisible in Columns[I].Options then
  31763. begin
  31764. Index := Columns[I].Index;
  31765. Text := Self.Text[Run, Index];
  31766. if Index = Header.MainColumn then
  31767. begin
  31768. Level := GetNodeLevel(Run);
  31769. Buffer.Add(Copy(Tabs, 1, Integer(Level) * Length(Separator)));
  31770. // Wrap the text with quotation marks if it contains the separator character.
  31771. if Pos(Separator, Text) > 0 then
  31772. begin
  31773. Buffer.Add('"');
  31774. Buffer.Add(Text);
  31775. Buffer.Add('"');
  31776. end
  31777. else
  31778. Buffer.Add(Text);
  31779. Buffer.Add(Copy(Tabs, 1, Integer(MaxLevel - Level) * Length(Separator)));
  31780. end
  31781. else
  31782. if Pos(Separator, Text) > 0 then
  31783. begin
  31784. Buffer.Add('"');
  31785. Buffer.Add(Text);
  31786. Buffer.Add('"');
  31787. end
  31788. else
  31789. Buffer.Add(Text);
  31790. if Columns[I] <> LastColumn then
  31791. Buffer.Add(Separator);
  31792. end;
  31793. end;
  31794. Run := GetNextNode(Run);
  31795. Buffer.AddNewLine;
  31796. end;
  31797. end
  31798. else
  31799. begin
  31800. while Assigned(Run) do
  31801. begin
  31802. Text := Self.Text[Run, NoColumn];
  31803. Level := GetNodeLevel(Run);
  31804. Buffer.Add(Copy(Tabs, 1, Integer(Level) * Length(Separator)));
  31805. Buffer.Add(Text);
  31806. Buffer.AddNewLine;
  31807. Run := GetNextNode(Run);
  31808. end;
  31809. end;
  31810. Result := Buffer.AsString;
  31811. finally
  31812. Buffer.Free;
  31813. end;
  31814. end;
  31815. //----------------------------------------------------------------------------------------------------------------------
  31816. procedure TCustomVirtualStringTree.GetTextInfo(Node: PVirtualNode; Column: TColumnIndex; const AFont: TFont; var R: TRect;
  31817. var Text: UnicodeString);
  31818. // Returns the font, the text and its bounding rectangle to the caller. R is returned as the closest
  31819. // bounding rectangle around Text.
  31820. var
  31821. NewHeight: Integer;
  31822. TM: TTextMetric;
  31823. begin
  31824. // Get default font and initialize the other parameters.
  31825. inherited GetTextInfo(Node, Column, AFont, R, Text);
  31826. Canvas.Font := AFont;
  31827. FFontChanged := False;
  31828. RedirectFontChangeEvent(Canvas);
  31829. DoPaintText(Node, Canvas, Column, ttNormal);
  31830. if FFontChanged then
  31831. begin
  31832. AFont.Assign(Canvas.Font);
  31833. GetTextMetrics(Canvas.Handle, TM);
  31834. NewHeight := TM.tmHeight;
  31835. end
  31836. else // Otherwise the correct font is already there and we only need to set the correct height.
  31837. NewHeight := FTextHeight;
  31838. RestoreFontChangeEvent(Canvas);
  31839. // Alignment to the actual text.
  31840. Text := Self.Text[Node, Column];
  31841. R := GetDisplayRect(Node, Column, True, not (vsMultiline in Node.States));
  31842. if toShowHorzGridLines in TreeOptions.PaintOptions then
  31843. Dec(R.Bottom);
  31844. InflateRect(R, 0, -(R.Bottom - R.Top - NewHeight) div 2);
  31845. end;
  31846. //----------------------------------------------------------------------------------------------------------------------
  31847. function TCustomVirtualStringTree.InvalidateNode(Node: PVirtualNode): TRect;
  31848. var
  31849. Data: PInteger;
  31850. begin
  31851. Result := inherited InvalidateNode(Node);
  31852. // Reset node width so changed text attributes are applied correctly.
  31853. if Assigned(Node) then
  31854. begin
  31855. Data := InternalData(Node);
  31856. if Assigned(Data) then
  31857. Data^ := 0;
  31858. // Reset height measured flag too to cause a re-issue of the OnMeasureItem event.
  31859. Exclude(Node.States, vsHeightMeasured);
  31860. end;
  31861. end;
  31862. //----------------------------------------------------------------------------------------------------------------------
  31863. function TCustomVirtualStringTree.Path(Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
  31864. Delimiter: WideChar): UnicodeString;
  31865. // Constructs a string containing the node and all its parents. The last character in the returned path is always the
  31866. // given delimiter.
  31867. var
  31868. S: UnicodeString;
  31869. begin
  31870. if (Node = nil) or (Node = FRoot) then
  31871. Result := Delimiter
  31872. else
  31873. begin
  31874. Result := '';
  31875. while Node <> FRoot do
  31876. begin
  31877. DoGetText(Node, Column, TextType, S);
  31878. Result := S + Delimiter + Result;
  31879. Node := Node.Parent;
  31880. end;
  31881. end;
  31882. end;
  31883. //----------------------------------------------------------------------------------------------------------------------
  31884. procedure TCustomVirtualStringTree.ReinitNode(Node: PVirtualNode; Recursive: Boolean);
  31885. var
  31886. Data: PInteger;
  31887. begin
  31888. inherited;
  31889. // Reset node width so changed text attributes are applied correctly.
  31890. if Assigned(Node) and (Node <> FRoot) then
  31891. begin
  31892. Data := InternalData(Node);
  31893. if Assigned(Data) then
  31894. Data^ := 0;
  31895. // vsHeightMeasured is already removed in the base tree.
  31896. end;
  31897. end;
  31898. //----------------- TVirtualStringTree ---------------------------------------------------------------------------------
  31899. function TVirtualStringTree.GetOptions: TStringTreeOptions;
  31900. begin
  31901. Result := FOptions as TStringTreeOptions;
  31902. end;
  31903. //----------------------------------------------------------------------------------------------------------------------
  31904. procedure TVirtualStringTree.SetOptions(const Value: TStringTreeOptions);
  31905. begin
  31906. FOptions.Assign(Value);
  31907. end;
  31908. //----------------------------------------------------------------------------------------------------------------------
  31909. function TVirtualStringTree.GetOptionsClass: TTreeOptionsClass;
  31910. begin
  31911. Result := TStringTreeOptions;
  31912. end;
  31913. //----------------------------------------------------------------------------------------------------------------------
  31914. {$if CompilerVersion >= 23}
  31915. class constructor TVirtualStringTree.Create();
  31916. begin
  31917. TCustomStyleEngine.RegisterStyleHook(TVirtualStringTree, TVclStyleScrollBarsHook);
  31918. end;
  31919. {$ifend}
  31920. //----------------------------------------------------------------------------------------------------------------------
  31921. function TCustomVirtualDrawTree.DoGetCellContentMargin(Node: PVirtualNode; Column: TColumnIndex;
  31922. CellContentMarginType: TVTCellContentMarginType = ccmtAllSides; Canvas: TCanvas = nil): TPoint;
  31923. begin
  31924. Result := Point(0, 0);
  31925. if Canvas = nil then
  31926. Canvas := Self.Canvas;
  31927. if Assigned(FOnGetCellContentMargin) then
  31928. FOnGetCellContentMargin(Self, Canvas, Node, Column, CellContentMarginType, Result);
  31929. end;
  31930. //----------------------------------------------------------------------------------------------------------------------
  31931. function TCustomVirtualDrawTree.DoGetNodeWidth(Node: PVirtualNode; Column: TColumnIndex; Canvas: TCanvas = nil): Integer;
  31932. begin
  31933. Result := 2 * FTextMargin;
  31934. if Canvas = nil then
  31935. Canvas := Self.Canvas;
  31936. if Assigned(FOnGetNodeWidth) then
  31937. FOnGetNodeWidth(Self, Canvas, Node, Column, Result);
  31938. end;
  31939. //----------------------------------------------------------------------------------------------------------------------
  31940. procedure TCustomVirtualDrawTree.DoPaintNode(var PaintInfo: TVTPaintInfo);
  31941. begin
  31942. if Assigned(FOnDrawNode) then
  31943. FOnDrawNode(Self, PaintInfo);
  31944. end;
  31945. function TCustomVirtualDrawTree.GetDefaultHintKind: TVTHintKind;
  31946. begin
  31947. Result := vhkOwnerDraw;
  31948. end;
  31949. //----------------- TVirtualDrawTree -----------------------------------------------------------------------------------
  31950. function TVirtualDrawTree.GetOptions: TVirtualTreeOptions;
  31951. begin
  31952. Result := FOptions as TVirtualTreeOptions;
  31953. end;
  31954. //----------------------------------------------------------------------------------------------------------------------
  31955. procedure TVirtualDrawTree.SetOptions(const Value: TVirtualTreeOptions);
  31956. begin
  31957. FOptions.Assign(Value);
  31958. end;
  31959. //----------------------------------------------------------------------------------------------------------------------
  31960. function TVirtualDrawTree.GetOptionsClass: TTreeOptionsClass;
  31961. begin
  31962. Result := TVirtualTreeOptions;
  31963. end;
  31964. //----------------------------------------------------------------------------------------------------------------------
  31965. {$if CompilerVersion >= 23}
  31966. class constructor TVirtualDrawTree.Create();
  31967. begin
  31968. TCustomStyleEngine.RegisterStyleHook(TVirtualDrawTree, TVclStyleScrollBarsHook);
  31969. end;
  31970. {$ifend}
  31971. //----------------------------------------------------------------------------------------------------------------------
  31972. // XE2+ VCL Style
  31973. {$if CompilerVersion >= 23 }
  31974. { TVclStyleScrollBarsHook }
  31975. procedure TVclStyleScrollBarsHook.CalcScrollBarsRect;
  31976. var
  31977. P: TPoint;
  31978. BorderValue: TSize;
  31979. BarInfo: TScrollBarInfo;
  31980. I: Integer;
  31981. procedure CalcVerticalRects;
  31982. begin
  31983. BarInfo.cbSize := SizeOf(BarInfo);
  31984. GetScrollBarInfo(Handle, Integer(OBJID_VSCROLL), BarInfo);
  31985. FVertScrollBarWindow.Visible := not(STATE_SYSTEM_INVISIBLE and BarInfo.rgstate[0] <> 0);
  31986. FVertScrollBarWindow.Enabled := not(STATE_SYSTEM_UNAVAILABLE and BarInfo.rgstate[0] <> 0);
  31987. if FVertScrollBarWindow.Visible then
  31988. begin
  31989. // ScrollBar Rect
  31990. P := BarInfo.rcScrollBar.TopLeft;
  31991. ScreenToClient(Handle, P);
  31992. FVertScrollBarRect.TopLeft := P;
  31993. P := BarInfo.rcScrollBar.BottomRight;
  31994. ScreenToClient(Handle, P);
  31995. FVertScrollBarRect.BottomRight := P;
  31996. OffsetRect(FVertScrollBarRect, BorderValue.cx, BorderValue.cy);
  31997. I := GetSystemMetrics(SM_CYVTHUMB);
  31998. // Down Button
  31999. FVertScrollBarDownButtonRect := FVertScrollBarRect;
  32000. FVertScrollBarDownButtonRect.Top := FVertScrollBarDownButtonRect.Bottom - I;
  32001. // UP Button
  32002. FVertScrollBarUpButtonRect := FVertScrollBarRect;
  32003. FVertScrollBarUpButtonRect.Bottom := FVertScrollBarUpButtonRect.Top + I;
  32004. FVertScrollBarSliderTrackRect := FVertScrollBarRect;
  32005. Inc(FVertScrollBarSliderTrackRect.Top, I);
  32006. Dec(FVertScrollBarSliderTrackRect.Bottom, I);
  32007. end;
  32008. end;
  32009. procedure CalcHorizontalRects;
  32010. begin
  32011. BarInfo.cbSize := SizeOf(BarInfo);
  32012. GetScrollBarInfo(Handle, Integer(OBJID_HSCROLL), BarInfo);
  32013. FHorzScrollBarWindow.Visible := not(STATE_SYSTEM_INVISIBLE and BarInfo.rgstate[0] <> 0);
  32014. FHorzScrollBarWindow.Enabled := not(STATE_SYSTEM_UNAVAILABLE and BarInfo.rgstate[0] <> 0);
  32015. if FHorzScrollBarWindow.Visible then
  32016. begin
  32017. // ScrollBar Rect
  32018. P := BarInfo.rcScrollBar.TopLeft;
  32019. ScreenToClient(Handle, P);
  32020. FHorzScrollBarRect.TopLeft := P;
  32021. P := BarInfo.rcScrollBar.BottomRight;
  32022. ScreenToClient(Handle, P);
  32023. FHorzScrollBarRect.BottomRight := P;
  32024. OffsetRect(FHorzScrollBarRect, BorderValue.cx, BorderValue.cy);
  32025. I := GetSystemMetrics(SM_CXHTHUMB);
  32026. // Down Button
  32027. FHorzScrollBarDownButtonRect := FHorzScrollBarRect;
  32028. FHorzScrollBarDownButtonRect.Left := FHorzScrollBarDownButtonRect.Right - I;
  32029. // UP Button
  32030. FHorzScrollBarUpButtonRect := FHorzScrollBarRect;
  32031. FHorzScrollBarUpButtonRect.Right := FHorzScrollBarUpButtonRect.Left + I;
  32032. FHorzScrollBarSliderTrackRect := FHorzScrollBarRect;
  32033. Inc(FHorzScrollBarSliderTrackRect.Left, I);
  32034. Dec(FHorzScrollBarSliderTrackRect.Right, I);
  32035. end;
  32036. end;
  32037. begin
  32038. BorderValue.cx := 0;
  32039. BorderValue.cy := 0;
  32040. if HasBorder then
  32041. if HasClientEdge then
  32042. begin
  32043. BorderValue.cx := GetSystemMetrics(SM_CXEDGE);
  32044. BorderValue.cy := GetSystemMetrics(SM_CYEDGE);
  32045. end;
  32046. CalcVerticalRects;
  32047. CalcHorizontalRects;
  32048. end;
  32049. constructor TVclStyleScrollBarsHook.Create(AControl: TWinControl);
  32050. begin
  32051. inherited;
  32052. FVertScrollBarWindow := TVclStyleScrollBarWindow.CreateParented(GetParent(Control.Handle));
  32053. FVertScrollBarWindow.ScrollBarWindowOwner := Self;
  32054. FVertScrollBarWindow.ScrollBarVertical := True;
  32055. FHorzScrollBarWindow := TVclStyleScrollBarWindow.CreateParented(GetParent(Control.Handle));
  32056. FHorzScrollBarWindow.ScrollBarWindowOwner := Self;
  32057. FVertScrollBarSliderState := tsThumbBtnVertNormal;
  32058. FVertScrollBarUpButtonState := tsArrowBtnUpNormal;
  32059. FVertScrollBarDownButtonState := tsArrowBtnDownNormal;
  32060. FHorzScrollBarSliderState := tsThumbBtnHorzNormal;
  32061. FHorzScrollBarUpButtonState := tsArrowBtnLeftNormal;
  32062. FHorzScrollBarDownButtonState := tsArrowBtnRightNormal;
  32063. end;
  32064. destructor TVclStyleScrollBarsHook.Destroy;
  32065. begin
  32066. FVertScrollBarWindow.ScrollBarWindowOwner := nil;
  32067. FreeAndNil(FVertScrollBarWindow);
  32068. FHorzScrollBarWindow.ScrollBarWindowOwner := nil;
  32069. FreeAndNil(FHorzScrollBarWindow);
  32070. inherited;
  32071. end;
  32072. procedure TVclStyleScrollBarsHook.DrawHorzScrollBar(DC: HDC);
  32073. var
  32074. B: TBitmap;
  32075. Details: TThemedElementDetails;
  32076. R: TRect;
  32077. begin
  32078. if ((Handle = 0) or (DC = 0)) then
  32079. Exit;
  32080. if FHorzScrollBarWindow.Visible and StyleServices.Available then
  32081. begin
  32082. B := TBitmap.Create;
  32083. try
  32084. B.Width := FHorzScrollBarRect.Width;
  32085. B.Height := FHorzScrollBarRect.Height;
  32086. MoveWindowOrg(B.Canvas.Handle, -FHorzScrollBarRect.Left, -FHorzScrollBarRect.Top);
  32087. R := FHorzScrollBarRect;
  32088. R.Left := FHorzScrollBarUpButtonRect.Right;
  32089. R.Right := FHorzScrollBarDownButtonRect.Left;
  32090. Details := StyleServices.GetElementDetails(tsUpperTrackHorzNormal);
  32091. StyleServices.DrawElement(B.Canvas.Handle, Details, R);
  32092. if FHorzScrollBarWindow.Enabled then
  32093. Details := StyleServices.GetElementDetails(FHorzScrollBarSliderState);
  32094. StyleServices.DrawElement(B.Canvas.Handle, Details, GetHorzScrollBarSliderRect);
  32095. if FHorzScrollBarWindow.Enabled then
  32096. Details := StyleServices.GetElementDetails(FHorzScrollBarUpButtonState)
  32097. else
  32098. Details := StyleServices.GetElementDetails(tsArrowBtnLeftDisabled);
  32099. StyleServices.DrawElement(B.Canvas.Handle, Details, FHorzScrollBarUpButtonRect);
  32100. if FHorzScrollBarWindow.Enabled then
  32101. Details := StyleServices.GetElementDetails(FHorzScrollBarDownButtonState)
  32102. else
  32103. Details := StyleServices.GetElementDetails(tsArrowBtnRightDisabled);
  32104. StyleServices.DrawElement(B.Canvas.Handle, Details, FHorzScrollBarDownButtonRect);
  32105. MoveWindowOrg(B.Canvas.Handle, FHorzScrollBarRect.Left, FHorzScrollBarRect.Top);
  32106. with FHorzScrollBarRect do
  32107. BitBlt(DC, Left, Top, B.Width, B.Height, B.Canvas.Handle, 0, 0, SRCCOPY);
  32108. finally
  32109. B.Free;
  32110. end;
  32111. end;
  32112. end;
  32113. procedure TVclStyleScrollBarsHook.DrawVertScrollBar(DC: HDC);
  32114. var
  32115. B: TBitmap;
  32116. Details: TThemedElementDetails;
  32117. R: TRect;
  32118. begin
  32119. if ((Handle = 0) or (DC = 0)) then
  32120. Exit;
  32121. if FVertScrollBarWindow.Visible and StyleServices.Available then
  32122. begin
  32123. B := TBitmap.Create;
  32124. try
  32125. B.Width := FVertScrollBarRect.Width;
  32126. B.Height := FVertScrollBarWindow.Height;
  32127. MoveWindowOrg(B.Canvas.Handle, -FVertScrollBarRect.Left, -FVertScrollBarRect.Top);
  32128. R := FVertScrollBarRect;
  32129. R.Bottom := B.Height + FVertScrollBarRect.Top;
  32130. Details := StyleServices.GetElementDetails(tsUpperTrackVertNormal);
  32131. StyleServices.DrawElement(B.Canvas.Handle, Details, R);
  32132. R.Top := FVertScrollBarUpButtonRect.Bottom;
  32133. R.Bottom := FVertScrollBarDownButtonRect.Top;
  32134. Details := StyleServices.GetElementDetails(tsUpperTrackVertNormal);
  32135. StyleServices.DrawElement(B.Canvas.Handle, Details, R);
  32136. if FVertScrollBarWindow.Enabled then
  32137. Details := StyleServices.GetElementDetails(FVertScrollBarSliderState);
  32138. StyleServices.DrawElement(B.Canvas.Handle, Details, GetVertScrollBarSliderRect);
  32139. if FVertScrollBarWindow.Enabled then
  32140. Details := StyleServices.GetElementDetails(FVertScrollBarUpButtonState)
  32141. else
  32142. Details := StyleServices.GetElementDetails(tsArrowBtnUpDisabled);
  32143. StyleServices.DrawElement(B.Canvas.Handle, Details, FVertScrollBarUpButtonRect);
  32144. if FVertScrollBarWindow.Enabled then
  32145. Details := StyleServices.GetElementDetails(FVertScrollBarDownButtonState)
  32146. else
  32147. Details := StyleServices.GetElementDetails(tsArrowBtnDownDisabled);
  32148. StyleServices.DrawElement(B.Canvas.Handle, Details, FVertScrollBarDownButtonRect);
  32149. MoveWindowOrg(B.Canvas.Handle, FVertScrollBarRect.Left, FVertScrollBarRect.Top);
  32150. with FVertScrollBarRect do
  32151. BitBlt(DC, Left, Top, B.Width, B.Height, B.Canvas.Handle, 0, 0, SRCCOPY);
  32152. finally
  32153. B.Free;
  32154. end;
  32155. end;
  32156. end;
  32157. function TVclStyleScrollBarsHook.GetHorzScrollBarSliderRect: TRect;
  32158. var
  32159. P: TPoint;
  32160. BarInfo: TScrollBarInfo;
  32161. begin
  32162. if FHorzScrollBarWindow.Visible and FHorzScrollBarWindow.Enabled then
  32163. begin
  32164. BarInfo.cbSize := SizeOf(BarInfo);
  32165. GetScrollBarInfo(Handle, Integer(OBJID_HSCROLL), BarInfo);
  32166. P := BarInfo.rcScrollBar.TopLeft;
  32167. ScreenToClient(Handle, P);
  32168. Result.TopLeft := P;
  32169. P := BarInfo.rcScrollBar.BottomRight;
  32170. ScreenToClient(Handle, P);
  32171. Result.BottomRight := P;
  32172. Result.Left := BarInfo.xyThumbTop;
  32173. Result.Right := BarInfo.xyThumbBottom;
  32174. if HasBorder then
  32175. if HasClientEdge then
  32176. OffsetRect(Result, 2, 2)
  32177. else
  32178. OffsetRect(Result, 1, 1);
  32179. end
  32180. else
  32181. Result := Rect(0, 0, 0, 0);
  32182. end;
  32183. function TVclStyleScrollBarsHook.GetVertScrollBarSliderRect: TRect;
  32184. var
  32185. P: TPoint;
  32186. BarInfo: TScrollBarInfo;
  32187. begin
  32188. if FVertScrollBarWindow.Visible and FVertScrollBarWindow.Enabled then
  32189. begin
  32190. BarInfo.cbSize := SizeOf(BarInfo);
  32191. GetScrollBarInfo(Handle, Integer(OBJID_VSCROLL), BarInfo);
  32192. P := BarInfo.rcScrollBar.TopLeft;
  32193. ScreenToClient(Handle, P);
  32194. Result.TopLeft := P;
  32195. P := BarInfo.rcScrollBar.BottomRight;
  32196. ScreenToClient(Handle, P);
  32197. Result.BottomRight := P;
  32198. Result.Top := BarInfo.xyThumbTop;
  32199. Result.Bottom := BarInfo.xyThumbBottom;
  32200. if HasBorder then
  32201. if HasClientEdge then
  32202. OffsetRect(Result, 2, 2)
  32203. else
  32204. OffsetRect(Result, 1, 1);
  32205. end
  32206. else
  32207. Result := Rect(0, 0, 0, 0);
  32208. end;
  32209. procedure TVclStyleScrollBarsHook.MouseLeave;
  32210. begin
  32211. inherited;
  32212. if FVertScrollBarSliderState = tsThumbBtnVertHot then
  32213. FVertScrollBarSliderState := tsThumbBtnVertNormal;
  32214. if FHorzScrollBarSliderState = tsThumbBtnHorzHot then
  32215. FHorzScrollBarSliderState := tsThumbBtnHorzNormal;
  32216. if FVertScrollBarUpButtonState = tsArrowBtnUpHot then
  32217. FVertScrollBarUpButtonState := tsArrowBtnUpNormal;
  32218. if FVertScrollBarDownButtonState = tsArrowBtnDownHot then
  32219. FVertScrollBarDownButtonState := tsArrowBtnDownNormal;
  32220. if FHorzScrollBarUpButtonState = tsArrowBtnLeftHot then
  32221. FHorzScrollBarUpButtonState := tsArrowBtnLeftNormal;
  32222. if FHorzScrollBarDownButtonState = tsArrowBtnRightHot then
  32223. FHorzScrollBarDownButtonState := tsArrowBtnRightNormal;
  32224. PaintScrollBars;
  32225. end;
  32226. procedure TVclStyleScrollBarsHook.PaintScrollBars;
  32227. begin
  32228. FVertScrollBarWindow.Repaint;
  32229. FHorzScrollBarWindow.Repaint;
  32230. end;
  32231. function TVclStyleScrollBarsHook.PointInTreeHeader(const P: TPoint): Boolean;
  32232. begin
  32233. Result := TBaseVirtualTree(Control).FHeader.InHeader(P);
  32234. end;
  32235. procedure TVclStyleScrollBarsHook.UpdateScrollBarWindow;
  32236. var
  32237. R: TRect;
  32238. Owner: TBaseVirtualTree;
  32239. HeaderHeight: Integer;
  32240. BorderWidth: Integer;
  32241. begin
  32242. Owner := TBaseVirtualTree(Control);
  32243. if (hoVisible in Owner.Header.Options) then
  32244. HeaderHeight := Owner.FHeader.Height
  32245. else
  32246. HeaderHeight := 0;
  32247. BorderWidth := 0;
  32248. // VertScrollBarWindow
  32249. if FVertScrollBarWindow.Visible then
  32250. begin
  32251. R := FVertScrollBarRect;
  32252. if Control.BidiMode = bdRightToLeft then
  32253. begin
  32254. OffsetRect(R, -R.Left, 0);
  32255. if HasBorder then
  32256. OffsetRect(R, GetSystemMetrics(SM_CXEDGE), 0);
  32257. end;
  32258. if HasBorder then
  32259. BorderWidth := GetSystemMetrics(SM_CYEDGE) * 2;
  32260. ShowWindow(FVertScrollBarWindow.Handle, SW_SHOW);
  32261. SetWindowPos(FVertScrollBarWindow.Handle, HWND_TOP, Control.Left + R.Left, Control.Top + R.Top + HeaderHeight, R.Right - R.Left,
  32262. Control.Height - HeaderHeight - BorderWidth, SWP_SHOWWINDOW);
  32263. end
  32264. else
  32265. ShowWindow(FVertScrollBarWindow.Handle, SW_HIDE);
  32266. // HorzScrollBarWindow
  32267. if FHorzScrollBarWindow.Visible then
  32268. begin
  32269. R := FHorzScrollBarRect;
  32270. if Control.BidiMode = bdRightToLeft then
  32271. OffsetRect(R, FVertScrollBarRect.Width, 0);
  32272. ShowWindow(FHorzScrollBarWindow.Handle, SW_SHOW);
  32273. SetWindowPos(FHorzScrollBarWindow.Handle, HWND_TOP, Control.Left + R.Left, Control.Top + R.Top + HeaderHeight, R.Right - R.Left,
  32274. R.Bottom - R.Top, SWP_SHOWWINDOW);
  32275. end
  32276. else
  32277. ShowWindow(FHorzScrollBarWindow.Handle, SW_HIDE);
  32278. end;
  32279. procedure TVclStyleScrollBarsHook.WMCaptureChanged(var Msg: TMessage);
  32280. begin
  32281. if FVertScrollBarWindow.Visible and FVertScrollBarWindow.Enabled then
  32282. begin
  32283. if FVertScrollBarUpButtonState = tsArrowBtnUpPressed then
  32284. begin
  32285. FVertScrollBarUpButtonState := tsArrowBtnUpNormal;
  32286. PaintScrollBars;
  32287. end;
  32288. if FVertScrollBarDownButtonState = tsArrowBtnDownPressed then
  32289. begin
  32290. FVertScrollBarDownButtonState := tsArrowBtnDownNormal;
  32291. PaintScrollBars;
  32292. end;
  32293. end;
  32294. if FHorzScrollBarWindow.Visible and FHorzScrollBarWindow.Enabled then
  32295. begin
  32296. if FHorzScrollBarUpButtonState = tsArrowBtnLeftPressed then
  32297. begin
  32298. FHorzScrollBarUpButtonState := tsArrowBtnLeftNormal;
  32299. PaintScrollBars;
  32300. end;
  32301. if FHorzScrollBarDownButtonState = tsArrowBtnRightPressed then
  32302. begin
  32303. FHorzScrollBarDownButtonState := tsArrowBtnRightNormal;
  32304. PaintScrollBars;
  32305. end;
  32306. end;
  32307. CallDefaultProc(TMessage(Msg));
  32308. Handled := True;
  32309. end;
  32310. procedure TVclStyleScrollBarsHook.WMHScroll(var Msg: TMessage);
  32311. begin
  32312. CallDefaultProc(TMessage(Msg));
  32313. PaintScrollBars;
  32314. Handled := True;
  32315. end;
  32316. procedure TVclStyleScrollBarsHook.CMUpdateVclStyleScrollBars(var Message: TMessage);
  32317. begin
  32318. CalcScrollBarsRect;
  32319. PaintScrollBars;
  32320. end;
  32321. procedure TVclStyleScrollBarsHook.WMKeyDown(var Msg: TMessage);
  32322. begin
  32323. CallDefaultProc(TMessage(Msg));
  32324. PaintScrollBars;
  32325. Handled := True;
  32326. end;
  32327. procedure TVclStyleScrollBarsHook.WMKeyUp(var Msg: TMessage);
  32328. begin
  32329. CallDefaultProc(TMessage(Msg));
  32330. PaintScrollBars;
  32331. Handled := True;
  32332. end;
  32333. procedure TVclStyleScrollBarsHook.WMLButtonDown(var Msg: TWMMouse);
  32334. begin
  32335. CallDefaultProc(TMessage(Msg));
  32336. PaintScrollBars;
  32337. Handled := True;
  32338. end;
  32339. procedure TVclStyleScrollBarsHook.WMLButtonUp(var Msg: TWMMouse);
  32340. var
  32341. P: TPoint;
  32342. begin
  32343. P := Point(Msg.XPos, Msg.YPos);
  32344. ScreenToClient(Handle, P);
  32345. if not PointInTreeHeader(P) then
  32346. begin
  32347. if FVertScrollBarWindow.Visible then
  32348. begin
  32349. if FVertScrollBarSliderState = tsThumbBtnVertPressed then
  32350. begin
  32351. PostMessage(Handle, WM_VSCROLL, Integer(SmallPoint(SB_ENDSCROLL, 0)), 0);
  32352. FLeftMouseButtonDown := False;
  32353. FVertScrollBarSliderState := tsThumbBtnVertNormal;
  32354. PaintScrollBars;
  32355. Handled := True;
  32356. ReleaseCapture;
  32357. Exit;
  32358. end;
  32359. if FVertScrollBarUpButtonState = tsArrowBtnUpPressed then
  32360. FVertScrollBarUpButtonState := tsArrowBtnUpNormal;
  32361. if FVertScrollBarDownButtonState = tsArrowBtnDownPressed then
  32362. FVertScrollBarDownButtonState := tsArrowBtnDownNormal;
  32363. end;
  32364. if FHorzScrollBarWindow.Visible then
  32365. begin
  32366. if FHorzScrollBarSliderState = tsThumbBtnHorzPressed then
  32367. begin
  32368. PostMessage(Handle, WM_HSCROLL, Integer(SmallPoint(SB_ENDSCROLL, 0)), 0);
  32369. FLeftMouseButtonDown := False;
  32370. FHorzScrollBarSliderState := tsThumbBtnHorzNormal;
  32371. PaintScrollBars;
  32372. Handled := True;
  32373. ReleaseCapture;
  32374. Exit;
  32375. end;
  32376. if FHorzScrollBarUpButtonState = tsArrowBtnLeftPressed then
  32377. FHorzScrollBarUpButtonState := tsArrowBtnLeftNormal;
  32378. if FHorzScrollBarDownButtonState = tsArrowBtnRightPressed then
  32379. FHorzScrollBarDownButtonState := tsArrowBtnRightNormal;
  32380. end;
  32381. PaintScrollBars;
  32382. end;
  32383. FLeftMouseButtonDown := False;
  32384. end;
  32385. procedure TVclStyleScrollBarsHook.WMMouseMove(var Msg: TWMMouse);
  32386. var
  32387. SF: TScrollInfo;
  32388. begin
  32389. inherited;
  32390. if FVertScrollBarSliderState = tsThumbBtnVertPressed then
  32391. begin
  32392. SF.fMask := SIF_ALL;
  32393. SF.cbSize := SizeOf(SF);
  32394. GetScrollInfo(Handle, SB_VERT, SF);
  32395. if SF.nPos <> Round(FScrollPos) then
  32396. FScrollPos := SF.nPos;
  32397. FScrollPos := FScrollPos + (SF.nMax - SF.nMin) * ((Mouse.CursorPos.Y - FPrevScrollPos) / FVertScrollBarSliderTrackRect.Height);
  32398. if FScrollPos < SF.nMin then
  32399. FScrollPos := SF.nMin;
  32400. if FScrollPos > SF.nMax then
  32401. FScrollPos := SF.nMax;
  32402. if SF.nPage <> 0 then
  32403. if Round(FScrollPos) > SF.nMax - Integer(SF.nPage) + 1 then
  32404. FScrollPos := SF.nMax - Integer(SF.nPage) + 1;
  32405. FPrevScrollPos := Mouse.CursorPos.Y;
  32406. SF.nPos := Round(FScrollPos);
  32407. SetScrollInfo(Handle, SB_VERT, SF, False);
  32408. PostMessage(Handle, WM_VSCROLL, Integer(SmallPoint(SB_THUMBPOSITION, Min(Round(FScrollPos), High(SmallInt)))), 0); // Min() prevents range check error
  32409. PaintScrollBars;
  32410. Handled := True;
  32411. Exit;
  32412. end;
  32413. if FHorzScrollBarSliderState = tsThumbBtnHorzPressed then
  32414. begin
  32415. SF.fMask := SIF_ALL;
  32416. SF.cbSize := SizeOf(SF);
  32417. GetScrollInfo(Handle, SB_HORZ, SF);
  32418. if SF.nPos <> Round(FScrollPos) then
  32419. FScrollPos := SF.nPos;
  32420. FScrollPos := FScrollPos + (SF.nMax - SF.nMin) * ((Mouse.CursorPos.X - FPrevScrollPos) / FHorzScrollBarSliderTrackRect.Width);
  32421. if FScrollPos < SF.nMin then
  32422. FScrollPos := SF.nMin;
  32423. if FScrollPos > SF.nMax then
  32424. FScrollPos := SF.nMax;
  32425. if SF.nPage <> 0 then
  32426. if Round(FScrollPos) > SF.nMax - Integer(SF.nPage) + 1 then
  32427. FScrollPos := SF.nMax - Integer(SF.nPage) + 1;
  32428. FPrevScrollPos := Mouse.CursorPos.X;
  32429. SF.nPos := Round(FScrollPos);
  32430. SetScrollInfo(Handle, SB_HORZ, SF, False);
  32431. PostMessage(Handle, WM_HSCROLL, Integer(SmallPoint(SB_THUMBPOSITION, Round(FScrollPos))), 0);
  32432. PaintScrollBars;
  32433. Handled := True;
  32434. Exit;
  32435. end;
  32436. if FHorzScrollBarSliderState = tsThumbBtnHorzHot then
  32437. begin
  32438. FHorzScrollBarSliderState := tsThumbBtnHorzNormal;
  32439. PaintScrollBars;
  32440. end
  32441. else
  32442. if FVertScrollBarSliderState = tsThumbBtnVertHot then
  32443. begin
  32444. FVertScrollBarSliderState := tsThumbBtnVertNormal;
  32445. PaintScrollBars;
  32446. end
  32447. else
  32448. if FHorzScrollBarUpButtonState = tsArrowBtnLeftHot then
  32449. begin
  32450. FHorzScrollBarUpButtonState := tsArrowBtnLeftNormal;
  32451. PaintScrollBars;
  32452. end
  32453. else
  32454. if FHorzScrollBarDownButtonState = tsArrowBtnRightHot then
  32455. begin
  32456. FHorzScrollBarDownButtonState := tsArrowBtnRightNormal;
  32457. PaintScrollBars;
  32458. end
  32459. else
  32460. if FVertScrollBarUpButtonState = tsArrowBtnUpHot then
  32461. begin
  32462. FVertScrollBarUpButtonState := tsArrowBtnUpNormal;
  32463. PaintScrollBars;
  32464. end
  32465. else
  32466. if FVertScrollBarDownButtonState = tsArrowBtnDownHot then
  32467. begin
  32468. FVertScrollBarDownButtonState := tsArrowBtnDownNormal;
  32469. PaintScrollBars;
  32470. end;
  32471. CallDefaultProc(TMessage(Msg));
  32472. if FLeftMouseButtonDown then
  32473. PaintScrollBars;
  32474. Handled := True;
  32475. end;
  32476. procedure TVclStyleScrollBarsHook.WMMouseWheel(var Msg: TMessage);
  32477. begin
  32478. CallDefaultProc(TMessage(Msg));
  32479. PaintScrollBars;
  32480. Handled := True;
  32481. end;
  32482. procedure TVclStyleScrollBarsHook.WMNCLButtonDblClk(var Msg: TWMMouse);
  32483. begin
  32484. WMNCLButtonDown(Msg);
  32485. end;
  32486. procedure TVclStyleScrollBarsHook.WMNCLButtonDown(var Msg: TWMMouse);
  32487. var
  32488. P: TPoint;
  32489. SF: TScrollInfo;
  32490. begin
  32491. P := Point(Msg.XPos, Msg.YPos);
  32492. ScreenToClient(Handle, P);
  32493. if HasBorder then
  32494. if HasClientEdge then
  32495. begin
  32496. P.X := P.X + 2;
  32497. P.Y := P.Y + 2;
  32498. end
  32499. else
  32500. begin
  32501. P.X := P.X + 1;
  32502. P.Y := P.Y + 1;
  32503. end;
  32504. if not PointInTreeHeader(P) then
  32505. begin
  32506. if FVertScrollBarWindow.Visible then
  32507. begin
  32508. if PtInRect(GetVertScrollBarSliderRect, P) then
  32509. begin
  32510. FLeftMouseButtonDown := True;
  32511. SF.fMask := SIF_ALL;
  32512. SF.cbSize := SizeOf(SF);
  32513. GetScrollInfo(Handle, SB_VERT, SF);
  32514. // FListPos := SF.nPos;
  32515. FScrollPos := SF.nPos;
  32516. FPrevScrollPos := Mouse.CursorPos.Y;
  32517. FVertScrollBarSliderState := tsThumbBtnVertPressed;
  32518. PaintScrollBars;
  32519. SetCapture(Handle);
  32520. Handled := True;
  32521. Exit;
  32522. end;
  32523. if FVertScrollBarWindow.Enabled then
  32524. begin
  32525. if PtInRect(FVertScrollBarDownButtonRect, P) then
  32526. FVertScrollBarDownButtonState := tsArrowBtnDownPressed;
  32527. if PtInRect(FVertScrollBarUpButtonRect, P) then
  32528. FVertScrollBarUpButtonState := tsArrowBtnUpPressed;
  32529. end;
  32530. end;
  32531. if FHorzScrollBarWindow.Visible then
  32532. begin
  32533. if PtInRect(GetHorzScrollBarSliderRect, P) then
  32534. begin
  32535. FLeftMouseButtonDown := True;
  32536. SF.fMask := SIF_ALL;
  32537. SF.cbSize := SizeOf(SF);
  32538. GetScrollInfo(Handle, SB_HORZ, SF);
  32539. // FListPos := SF.nPos;
  32540. FScrollPos := SF.nPos;
  32541. FPrevScrollPos := Mouse.CursorPos.X;
  32542. FHorzScrollBarSliderState := tsThumbBtnHorzPressed;
  32543. PaintScrollBars;
  32544. SetCapture(Handle);
  32545. Handled := True;
  32546. Exit;
  32547. end;
  32548. if FHorzScrollBarWindow.Enabled then
  32549. begin
  32550. if PtInRect(FHorzScrollBarDownButtonRect, P) then
  32551. FHorzScrollBarDownButtonState := tsArrowBtnRightPressed;
  32552. if PtInRect(FHorzScrollBarUpButtonRect, P) then
  32553. FHorzScrollBarUpButtonState := tsArrowBtnLeftPressed;
  32554. end;
  32555. end;
  32556. FLeftMouseButtonDown := True;
  32557. PaintScrollBars;
  32558. end;
  32559. end;
  32560. procedure TVclStyleScrollBarsHook.WMNCLButtonUp(var Msg: TWMMouse);
  32561. var
  32562. P: TPoint;
  32563. B: Boolean;
  32564. begin
  32565. P := Point(Msg.XPos, Msg.YPos);
  32566. ScreenToClient(Handle, P);
  32567. if HasBorder then
  32568. if HasClientEdge then
  32569. begin
  32570. P.X := P.X + 2;
  32571. P.Y := P.Y + 2;
  32572. end
  32573. else
  32574. begin
  32575. P.X := P.X + 1;
  32576. P.Y := P.Y + 1;
  32577. end;
  32578. B := PointInTreeHeader(P);
  32579. if not B then
  32580. begin
  32581. if FVertScrollBarWindow.Visible then
  32582. if FVertScrollBarWindow.Enabled then
  32583. begin
  32584. if FVertScrollBarSliderState = tsThumbBtnVertPressed then
  32585. begin
  32586. FLeftMouseButtonDown := False;
  32587. FVertScrollBarSliderState := tsThumbBtnVertNormal;
  32588. PaintScrollBars;
  32589. Handled := True;
  32590. Exit;
  32591. end;
  32592. if PtInRect(FVertScrollBarDownButtonRect, P) then
  32593. FVertScrollBarDownButtonState := tsArrowBtnDownHot
  32594. else
  32595. FVertScrollBarDownButtonState := tsArrowBtnDownNormal;
  32596. if PtInRect(FVertScrollBarUpButtonRect, P) then
  32597. FVertScrollBarUpButtonState := tsArrowBtnUpHot
  32598. else
  32599. FVertScrollBarUpButtonState := tsArrowBtnUpNormal;
  32600. end;
  32601. if FHorzScrollBarWindow.Visible then
  32602. if FHorzScrollBarWindow.Enabled then
  32603. begin
  32604. if FHorzScrollBarSliderState = tsThumbBtnHorzPressed then
  32605. begin
  32606. FLeftMouseButtonDown := False;
  32607. FHorzScrollBarSliderState := tsThumbBtnHorzNormal;
  32608. PaintScrollBars;
  32609. Handled := True;
  32610. Exit;
  32611. end;
  32612. if PtInRect(FHorzScrollBarDownButtonRect, P) then
  32613. FHorzScrollBarDownButtonState := tsArrowBtnRightHot
  32614. else
  32615. FHorzScrollBarDownButtonState := tsArrowBtnRightNormal;
  32616. if PtInRect(FHorzScrollBarUpButtonRect, P) then
  32617. FHorzScrollBarUpButtonState := tsArrowBtnLeftHot
  32618. else
  32619. FHorzScrollBarUpButtonState := tsArrowBtnLeftNormal;
  32620. end;
  32621. CallDefaultProc(TMessage(Msg));
  32622. end;
  32623. if not B and (FHorzScrollBarWindow.Visible) or (FVertScrollBarWindow.Visible) then
  32624. PaintScrollBars;
  32625. Handled := True;
  32626. end;
  32627. procedure TVclStyleScrollBarsHook.WMNCMouseMove(var Msg: TWMMouse);
  32628. var
  32629. P: TPoint;
  32630. MustUpdateScroll: Boolean;
  32631. B: Boolean;
  32632. begin
  32633. inherited;
  32634. P := Point(Msg.XPos, Msg.YPos);
  32635. ScreenToClient(Handle, P);
  32636. if PointInTreeHeader(P) then
  32637. begin
  32638. CallDefaultProc(TMessage(Msg));
  32639. PaintScrollBars;
  32640. Handled := True;
  32641. Exit;
  32642. end;
  32643. if HasBorder then
  32644. if HasClientEdge then
  32645. begin
  32646. P.X := P.X + 2;
  32647. P.Y := P.Y + 2;
  32648. end
  32649. else
  32650. begin
  32651. P.X := P.X + 1;
  32652. P.Y := P.Y + 1;
  32653. end;
  32654. MustUpdateScroll := False;
  32655. if FVertScrollBarWindow.Enabled then
  32656. begin
  32657. B := PtInRect(GetVertScrollBarSliderRect, P);
  32658. if B and (FVertScrollBarSliderState = tsThumbBtnVertNormal) then
  32659. begin
  32660. FVertScrollBarSliderState := tsThumbBtnVertHot;
  32661. MustUpdateScroll := True;
  32662. end
  32663. else if not B and (FVertScrollBarSliderState = tsThumbBtnVertHot) then
  32664. begin
  32665. FVertScrollBarSliderState := tsThumbBtnVertNormal;
  32666. MustUpdateScroll := True;
  32667. end;
  32668. B := PtInRect(FVertScrollBarDownButtonRect, P);
  32669. if B and (FVertScrollBarDownButtonState = tsArrowBtnDownNormal) then
  32670. begin
  32671. FVertScrollBarDownButtonState := tsArrowBtnDownHot;
  32672. MustUpdateScroll := True;
  32673. end
  32674. else if not B and (FVertScrollBarDownButtonState = tsArrowBtnDownHot) then
  32675. begin
  32676. FVertScrollBarDownButtonState := tsArrowBtnDownNormal;
  32677. MustUpdateScroll := True;
  32678. end;
  32679. B := PtInRect(FVertScrollBarUpButtonRect, P);
  32680. if B and (FVertScrollBarUpButtonState = tsArrowBtnUpNormal) then
  32681. begin
  32682. FVertScrollBarUpButtonState := tsArrowBtnUpHot;
  32683. MustUpdateScroll := True;
  32684. end
  32685. else if not B and (FVertScrollBarUpButtonState = tsArrowBtnUpHot) then
  32686. begin
  32687. FVertScrollBarUpButtonState := tsArrowBtnUpNormal;
  32688. MustUpdateScroll := True;
  32689. end;
  32690. end;
  32691. if FHorzScrollBarWindow.Enabled then
  32692. begin
  32693. B := PtInRect(GetHorzScrollBarSliderRect, P);
  32694. if B and (FHorzScrollBarSliderState = tsThumbBtnHorzNormal) then
  32695. begin
  32696. FHorzScrollBarSliderState := tsThumbBtnHorzHot;
  32697. MustUpdateScroll := True;
  32698. end
  32699. else if not B and (FHorzScrollBarSliderState = tsThumbBtnHorzHot) then
  32700. begin
  32701. FHorzScrollBarSliderState := tsThumbBtnHorzNormal;
  32702. MustUpdateScroll := True;
  32703. end;
  32704. B := PtInRect(FHorzScrollBarDownButtonRect, P);
  32705. if B and (FHorzScrollBarDownButtonState = tsArrowBtnRightNormal) then
  32706. begin
  32707. FHorzScrollBarDownButtonState := tsArrowBtnRightHot;
  32708. MustUpdateScroll := True;
  32709. end
  32710. else if not B and (FHorzScrollBarDownButtonState = tsArrowBtnRightHot) then
  32711. begin
  32712. FHorzScrollBarDownButtonState := tsArrowBtnRightNormal;
  32713. MustUpdateScroll := True;
  32714. end;
  32715. B := PtInRect(FHorzScrollBarUpButtonRect, P);
  32716. if B and (FHorzScrollBarUpButtonState = tsArrowBtnLeftNormal) then
  32717. begin
  32718. FHorzScrollBarUpButtonState := tsArrowBtnLeftHot;
  32719. MustUpdateScroll := True;
  32720. end
  32721. else if not B and (FHorzScrollBarUpButtonState = tsArrowBtnLeftHot) then
  32722. begin
  32723. FHorzScrollBarUpButtonState := tsArrowBtnLeftNormal;
  32724. MustUpdateScroll := True;
  32725. end;
  32726. end;
  32727. if MustUpdateScroll then
  32728. PaintScrollBars;
  32729. end;
  32730. procedure TVclStyleScrollBarsHook.WMNCPaint(var Msg: TMessage);
  32731. begin
  32732. CalcScrollBarsRect;
  32733. UpdateScrollBarWindow;
  32734. // PaintScrollBars;
  32735. // Handled := True;
  32736. end;
  32737. procedure TVclStyleScrollBarsHook.WMSize(var Msg: TMessage);
  32738. begin
  32739. CallDefaultProc(TMessage(Msg));
  32740. CalcScrollBarsRect;
  32741. UpdateScrollBarWindow;
  32742. PaintScrollBars;
  32743. Handled := True;
  32744. end;
  32745. procedure TVclStyleScrollBarsHook.WMMove(var Msg: TMessage);
  32746. begin
  32747. CallDefaultProc(TMessage(Msg));
  32748. if not (tsWindowCreating in TBaseVirtualTree(Control).FStates) then
  32749. begin
  32750. CalcScrollBarsRect;
  32751. UpdateScrollBarWindow;
  32752. PaintScrollBars;
  32753. end;
  32754. Handled := True;
  32755. end;
  32756. procedure TVclStyleScrollBarsHook.WMPosChanged(var Msg: TMessage);
  32757. begin
  32758. WMMove(Msg);
  32759. end;
  32760. procedure TVclStyleScrollBarsHook.WMVScroll(var Msg: TMessage);
  32761. begin
  32762. CallDefaultProc(TMessage(Msg));
  32763. PaintScrollBars;
  32764. Handled := True;
  32765. end;
  32766. { TVclStyleScrollBarsHook.TVclStyleScrollBarWindow }
  32767. constructor TVclStyleScrollBarsHook.TVclStyleScrollBarWindow.Create(AOwner: TComponent);
  32768. begin
  32769. inherited;
  32770. ControlStyle := ControlStyle + [csOverrideStylePaint];
  32771. FScrollBarWindowOwner := nil;
  32772. FScrollBarVertical := False;
  32773. FScrollBarVisible := False;
  32774. FScrollBarEnabled := False;
  32775. end;
  32776. procedure TVclStyleScrollBarsHook.TVclStyleScrollBarWindow.CreateParams(var Params: TCreateParams);
  32777. begin
  32778. inherited;
  32779. Params.Style := Params.Style or WS_CHILDWINDOW or WS_CLIPCHILDREN or WS_CLIPSIBLINGS;
  32780. Params.ExStyle := Params.ExStyle or WS_EX_NOPARENTNOTIFY;
  32781. end;
  32782. procedure TVclStyleScrollBarsHook.TVclStyleScrollBarWindow.WMEraseBkgnd(var Msg: TMessage);
  32783. begin
  32784. Msg.Result := 1;
  32785. end;
  32786. procedure TVclStyleScrollBarsHook.TVclStyleScrollBarWindow.WMNCHitTest(var Msg: TWMNCHitTest);
  32787. begin
  32788. Msg.Result := HTTRANSPARENT;
  32789. end;
  32790. procedure TVclStyleScrollBarsHook.TVclStyleScrollBarWindow.WMPaint(var Msg: TWMPaint);
  32791. var
  32792. PS: TPaintStruct;
  32793. DC: HDC;
  32794. begin
  32795. BeginPaint(Handle, PS);
  32796. try
  32797. if FScrollBarWindowOwner <> nil then
  32798. begin
  32799. DC := GetWindowDC(Handle);
  32800. try
  32801. if FScrollBarVertical then
  32802. begin
  32803. MoveWindowOrg(DC, -FScrollBarWindowOwner.FVertScrollBarRect.Left, -FScrollBarWindowOwner.FVertScrollBarRect.Top);
  32804. FScrollBarWindowOwner.DrawVertScrollBar(DC);
  32805. end
  32806. else
  32807. begin
  32808. MoveWindowOrg(DC, -FScrollBarWindowOwner.FHorzScrollBarRect.Left, -FScrollBarWindowOwner.FHorzScrollBarRect.Top);
  32809. FScrollBarWindowOwner.DrawHorzScrollBar(DC);
  32810. end;
  32811. finally
  32812. ReleaseDC(Handle, DC);
  32813. end;
  32814. end;
  32815. finally
  32816. EndPaint(Handle, PS);
  32817. end;
  32818. end;
  32819. {$ifend}
  32820. initialization
  32821. // Necessary for dynamic package loading.
  32822. Initialized := False;
  32823. NeedToUnitialize := False;
  32824. // This watcher is used whenever a global structure could be modified by more than one thread.
  32825. Watcher := TCriticalSection.Create;
  32826. finalization
  32827. if Initialized then
  32828. FinalizeGlobalStructures;
  32829. InternalClipboardFormats.Free;
  32830. InternalClipboardFormats := nil;
  32831. Watcher.Free;
  32832. Watcher := nil;
  32833. end.